diff --git a/krusader/FileSystem/fileitem.cpp b/krusader/FileSystem/fileitem.cpp index b253a870..c0e6483c 100644 --- a/krusader/FileSystem/fileitem.cpp +++ b/krusader/FileSystem/fileitem.cpp @@ -1,232 +1,240 @@ /*************************************************************************** fileitem.cpp ------------------- copyright : (C) 2000 by Rafi Yanai e-mail : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net --------------------------------------------------------------------------- *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD S o u r c e F i l e *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "fileitem.h" // QtCore #include #include #include #include #include #include "krpermhandler.h" #include "filesystemprovider.h" bool FileItem::userDefinedFolderIcons = true; // wrapper class; QCache needs objects class FileSize { public: const KIO::filesize_t m_size; FileSize(KIO::filesize_t size) : m_size(size) {} }; // cache for calculated directory sizes; static QCache s_fileSizeCache(1000); FileItem::FileItem(const QString &name, const QUrl &url, bool isDir, KIO::filesize_t size, mode_t mode, time_t mtime, time_t ctime, time_t atime, uid_t uid, gid_t gid, const QString &owner, const QString &group, bool isLink, const QString &linkDest, bool isBrokenLink, const QString &acl, const QString &defaultAcl) : m_name(name), m_url(url), m_isDir(isDir), m_size(size), m_mode(mode), m_mtime(mtime), m_ctime(ctime), m_atime(atime), m_uid(uid), m_gid(gid), m_owner(owner), m_group(group), m_isLink(isLink), m_linkDest(linkDest), m_isBrokenLink(isBrokenLink), m_acl(acl), m_defaulfAcl(defaultAcl), m_AclLoaded(false), m_mimeType(), m_icon() { m_permissions = KRpermHandler::mode2QString(mode); if (m_owner.isEmpty()) m_owner = KRpermHandler::uid2user(m_uid); if (m_group.isEmpty()) m_group = KRpermHandler::gid2group(m_gid); if (m_isDir && !m_isLink) { m_size = s_fileSizeCache.contains(m_url) ? s_fileSizeCache[m_url]->m_size : -1; } } FileItem *FileItem::createDummy() { FileItem *file = new FileItem("..", QUrl(), true, 0, 0, 0, 0, 0); file->setIcon("go-up"); return file; } +FileItem *FileItem::createBroken(const QString &name, const QUrl &url) +{ + FileItem *file = new FileItem(name, url, false, + 0, 0, 0, 0, 0); + file->setIcon("file-broken"); + return file; +} + FileItem *FileItem::createVirtualDir(const QString &name, const QUrl &url) { return new FileItem(name, url, true, 0, 0700, time(0), time(0), time(0), getuid(), getgid()); } FileItem *FileItem::createCopy(const FileItem &file, const QString &newName) { return new FileItem(newName, file.getUrl(), file.isDir(), file.getSize(), file.getMode(), file.getTime_t(), file.getChangedTime(), file.getAccessTime(), file.m_uid, file.m_gid, file.getOwner(), file.getGroup(), file.isSymLink(), file.getSymDest(), file.isBrokenLink()); } char FileItem::isReadable() const { if (m_uid != (uid_t)-1 && m_gid != (gid_t)-1) return KRpermHandler::readable(m_permissions, m_gid, m_uid); else return KRpermHandler::ftpReadable(m_owner, m_url.userName(), m_permissions); } char FileItem::isWriteable() const { if (m_uid != (uid_t)-1 && m_gid != (gid_t)-1) return KRpermHandler::writeable(m_permissions, m_gid, m_uid); else return KRpermHandler::ftpWriteable(m_owner, m_url.userName(), m_permissions); } char FileItem::isExecutable() const { if (m_uid != (uid_t)-1 && m_gid != (gid_t)-1) return KRpermHandler::executable(m_permissions, m_gid, m_uid); else return KRpermHandler::ftpExecutable(m_owner, m_url.userName(), m_permissions); } void FileItem::setSize(KIO::filesize_t size) { m_size = size; s_fileSizeCache.insert(m_url, new FileSize(size)); } const QString &FileItem::getMime() { if (m_mimeType.isEmpty()) { if (m_isDir) { m_mimeType = "inode/directory"; m_icon = "inode-directory"; } else if (isBrokenLink()) { m_mimeType = "unknown"; m_icon = "file-broken"; } else { const QMimeDatabase db; const QMimeType mt = db.mimeTypeForUrl(getUrl()); m_mimeType = mt.isValid() ? mt.name() : "unknown"; m_icon = mt.isValid() ? mt.iconName() : "file-broken"; if (m_mimeType == "inode/directory") { // TODO view update needed? and does this ever happen? m_isDir = true; } } if (m_isDir && userDefinedFolderIcons) { const QUrl url = getUrl(); if (url.isLocalFile()) { const QString file = url.toLocalFile() + "/.directory"; const KDesktopFile cfg(file); const QString icon = cfg.readIcon(); if (!icon.isEmpty()) m_icon = icon.startsWith(QLatin1String("./")) ? // relative path url.toLocalFile() + '/' + icon : icon; } } } return m_mimeType; } const QString &FileItem::getIcon() { if (m_icon.isEmpty()) { getMime(); // sets the icon } return m_icon; } const QString &FileItem::getACL() { if (!m_AclLoaded) loadACL(); return m_acl; } void FileItem::loadACL() { if (m_url.isLocalFile()) { FileSystemProvider::getACL(this, m_acl, m_defaulfAcl); } m_AclLoaded = true; } const KIO::UDSEntry FileItem::getEntry() { KIO::UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_NAME, getName()); entry.insert(KIO::UDSEntry::UDS_SIZE, getSize()); entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, getTime_t()); entry.insert(KIO::UDSEntry::UDS_CREATION_TIME, getChangedTime()); entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, getAccessTime()); entry.insert(KIO::UDSEntry::UDS_USER, getOwner()); entry.insert(KIO::UDSEntry::UDS_GROUP, getGroup()); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, getMime()); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, getMode() & S_IFMT); entry.insert(KIO::UDSEntry::UDS_ACCESS, getMode() & 07777); if (isSymLink()) entry.insert(KIO::UDSEntry::UDS_LINK_DEST, getSymDest()); if (!m_AclLoaded) loadACL(); if (!m_acl.isNull() || !m_defaulfAcl.isNull()) { entry.insert(KIO::UDSEntry::UDS_EXTENDED_ACL, 1); if (!m_acl.isNull()) entry.insert(KIO::UDSEntry::UDS_ACL_STRING, m_acl); if (!m_defaulfAcl.isNull()) entry.insert(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, m_defaulfAcl); } return entry; } diff --git a/krusader/FileSystem/fileitem.h b/krusader/FileSystem/fileitem.h index 7c4b7b91..8fabfbdb 100644 --- a/krusader/FileSystem/fileitem.h +++ b/krusader/FileSystem/fileitem.h @@ -1,169 +1,171 @@ /*************************************************************************** fileitem.h ------------------- begin : Thu May 4 2000 copyright : (C) 2000 by Shie Erlich & Rafi Yanai e-mail : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD H e a d e r F i l e *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef FILEITEM_H #define FILEITEM_H #include // QtCore #include #include #include #include /** * A file item gives access all meta information of a (virtual, dummy or real) file or directory in * the filesystem. * * NOTE: The name of a file item is supposed to be unique within a directory. */ class FileItem { public: /** * Create a new file item. * * Don't use this constructor outside of FileSystem! If you really need to, create a new static * factory method below. * * NOTE: According to Unix standard uid and gid CAN have signed or unsigned type. We use (e.g.) * "(uid_t) -1" as a special invalid user ID for non-local files. * NOTE: ACLs are currently only used by Synchronizer. * * @param name the display name of this file. Don't have to be the real filename. * @param url (real) absolute URL of this file * @param isDir true if this file is a directory. Else false. * @param size size of file * @param mode mode of file (file type and permissions) * @param mtime file modification time * @param ctime file changed time * @param atime file access time * @param uid Unix user id of file owner. Use -1 here and provide an owner name for non-local files. * @param gid Unix group id of file group. Use -1 here and provide a group name for non-local files. * @param owner user name of file owner. Can be empty for local files - * @param group group name of file group. Cam be empty for local files. + * @param group group name of file group. Can be empty for local files. * @param isLink true if file is a symbolic link. Else false. * @param linkDest link destination path if file is a link. Relative or absolute. Empty by default. * @param isBrokenLink true if file is a symbolic link and destination file does not exists. Else false. * @param acl ACL string of file. Can be empty and is loaded on demand. * @param defaultAcl default ACL string of file (only for directories). Can be empty and is loaded on demand. */ FileItem(const QString &name, const QUrl &url, bool isDir, KIO::filesize_t size, mode_t mode, time_t mtime, time_t ctime, time_t atime, uid_t uid = -1, gid_t gid = -1, const QString &owner = QString(), const QString &group = QString(), bool isLink = false, const QString &linkDest = QString(), bool isBrokenLink = false, const QString &acl = QString(), const QString &defaultAcl = QString()); /** Create a new ".." dummy file item. */ static FileItem *createDummy(); + /** Create a file item for a broken file which metadata could not be read. */ + static FileItem *createBroken(const QString &name, const QUrl &url); /** Create a new virtual directory. */ static FileItem *createVirtualDir(const QString &name, const QUrl &url); /** Create a new file item copy with a different name. */ static FileItem *createCopy(const FileItem &file, const QString &newName); // following functions give-out file details inline const QString &getName() const { return m_name; } /** Return the file size. Returns 0 for directories with unknown size. */ inline KIO::filesize_t getSize() const { return m_size == (KIO::filesize_t)-1 ? 0 : m_size; } /** Return the file size. Returns (KIO::filesize_t)-1 for directories with unknown size. */ inline KIO::filesize_t getUISize() const { return m_size; } inline const QString &getPerm() const { return m_permissions; } inline bool isDir() const { return m_isDir; } inline bool isSymLink() const { return m_isLink; } inline bool isBrokenLink() const { return m_isBrokenLink; } inline const QString &getSymDest() const { return m_linkDest; } inline mode_t getMode() const { return m_mode; } inline time_t getTime_t() const { return m_mtime; } inline time_t getChangedTime() const { return m_ctime; } inline time_t getAccessTime() const { return m_atime; } inline const QUrl &getUrl() const { return m_url; } inline const QString &getOwner() const { return m_owner; } inline const QString &getGroup() const { return m_group; } const QString &getMime(); const QString &getIcon(); const QString &getACL(); const QString &getDefaultACL(); const KIO::UDSEntry getEntry(); //< return the UDSEntry from the file item char isReadable() const; char isWriteable() const; char isExecutable() const; /** * Set the file size. * used ONLY when calculating a directory's space, needs to change the * displayed size of the viewitem and thus the file item. For INTERNAL USE ! */ void setSize(KIO::filesize_t size); inline static void loadUserDefinedFolderIcons(bool load) { userDefinedFolderIcons = load; } private: void setIcon(const QString &icon) { m_icon = icon; m_mimeType = "?"; } void loadACL(); QString m_name; //< file name QUrl m_url; //< file URL bool m_isDir; //< flag, true if it's a directory KIO::filesize_t m_size; //< file size mode_t m_mode; //< file mode (file type and permissions) time_t m_mtime; //< file modification time time_t m_ctime; //< file changed time time_t m_atime; //< file access time uid_t m_uid; //< file owner id gid_t m_gid; //< file group id QString m_owner; //< file owner name QString m_group; //< file group name bool m_isLink; //< true if the file is a symlink QString m_linkDest; //< if it's a sym link - its detination bool m_isBrokenLink; //< true if the link destianation does not exist QString m_permissions; //< file permissions string QString m_acl; //< ACL permission string, may lazy initialized QString m_defaulfAcl; //< ACL default string, may lazy initialized bool m_AclLoaded; //< flag, indicates that ACL permissions already loaded QString m_mimeType; //< file mimetype, lazy initialized QString m_icon; //< the name of the icon file, lazy initialized static bool userDefinedFolderIcons; }; #endif diff --git a/krusader/FileSystem/filesystem.cpp b/krusader/FileSystem/filesystem.cpp index 386176df..1acfc013 100644 --- a/krusader/FileSystem/filesystem.cpp +++ b/krusader/FileSystem/filesystem.cpp @@ -1,312 +1,347 @@ /*************************************************************************** filesystem.cpp ------------------- copyright : (C) 2000 by Shie Erlich & Rafi Yanai e-mail : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net ------------------------------------------------------------------------ the filesystem class is an extendable class which by itself does (almost) nothing. other filesystems like the normal_filesystem inherits from this class and make it possible to use a consistent API for all types of filesystems. *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD S o u r c e F i l e *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ +#include + #include "filesystem.h" // QtCore #include #include #include // QtWidgets #include #include #include #include #include "fileitem.h" #include "krpermhandler.h" #include "../defaults.h" #include "../krglobal.h" #include "../JobMan/jobman.h" #include "../JobMan/krjob.h" FileSystem::FileSystem() : DirListerInterface(0), _isRefreshing(false) {} FileSystem::~FileSystem() { clear(_fileItems); // please don't remove this line. This informs the view about deleting the file items. emit cleared(); } QList FileSystem::getUrls(const QStringList &names) const { QList urls; for (const QString name : names) { urls.append(getUrl(name)); } return urls; } FileItem *FileSystem::getFileItem(const QString &name) const { return _fileItems.contains(name) ? _fileItems.value(name) : 0; } KIO::filesize_t FileSystem::totalSize() const { KIO::filesize_t temp = 0; for (FileItem *item : _fileItems.values()) { if (!item->isDir() && item->getName() != "." && item->getName() != "..") { temp += item->getSize(); } } return temp; } QUrl FileSystem::ensureTrailingSlash(const QUrl &url) { if (url.path().endsWith('/')) { return url; } QUrl adjustedUrl(url); adjustedUrl.setPath(adjustedUrl.path() + '/'); return adjustedUrl; } QUrl FileSystem::preferLocalUrl(const QUrl &url){ if (url.isEmpty() || !url.scheme().isEmpty()) return url; QUrl adjustedUrl = url; adjustedUrl.setScheme("file"); return adjustedUrl; } bool FileSystem::scanOrRefresh(const QUrl &directory, bool onlyScan) { qDebug() << "from current dir=" << _currentDirectory.toDisplayString() << "; to=" << directory.toDisplayString(); if (_isRefreshing) { // NOTE: this does not happen (unless async)"; return false; } // workaround for krarc: find out if transition to local fs is wanted and adjust URL manually QUrl url = directory; if (_currentDirectory.scheme() == "krarc" && url.scheme() == "krarc" && QDir(url.path()).exists()) { url.setScheme("file"); } const bool dirChange = !url.isEmpty() && cleanUrl(url) != _currentDirectory; const QUrl toRefresh = dirChange ? url.adjusted(QUrl::NormalizePathSegments) : _currentDirectory; if (!toRefresh.isValid()) { emit error(i18n("Malformed URL:\n%1", toRefresh.toDisplayString())); return false; } _isRefreshing = true; FileItemDict tempFileItems(_fileItems); // old file items are still used during refresh _fileItems.clear(); if (dirChange) // show an empty directory while loading the new one and clear selection emit cleared(); const bool refreshed = refreshInternal(toRefresh, onlyScan); _isRefreshing = false; if (!refreshed) { // cleanup and abort if (!dirChange) emit cleared(); clear(tempFileItems); return false; } emit scanDone(dirChange); clear(tempFileItems); updateFilesystemInfo(); return true; } void FileSystem::deleteFiles(const QStringList &fileNames, bool moveToTrash) { // get absolute URLs for file names deleteAnyFiles(getUrls(fileNames), moveToTrash); } void FileSystem::deleteAnyFiles(const QList &urls, bool moveToTrash) { KrJob *krJob = KrJob::createDeleteJob(urls, moveToTrash); connect(krJob, &KrJob::started, this, [=](KIO::Job *job) { connectJobToSources(job, urls); }); if (moveToTrash) { // update destination: the trash bin (in case a panel/tab is showing it) connect(krJob, &KrJob::started, this, [=](KIO::Job *job) { // Note: the "trash" protocal should always have only one "/" after the "scheme:" part connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(QUrl("trash:/"), false); }); }); } krJobMan->manageJob(krJob); } void FileSystem::connectJobToSources(KJob *job, const QList urls) { if (!urls.isEmpty()) { // TODO we assume that all files were in the same directory and only emit one signal for // the directory of the first file URL (all subdirectories of parent are notified) const QUrl url = urls.first().adjusted(QUrl::RemoveFilename); connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(url, true); }); } } void FileSystem::connectJobToDestination(KJob *job, const QUrl &destination) { connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(destination, false); }); // (additional) direct refresh if on local fs because watcher is too slow const bool refresh = cleanUrl(destination) == _currentDirectory && isLocal(); connect(job, &KIO::Job::result, this, [=](KJob* job) { slotJobResult(job, refresh); }); } bool FileSystem::showHiddenFiles() { const KConfigGroup gl(krConfig, "Look&Feel"); return gl.readEntry("Show Hidden", _ShowHidden); } void FileSystem::addFileItem(FileItem *item) { _fileItems.insert(item->getName(), item); } FileItem *FileSystem::createLocalFileItem(const QString &name, const QString &directory, bool virt) { const QDir dir = QDir(directory); const QString path = dir.filePath(name); - const QByteArray filePath = path.toLocal8Bit(); + const QByteArray pathByteArray = path.toLocal8Bit(); + const QString fileItemName = virt ? path : name; + const QUrl fileItemUrl = QUrl::fromLocalFile(path); + // read file status; in case of error create a "broken" file item QT_STATBUF stat_p; - stat_p.st_size = 0; - stat_p.st_mode = 0; - stat_p.st_mtime = 0; - stat_p.st_uid = 0; - stat_p.st_gid = 0; - QT_LSTAT(filePath.data(), &stat_p); - const KIO::filesize_t size = stat_p.st_size; + memset(&stat_p, 0, sizeof(stat_p)); + if (QT_LSTAT(pathByteArray.data(), &stat_p) < 0) + return FileItem::createBroken(fileItemName, fileItemUrl); + const KIO::filesize_t size = stat_p.st_size; bool isDir = S_ISDIR(stat_p.st_mode); const bool isLink = S_ISLNK(stat_p.st_mode); + // for links, read link destination and determine whether it's broken or not QString linkDestination; bool brokenLink = false; if (isLink) { - // find where the link is pointing to - qDebug() << "link name=" << path; - // the path of the symlink target cannot be longer than the file size of the symlink - char buffer[stat_p.st_size]; - memset(buffer, 0, sizeof(buffer)); - int bytesRead = readlink(filePath.data(), buffer, sizeof(buffer)); - if (bytesRead != -1) { - linkDestination = QString::fromLocal8Bit(buffer, bytesRead); // absolute or relative + linkDestination = readLinkSafely(pathByteArray.data()); + + if (linkDestination.isNull()) + brokenLink = true; + else { const QFileInfo linkFile(dir, linkDestination); if (!linkFile.exists()) brokenLink = true; else if (linkFile.isDir()) isDir = true; - } else { - qWarning() << "failed to read link, path=" << path; } } - return new FileItem(virt ? path : name, QUrl::fromLocalFile(path), isDir, - size, stat_p.st_mode, - stat_p.st_mtime, stat_p.st_ctime, stat_p.st_atime, - stat_p.st_uid, stat_p.st_gid, QString(), QString(), - isLink, linkDestination, brokenLink); + // create normal file item + return new FileItem(fileItemName, fileItemUrl, isDir, + size, stat_p.st_mode, + stat_p.st_mtime, stat_p.st_ctime, stat_p.st_atime, + stat_p.st_uid, stat_p.st_gid, QString(), QString(), + isLink, linkDestination, brokenLink); +} + +QString FileSystem::readLinkSafely(const char *path) +{ + // inspired by the areadlink_with_size function from gnulib, which is used for coreutils + // idea: start with a small buffer and gradually increase it as we discover it wasn't enough + + QT_OFF_T bufferSize = 1024; // start with 1 KiB + QT_OFF_T maxBufferSize = std::numeric_limits::max(); + + while (true) { + // try to read the link + std::unique_ptr buffer(new char[bufferSize]); + auto nBytesRead = readlink(path, buffer.get(), bufferSize); + + // should never happen, asserted by the readlink + if (nBytesRead > bufferSize) + return QString(); + + // read failure + if (nBytesRead < 0) { + qDebug() << "Failed to read the link " << path; + return QString(); + } + + // read success + if (nBytesRead < bufferSize || nBytesRead == maxBufferSize) + return QString::fromLocal8Bit(buffer.get(), nBytesRead); + + // increase the buffer and retry again + // bufferSize < maxBufferSize is implied from previous checks + if (bufferSize <= maxBufferSize / 2) + bufferSize *= 2; + else + bufferSize = maxBufferSize; + } } FileItem *FileSystem::createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt) { const KFileItem kfi(entry, directory, true, true); const QString name = kfi.text(); // ignore un-needed entries if (name.isEmpty() || name == "." || name == "..") { return 0; } const QString localPath = kfi.localPath(); const QUrl url = !localPath.isEmpty() ? QUrl::fromLocalFile(localPath) : kfi.url(); const QString fname = virt ? url.toDisplayString() : name; // get file statistics... const time_t mtime = kfi.time(KFileItem::ModificationTime).toTime_t(); const time_t ctime = kfi.time(KFileItem::CreationTime).toTime_t(); // "Creation"? its "Changed" const time_t atime = kfi.time(KFileItem::AccessTime).toTime_t(); const mode_t mode = kfi.mode() | kfi.permissions(); // NOTE: we could get the mimetype (and file icon) from the kfileitem here but this is very // slow. Instead, the file item class has it's own (faster) way to determine the file type. // NOTE: "broken link" flag is always false, checking link destination existence is // considered to be too expensive return new FileItem(fname, url, kfi.isDir(), kfi.size(), mode, mtime, ctime, atime, (uid_t) -1, (gid_t) -1, kfi.user(), kfi.group(), kfi.isLink(), kfi.linkDest(), false, kfi.ACL().asString(), kfi.defaultACL().asString()); } void FileSystem::slotJobResult(KJob *job, bool refresh) { if (job->error() && job->uiDelegate()) { // show errors for modifying operations as popup (works always) job->uiDelegate()->showErrorMessage(); } if (refresh) { FileSystem::refresh(); } } void FileSystem::clear(FileItemDict &fileItems) { QHashIterator lit(fileItems); while (lit.hasNext()) { delete lit.next().value(); } fileItems.clear(); } diff --git a/krusader/FileSystem/filesystem.h b/krusader/FileSystem/filesystem.h index c1016648..601e51fe 100644 --- a/krusader/FileSystem/filesystem.h +++ b/krusader/FileSystem/filesystem.h @@ -1,228 +1,231 @@ /*************************************************************************** filesystem.h ------------------- begin : Thu May 4 2000 copyright : (C) 2000 by Shie Erlich & Rafi Yanai e-mail : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD H e a d e r F i l e *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef FILESYSTEM_H #define FILESYSTEM_H #include "dirlisterinterface.h" // QtCore #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include "../JobMan/jobman.h" class FileItem; /** * An abstract filesystem. Use the implementations of this class for all file operations. * * It represents a directory and gives access to its files. All common file operations * are supported. Methods with absolute URL as argument can be used independently from the current * directory. Otherwise - if the methods argument is a file name - the operation is performed inside * the current directory. * * Notification signals are emitted if the directory content may have been changed. */ class FileSystem : public DirListerInterface { Q_OBJECT public: enum FS_TYPE { /// Virtual filesystem. Krusaders custom virt:/ protocol FS_VIRTUAL, /// Filesystem supporting all KIO protocols (file:/, ftp:/, smb:/, etc.) FS_DEFAULT }; FileSystem(); virtual ~FileSystem(); // DirListerInterface implementation inline QList fileItems() const { return _fileItems.values(); } inline unsigned long numFileItems() const { return _fileItems.count(); } inline bool isRoot() const { const QString path = _currentDirectory.path(); return path.isEmpty() || path == "/"; } /// Copy (copy, move or link) files in this filesystem. /// Destination is absolute URL. May implemented async. virtual void copyFiles(const QList &urls, const QUrl &destination, KIO::CopyJob::CopyMode mode = KIO::CopyJob::Copy, bool showProgressInfo = true, JobMan::StartMode startMode = JobMan::Default) = 0; /// Handle file dropping in this filesystem. Destination is absolute URL. May implemented async. virtual void dropFiles(const QUrl &destination, QDropEvent *event) = 0; /// Copy (copy, move or link) files to the current filesystem directory or to "dir", the /// directory name relative to the current dir. May implemented async. virtual void addFiles(const QList &fileUrls, KIO::CopyJob::CopyMode mode, const QString &dir = "") = 0; /// Create a new directory in the current directory. May implemented async. virtual void mkDir(const QString &name) = 0; /// Rename file/directory in the current directory. May implemented async. virtual void rename(const QString &fileName, const QString &newName) = 0; /// Return an absolute URL for a single file/directory name in the current directory - with no /// trailing slash. virtual QUrl getUrl(const QString &name) const = 0; /// Return a list of URLs for multiple files/directories in the current directory. QList getUrls(const QStringList &names) const; /// Return true if all files can be moved to trash, else false. virtual bool canMoveToTrash(const QStringList &fileNames) const = 0; /// Return the filesystem mount point of the current directory. Empty string by default. virtual QString mountPoint() const { return QString(); } /// Returns true if this filesystem implementation does not need to be notified about changes in the /// current directory. Else false. virtual bool hasAutoUpdate() const { return false; } /// Notify this filesystem that the filesystem info of the current directory may have changed. virtual void updateFilesystemInfo() {} /** * Scan all files and directories in a directory and create the file items for them. Blocking. * * @param directory if given, the lister tries to change to this directory, else the old * directory is refreshed * @return true if scan was successful, else (not implemented, scan failed or refresh job * was killed) false. */ bool scanDir(const QUrl &directory = QUrl()) { return scanOrRefresh(directory, false); } /// Change or refresh the current directory and scan it. Blocking. /// Returns true if directory was scanned. Returns false if failed or scan job was killed. bool refresh(const QUrl &directory = QUrl()) { return scanOrRefresh(directory, false); } /// Returns the current directory path of this filesystem. inline QUrl currentDirectory() const { return _currentDirectory; } /// Return the file item for a file name in the current directory. Or 0 if not found. FileItem *getFileItem(const QString &name) const; /// The total size of all files in the current directory (only valid after scan). // TODO unused KIO::filesize_t totalSize() const; /// Return the filesystem type. inline FS_TYPE type() const { return _type; } /// Return true if the current directory is local (without recognizing mount points). inline bool isLocal() const { return _currentDirectory.isLocalFile(); } /// Return true if the current directory is a remote (network) location. inline bool isRemote() const { const QString sc = _currentDirectory.scheme(); return (sc == "fish" || sc == "ftp" || sc == "sftp" || sc == "nfs" || sc == "smb" || sc == "webdav"); } /// Returns true if this filesystem is currently refreshing the current directory. inline bool isRefreshing() const { return _isRefreshing; } /// Delete or trash files in the current directory. Implemented async. void deleteFiles(const QStringList &fileNames, bool moveToTrash = true); /// Delete or trash arbitrary files. Implemented async. Universal refresh not fully implemented. void deleteAnyFiles(const QList &urls, bool moveToTrash); /// Return the input URL with a trailing slash if absent. static QUrl ensureTrailingSlash(const QUrl &url); /// Return the input URL without trailing slash. static QUrl cleanUrl(const QUrl &url) { return url.adjusted(QUrl::StripTrailingSlash); } /// Add 'file' scheme to non-empty URL without scheme static QUrl preferLocalUrl(const QUrl &url); /// Return a file item for a local file inside a directory static FileItem *createLocalFileItem(const QString &name, const QString &directory, bool virt = false); /// Return a file item for a KIO result. Returns 0 if entry is not needed static FileItem *createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt = false); - // set the parent window to be used for dialogs + /// Read a symlink with an extra precaution + static QString readLinkSafely(const char *path); + + /// Set the parent window to be used for dialogs void setParentWindow(QWidget *widget) { parentWindow = widget; } signals: /// Emitted when this filesystem is currently refreshing the filesystem directory. void refreshJobStarted(KIO::Job *job); /// Emitted when an error occurred in this filesystem during refresh. void error(const QString &msg); /// Emitted when the content of a directory was changed by this filesystem. void fileSystemChanged(const QUrl &directory, bool removed); /// Emitted when the information for the filesystem of the current directory changed. /// Information is either /// * 'metaInfo': a displayable string about the fs, empty by default, OR /// * 'fsType', 'total' and 'free': filesystem type, size and free space, /// empty string or 0 by default void fileSystemInfoChanged(const QString &metaInfo, const QString &fsType, KIO::filesize_t total, KIO::filesize_t free); /// Emitted before a directory path is opened for reading. Used for automounting. void aboutToOpenDir(const QString &path); protected: /// Fill the filesystem dictionary with file items, must be implemented for each filesystem. virtual bool refreshInternal(const QUrl &origin, bool stayInDir) = 0; /// Connect the result signal of a file operation job - source URLs. void connectJobToSources(KJob *job, const QList urls); /// Connect the result signal of a file operation job - destination URL. void connectJobToDestination(KJob *job, const QUrl &destination); /// Returns true if showing hidden files is set in config. bool showHiddenFiles(); /// Add a new file item to the internal dictionary (while refreshing). void addFileItem(FileItem *item); FS_TYPE _type; // the filesystem type. QUrl _currentDirectory; // the path or file the filesystem originates from. bool _isRefreshing; // true if filesystem is busy with refreshing QPointer parentWindow; protected slots: /// Handle result after job (except when refreshing!) finished void slotJobResult(KJob *job, bool refresh); private: typedef QHash FileItemDict; // optional TODO: add an async version of this bool scanOrRefresh(const QUrl &directory, bool onlyScan); /// Delete and clear file items. void clear(FileItemDict &fileItems); FileItemDict _fileItems; // the list of files in the current dictionary }; #endif