1 /*****************************************************************************
2  * Copyright (C) 2003 Shie Erlich <erlich@users.sourceforge.net>             *
3  * Copyright (C) 2003 Rafi Yanai <yanai@users.sourceforge.net>               *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "filesystemprovider.h"
23 
24 #ifdef HAVE_POSIX_ACL
25 #include <sys/acl.h>
26 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
27 #include <acl/libacl.h>
28 #endif
29 #endif
30 
31 // QtCore
32 #include <QDebug>
33 #include <QDir>
34 
35 #include <KIOCore/KMountPoint>
36 
37 #include "defaultfilesystem.h"
38 #include "fileitem.h"
39 #include "virtualfilesystem.h"
40 #include "../krservices.h"
41 #include "../JobMan/jobman.h"
42 
43 
FileSystemProvider()44 FileSystemProvider::FileSystemProvider() : _defaultFileSystem(0), _virtFileSystem(0) {}
45 
getFilesystem(const QUrl & url,FileSystem * oldFilesystem)46 FileSystem *FileSystemProvider::getFilesystem(const QUrl &url, FileSystem *oldFilesystem)
47 {
48     const FileSystem::FS_TYPE type = getFilesystemType(url);
49     return oldFilesystem && oldFilesystem->type() == type ? oldFilesystem : createFilesystem(type);
50 }
51 
startCopyFiles(const QList<QUrl> & urls,const QUrl & destination,KIO::CopyJob::CopyMode mode,bool showProgressInfo,JobMan::StartMode startMode)52 void FileSystemProvider::startCopyFiles(const QList<QUrl> &urls, const QUrl &destination,
53                                   KIO::CopyJob::CopyMode mode, bool showProgressInfo,
54                                   JobMan::StartMode startMode)
55 {
56     FileSystem *fs = getFilesystemInstance(destination);
57     fs->copyFiles(urls, destination, mode, showProgressInfo, startMode);
58 }
59 
startDropFiles(QDropEvent * event,const QUrl & destination)60 void FileSystemProvider::startDropFiles(QDropEvent *event, const QUrl &destination)
61 {
62     FileSystem *fs = getFilesystemInstance(destination);
63     fs->dropFiles(destination, event);
64 }
65 
startDeleteFiles(const QList<QUrl> & urls,bool moveToTrash)66 void FileSystemProvider::startDeleteFiles(const QList<QUrl> &urls, bool moveToTrash)
67 {
68     if (urls.isEmpty())
69         return;
70 
71     // assume all URLs use the same filesystem
72     FileSystem *fs = getFilesystemInstance(urls.first());
73     fs->deleteFiles(urls, moveToTrash);
74 }
75 
refreshFilesystems(const QUrl & directory,bool removed)76 void FileSystemProvider::refreshFilesystems(const QUrl &directory, bool removed)
77 {
78     qDebug() << "changed=" << directory.toDisplayString();
79 
80     QMutableListIterator<QPointer<FileSystem>> it(_fileSystems);
81     while (it.hasNext()) {
82         if (it.next().isNull()) {
83             it.remove();
84         }
85     }
86 
87     QString mountPoint = "";
88     if (directory.isLocalFile()) {
89         KMountPoint::Ptr kMountPoint = KMountPoint::currentMountPoints().findByPath(directory.path());
90         if (kMountPoint)
91             mountPoint = kMountPoint->mountPoint();
92     }
93 
94     for(QPointer<FileSystem> fileSystemPointer: _fileSystems) {
95         FileSystem *fs = fileSystemPointer.data();
96         // refresh all filesystems currently showing this directory
97         // and always refresh filesystems showing a virtual directory; it can contain files from
98         // various places, we don't know if they were (re)moved. Refreshing is also fast enough.
99         const QUrl fileSystemDir = fs->currentDirectory();
100         if ((!fs->hasAutoUpdate() && (fileSystemDir == FileSystem::cleanUrl(directory) ||
101                                       (fs->type() == FileSystem::FS_VIRTUAL && !fs->isRoot())))
102             // also refresh if a parent directory was (re)moved (not detected by file watcher)
103             || (removed && directory.isParentOf(fileSystemDir))) {
104             fs->refresh();
105             // ..or refresh filesystem info if mount point is the same (for free space update)
106         } else if (!mountPoint.isEmpty() && mountPoint == fs->mountPoint()) {
107             fs->updateFilesystemInfo();
108         }
109     }
110 }
111 
getFilesystemInstance(const QUrl & directory)112 FileSystem *FileSystemProvider::getFilesystemInstance(const QUrl &directory)
113 {
114     const FileSystem::FS_TYPE type = getFilesystemType(directory);
115     switch (type) {
116     case FileSystem::FS_VIRTUAL:
117         if (!_virtFileSystem)
118             _virtFileSystem = createFilesystem(type);
119         return _virtFileSystem;
120         break;
121     default:
122         if (!_defaultFileSystem)
123             _defaultFileSystem = createFilesystem(type);
124         return _defaultFileSystem;
125     }
126 }
127 
createFilesystem(FileSystem::FS_TYPE type)128 FileSystem *FileSystemProvider::createFilesystem(FileSystem::FS_TYPE type)
129 {
130     FileSystem *newFilesystem;
131     switch (type) {
132     case (FileSystem::FS_VIRTUAL): newFilesystem = new VirtualFileSystem(); break;
133     default: newFilesystem = new DefaultFileSystem();
134     }
135 
136     QPointer<FileSystem> fileSystemPointer(newFilesystem);
137     _fileSystems.append(fileSystemPointer);
138     connect(newFilesystem, &FileSystem::fileSystemChanged, this, &FileSystemProvider::refreshFilesystems);
139     return newFilesystem;
140 }
141 
142 // ==== static ====
143 
instance()144 FileSystemProvider &FileSystemProvider::instance()
145 {
146     static FileSystemProvider instance;
147     return instance;
148 }
149 
getFilesystemType(const QUrl & url)150 FileSystem::FS_TYPE FileSystemProvider::getFilesystemType(const QUrl &url)
151 {
152     return url.scheme() == QStringLiteral("virt") ? FileSystem::FS_VIRTUAL : FileSystem::FS_DEFAULT;
153 }
154 
getACL(FileItem * file,QString & acl,QString & defAcl)155 void FileSystemProvider::getACL(FileItem *file, QString &acl, QString &defAcl)
156 {
157     Q_UNUSED(file);
158     acl.clear();
159     defAcl.clear();
160 #ifdef HAVE_POSIX_ACL
161     QString fileName = FileSystem::cleanUrl(file->getUrl()).path();
162 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
163     if (acl_extended_file(fileName)) {
164 #endif
165         acl = getACL(fileName, ACL_TYPE_ACCESS);
166         if (file->isDir())
167             defAcl = getACL(fileName, ACL_TYPE_DEFAULT);
168 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
169     }
170 #endif
171 #endif
172 }
173 
getACL(const QString & path,int type)174 QString FileSystemProvider::getACL(const QString & path, int type)
175 {
176     Q_UNUSED(path);
177     Q_UNUSED(type);
178 #ifdef HAVE_POSIX_ACL
179     acl_t acl = 0;
180     // do we have an acl for the file, and/or a default acl for the dir, if it is one?
181     if ((acl = acl_get_file(path.toLocal8Bit(), type)) != 0) {
182         bool aclExtended = false;
183 
184 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
185         aclExtended = acl_equiv_mode(acl, 0);
186 #else
187         acl_entry_t entry;
188         int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
189         while (ret == 1) {
190             acl_tag_t currentTag;
191             acl_get_tag_type(entry, &currentTag);
192             if (currentTag != ACL_USER_OBJ &&
193                     currentTag != ACL_GROUP_OBJ &&
194                     currentTag != ACL_OTHER) {
195                 aclExtended = true;
196                 break;
197             }
198             ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
199         }
200 #endif
201 
202         if (!aclExtended) {
203             acl_free(acl);
204             acl = 0;
205         }
206     }
207 
208     if (acl == 0)
209         return QString();
210 
211     char *aclString = acl_to_text(acl, 0);
212     QString ret = QString::fromLatin1(aclString);
213     acl_free((void*)aclString);
214     acl_free(acl);
215 
216     return ret;
217 #else
218     return QString();
219 #endif
220 }
221