File indexing completed on 2024-05-05 17:56:55

0001 /*
0002     SPDX-FileCopyrightText: 2003 Shie Erlich <erlich@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2003 Rafi Yanai <yanai@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "filesystemprovider.h"
0010 
0011 #ifdef HAVE_POSIX_ACL
0012 #include <sys/acl.h>
0013 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
0014 #include <acl/libacl.h>
0015 #endif
0016 #endif
0017 
0018 // QtCore
0019 #include <QDebug>
0020 #include <QDir>
0021 
0022 #include <KIOCore/KMountPoint>
0023 
0024 #include "../JobMan/jobman.h"
0025 #include "../krservices.h"
0026 #include "defaultfilesystem.h"
0027 #include "fileitem.h"
0028 #include "virtualfilesystem.h"
0029 
0030 FileSystemProvider::FileSystemProvider()
0031     : _defaultFileSystem(nullptr)
0032     , _virtFileSystem(nullptr)
0033 {
0034 }
0035 
0036 FileSystem *FileSystemProvider::getFilesystem(const QUrl &url, FileSystem *oldFilesystem)
0037 {
0038     const FileSystem::FS_TYPE type = getFilesystemType(url);
0039     return oldFilesystem && oldFilesystem->type() == type ? oldFilesystem : createFilesystem(type);
0040 }
0041 
0042 void FileSystemProvider::startCopyFiles(const QList<QUrl> &urls,
0043                                         const QUrl &destination,
0044                                         KIO::CopyJob::CopyMode mode,
0045                                         bool showProgressInfo,
0046                                         JobMan::StartMode startMode)
0047 {
0048     FileSystem *fs = getFilesystemInstance(destination);
0049     fs->copyFiles(urls, destination, mode, showProgressInfo, startMode);
0050 }
0051 
0052 void FileSystemProvider::startDropFiles(QDropEvent *event, const QUrl &destination)
0053 {
0054     FileSystem *fs = getFilesystemInstance(destination);
0055     fs->dropFiles(destination, event);
0056 }
0057 
0058 void FileSystemProvider::startDeleteFiles(const QList<QUrl> &urls, bool moveToTrash)
0059 {
0060     if (urls.isEmpty())
0061         return;
0062 
0063     // assume all URLs use the same filesystem
0064     FileSystem *fs = getFilesystemInstance(urls.first());
0065     fs->deleteFiles(urls, moveToTrash);
0066 }
0067 
0068 void FileSystemProvider::refreshFilesystems(const QUrl &directory, bool removed)
0069 {
0070     qDebug() << "changed=" << directory.toDisplayString();
0071 
0072     QMutableListIterator<QPointer<FileSystem>> it(_fileSystems);
0073     while (it.hasNext()) {
0074         if (it.next().isNull()) {
0075             it.remove();
0076         }
0077     }
0078 
0079     QString mountPoint = "";
0080     if (directory.isLocalFile()) {
0081         KMountPoint::Ptr kMountPoint = KMountPoint::currentMountPoints().findByPath(directory.path());
0082         if (kMountPoint)
0083             mountPoint = kMountPoint->mountPoint();
0084     }
0085 
0086     for (const QPointer<FileSystem> &fileSystemPointer : _fileSystems) {
0087         FileSystem *fs = fileSystemPointer.data();
0088         // refresh all filesystems currently showing this directory
0089         // and always refresh filesystems showing a virtual directory; it can contain files from
0090         // various places, we don't know if they were (re)moved. Refreshing is also fast enough.
0091         const QUrl fileSystemDir = fs->currentDirectory();
0092         if ((!fs->hasAutoUpdate() && (fileSystemDir == FileSystem::cleanUrl(directory) || (fs->type() == FileSystem::FS_VIRTUAL && !fs->isRoot())))
0093             // also refresh if a parent directory was (re)moved (not detected by file watcher)
0094             || (removed && directory.isParentOf(fileSystemDir))) {
0095             fs->refresh();
0096             // ..or refresh filesystem info if mount point is the same (for free space update)
0097         } else if (!mountPoint.isEmpty() && mountPoint == fs->mountPoint()) {
0098             fs->updateFilesystemInfo();
0099         }
0100     }
0101 }
0102 
0103 FileSystem *FileSystemProvider::getFilesystemInstance(const QUrl &directory)
0104 {
0105     const FileSystem::FS_TYPE type = getFilesystemType(directory);
0106     switch (type) {
0107     case FileSystem::FS_VIRTUAL:
0108         if (!_virtFileSystem)
0109             _virtFileSystem = createFilesystem(type);
0110         return _virtFileSystem;
0111         break;
0112     default:
0113         if (!_defaultFileSystem)
0114             _defaultFileSystem = createFilesystem(type);
0115         return _defaultFileSystem;
0116     }
0117 }
0118 
0119 FileSystem *FileSystemProvider::createFilesystem(FileSystem::FS_TYPE type)
0120 {
0121     FileSystem *newFilesystem;
0122     switch (type) {
0123     case (FileSystem::FS_VIRTUAL):
0124         newFilesystem = new VirtualFileSystem();
0125         break;
0126     default:
0127         newFilesystem = new DefaultFileSystem();
0128     }
0129 
0130     QPointer<FileSystem> fileSystemPointer(newFilesystem);
0131     _fileSystems.append(fileSystemPointer);
0132     connect(newFilesystem, &FileSystem::fileSystemChanged, this, &FileSystemProvider::refreshFilesystems);
0133     return newFilesystem;
0134 }
0135 
0136 // ==== static ====
0137 
0138 FileSystemProvider &FileSystemProvider::instance()
0139 {
0140     static FileSystemProvider instance;
0141     return instance;
0142 }
0143 
0144 FileSystem::FS_TYPE FileSystemProvider::getFilesystemType(const QUrl &url)
0145 {
0146     return url.scheme() == QStringLiteral("virt") ? FileSystem::FS_VIRTUAL : FileSystem::FS_DEFAULT;
0147 }
0148 
0149 void FileSystemProvider::getACL(FileItem *file, QString &acl, QString &defAcl)
0150 {
0151     Q_UNUSED(file);
0152     acl.clear();
0153     defAcl.clear();
0154 #ifdef HAVE_POSIX_ACL
0155     QString fileName = FileSystem::cleanUrl(file->getUrl()).path();
0156 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
0157     if (acl_extended_file(fileName)) {
0158 #endif
0159         acl = getACL(fileName, ACL_TYPE_ACCESS);
0160         if (file->isDir())
0161             defAcl = getACL(fileName, ACL_TYPE_DEFAULT);
0162 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
0163     }
0164 #endif
0165 #endif
0166 }
0167 
0168 QString FileSystemProvider::getACL(const QString &path, int type)
0169 {
0170     Q_UNUSED(path);
0171     Q_UNUSED(type);
0172 #ifdef HAVE_POSIX_ACL
0173     acl_t acl = nullptr;
0174     // do we have an acl for the file, and/or a default acl for the dir, if it is one?
0175     if ((acl = acl_get_file(path.toLocal8Bit(), type)) != nullptr) {
0176         bool aclExtended = false;
0177 
0178 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
0179         aclExtended = acl_equiv_mode(acl, 0);
0180 #else
0181         acl_entry_t entry;
0182         int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
0183         while (ret == 1) {
0184             acl_tag_t currentTag;
0185             acl_get_tag_type(entry, &currentTag);
0186             if (currentTag != ACL_USER_OBJ && currentTag != ACL_GROUP_OBJ && currentTag != ACL_OTHER) {
0187                 aclExtended = true;
0188                 break;
0189             }
0190             ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
0191         }
0192 #endif
0193 
0194         if (!aclExtended) {
0195             acl_free(acl);
0196             acl = nullptr;
0197         }
0198     }
0199 
0200     if (acl == nullptr)
0201         return QString();
0202 
0203     char *aclString = acl_to_text(acl, nullptr);
0204     QString ret = QString::fromLatin1(aclString);
0205     acl_free((void *)aclString);
0206     acl_free(acl);
0207 
0208     return ret;
0209 #else
0210     return QString();
0211 #endif
0212 }