Support Windows .lnk files with VFS Cf API.
authoralex-z <blackslayer4@gmail.com>
Wed, 10 Apr 2024 19:54:37 +0000 (21:54 +0200)
committerMatthieu Gallien <matthieu.gallien@nextcloud.com>
Wed, 10 Jul 2024 08:29:21 +0000 (10:29 +0200)
Signed-off-by: alex-z <blackslayer4@gmail.com>
27 files changed:
src/common/filesystembase.cpp
src/common/filesystembase.h
src/csync/csync_exclude.cpp
src/gui/caseclashfilenamedialog.cpp
src/gui/conflictsolver.cpp
src/gui/folder.cpp
src/gui/folderman.cpp
src/gui/folderwatcher.cpp
src/gui/folderwatcher_win.cpp
src/gui/owncloudgui.cpp
src/gui/socketapi/socketapi.cpp
src/gui/syncrunfilelog.cpp
src/gui/tray/usermodel.cpp
src/libsync/caseclashconflictsolver.cpp
src/libsync/discovery.cpp
src/libsync/filesystem.cpp
src/libsync/filesystem.h
src/libsync/owncloudpropagator.cpp
src/libsync/propagatedownload.cpp
src/libsync/propagateremotemove.cpp
src/libsync/propagateuploadencrypted.cpp
src/libsync/propagatorjobs.cpp
src/libsync/syncengine.cpp
src/libsync/vfs/cfapi/cfapiwrapper.cpp
src/libsync/vfs/cfapi/vfs_cfapi.cpp
src/libsync/vfs/suffix/vfs_suffix.cpp
test/testsynccfapi.cpp

index ef9ba1efd677812b9e52e092dbe7e4d38031600e..42dc8dab06d49a331ce2f226b01b469e74ffee0d 100644 (file)
@@ -52,8 +52,11 @@ QString FileSystem::longWinPath(const QString &inpath)
 
 void FileSystem::setFileHidden(const QString &filename, bool hidden)
 {
+    if (filename.isEmpty()) {
+        return;
+    }
 #ifdef _WIN32
-    QString fName = longWinPath(filename);
+    const QString fName = longWinPath(filename);
     DWORD dwAttrs = 0;
 
     dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());
@@ -71,6 +74,24 @@ void FileSystem::setFileHidden(const QString &filename, bool hidden)
 #endif
 }
 
+bool FileSystem::isFileHidden(const QString &filename)
+{
+#ifdef _WIN32
+    if (isLnkFile(filename)) {
+        const QString fName = longWinPath(filename);
+        DWORD dwAttrs = 0;
+
+        dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());
+
+        if (dwAttrs == INVALID_FILE_ATTRIBUTES) {
+            return false;
+        }
+        return dwAttrs & FILE_ATTRIBUTE_HIDDEN;
+    }
+#endif
+    return QFileInfo(filename).isHidden();
+}
+
 static QFile::Permissions getDefaultWritePermissions()
 {
     QFile::Permissions result = QFile::WriteUser;
@@ -89,11 +110,27 @@ static QFile::Permissions getDefaultWritePermissions()
 
 void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
 {
+#ifdef  Q_OS_WIN
+    if (isLnkFile(filename)) {
+        if (!fileExists(filename)) {
+            return;
+        }
+        const auto permissions = filePermissionsWin(filename);
+
+        std::filesystem::perms allWritePermissions = std::filesystem::perms::_All_write;
+        static std::filesystem::perms defaultWritePermissions = std::filesystem::perms::others_write;
+
+        std::filesystem::permissions(filename.toStdString(), allWritePermissions, std::filesystem::perm_options::remove);
+
+        if (!readonly) {
+            std::filesystem::permissions(filename.toStdString(), defaultWritePermissions, std::filesystem::perm_options::add);
+        }
+    }
+#endif
     QFile file(filename);
     QFile::Permissions permissions = file.permissions();
 
-    QFile::Permissions allWritePermissions =
-        QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
+    QFile::Permissions allWritePermissions = QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
     static QFile::Permissions defaultWritePermissions = getDefaultWritePermissions();
 
     permissions &= ~allWritePermissions;
@@ -116,6 +153,18 @@ void FileSystem::setFolderMinimumPermissions(const QString &filename)
 
 bool FileSystem::setFileReadOnlyWeak(const QString &filename, bool readonly)
 {
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        const auto permissions = filePermissionsWin(filename);
+
+        if (!readonly && static_cast<bool>((permissions & std::filesystem::perms::owner_write))) {
+            return false; // already writable enough
+        }
+
+        setFileReadOnly(filename, readonly);
+        return true;
+    }
+#endif
     QFile file(filename);
     QFile::Permissions permissions = file.permissions();
 
@@ -193,7 +242,7 @@ bool FileSystem::uncheckedRenameReplace(const QString &originFileName,
 
 #else //Q_OS_WIN
     // You can not overwrite a read-only file on windows.
-    if (!QFileInfo(destinationFileName).isWritable()) {
+    if (!isWritable(destinationFileName)) {
         setFileReadOnly(destinationFileName, false);
     }
 
@@ -289,11 +338,24 @@ bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qi
 }
 
 #ifdef Q_OS_WIN
+std::filesystem::perms FileSystem::filePermissionsWin(const QString &filename)
+{
+    return std::filesystem::status(filename.toStdString()).permissions();
+}
+
+void FileSystem::setFilePermissionsWin(const QString &filename, const std::filesystem::perms &perms)
+{
+    if (!fileExists(filename)) {
+        return;
+    }
+    std::filesystem::permissions(filename.toStdString(), perms);
+}
+
 static bool fileExistsWin(const QString &filename)
 {
     WIN32_FIND_DATA FindFileData;
     HANDLE hFind = nullptr;
-    QString fName = FileSystem::longWinPath(filename);
+    const QString fName = FileSystem::longWinPath(filename);
 
     hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
     if (hFind == INVALID_HANDLE_VALUE) {
@@ -302,6 +364,25 @@ static bool fileExistsWin(const QString &filename)
     FindClose(hFind);
     return true;
 }
+
+static bool isDirWin(const QString &filename)
+{
+    WIN32_FIND_DATA FindFileData;
+    HANDLE hFind = nullptr;
+    const QString fName = FileSystem::longWinPath(filename);
+
+    hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
+    if (hFind == INVALID_HANDLE_VALUE) {
+        return false;
+    }
+    FindClose(hFind);
+    return FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+}
+
+static bool isFileWin(const QString &filename)
+{
+    return !isDirWin(filename);
+}
 #endif
 
 bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
@@ -323,6 +404,100 @@ bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
     return re;
 }
 
+bool FileSystem::isDir(const QString &filename, const QFileInfo &fileInfo)
+{
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        // Use a native check.
+        return isDirWin(filename);
+    }
+#endif
+    bool re = fileInfo.isDir();
+    // if the filename is different from the filename in fileInfo, the fileInfo is
+    // not valid. There needs to be one initialised here. Otherwise the incoming
+    // fileInfo is re-used.
+    if (fileInfo.filePath() != filename) {
+        QFileInfo myFI(filename);
+        re = myFI.isDir();
+    }
+    return re;
+}
+
+bool FileSystem::isFile(const QString &filename, const QFileInfo &fileInfo)
+{
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        // Use a native check.
+        return isFileWin(filename);
+    }
+#endif
+    bool re = fileInfo.isDir();
+    // if the filename is different from the filename in fileInfo, the fileInfo is
+    // not valid. There needs to be one initialised here. Otherwise the incoming
+    // fileInfo is re-used.
+    if (fileInfo.filePath() != filename) {
+        QFileInfo myFI(filename);
+        re = myFI.isFile();
+    }
+    return re;
+}
+
+bool FileSystem::isWritable(const QString &filename, const QFileInfo &fileInfo)
+{
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        const auto permissions = filePermissionsWin(filename);
+        return static_cast<bool>((permissions & std::filesystem::perms::owner_write));
+    }
+#endif
+    bool re = fileInfo.isWritable();
+    // if the filename is different from the filename in fileInfo, the fileInfo is
+    // not valid. There needs to be one initialised here. Otherwise the incoming
+    // fileInfo is re-used.
+    if (fileInfo.filePath() != filename) {
+        QFileInfo myFI(filename);
+        re = myFI.isWritable();
+    }
+    return re;
+}
+
+bool FileSystem::isReadable(const QString &filename, const QFileInfo &fileInfo)
+{
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        const auto permissions = filePermissionsWin(filename);
+        return static_cast<bool>((permissions & std::filesystem::perms::owner_read));
+    }
+#endif
+    bool re = fileInfo.isReadable();
+    // if the filename is different from the filename in fileInfo, the fileInfo is
+    // not valid. There needs to be one initialised here. Otherwise the incoming
+    // fileInfo is re-used.
+    if (fileInfo.filePath() != filename) {
+        QFileInfo myFI(filename);
+        re = myFI.isReadable();
+    }
+    return re;
+}
+
+bool FileSystem::isSymLink(const QString &filename, const QFileInfo &fileInfo)
+{
+#ifdef Q_OS_WIN
+    if (isLnkFile(filename)) {
+        return isJunction(filename);
+    }
+#endif
+    bool re = fileInfo.isSymLink();
+    // if the filename is different from the filename in fileInfo, the fileInfo is
+    // not valid. There needs to be one initialised here. Otherwise the incoming
+    // fileInfo is re-used.
+    if (fileInfo.filePath() != filename) {
+        QFileInfo myFI(filename);
+        re = myFI.isSymLink();
+    }
+    return re;
+}
+
 #ifdef Q_OS_WIN
 QString FileSystem::fileSystemForPath(const QString &path)
 {
@@ -351,7 +526,7 @@ bool FileSystem::remove(const QString &fileName, QString *errorString)
 #ifdef Q_OS_WIN
     // You cannot delete a read-only file on windows, but we want to
     // allow that.
-    if (!QFileInfo(fileName).isWritable()) {
+    if (!isWritable(fileName)) {
         setFileReadOnly(fileName, false);
     }
 #endif
index 6a3baa7afb6e3ee3610b41bba88a31da580902cd..8f5554000e079c28172aa666abfcc1d762f5b12a 100644 (file)
 
 #include <ctime>
 
+#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
+#include <filesystem>
+#endif
+
 class QFile;
 
 namespace OCC {
@@ -53,6 +57,8 @@ namespace FileSystem {
      */
     void OCSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);
 
+    bool OCSYNC_EXPORT isFileHidden(const QString &filename);
+
     /**
      * @brief Marks the file as read-only.
      *
@@ -89,6 +95,34 @@ namespace FileSystem {
      */
     bool OCSYNC_EXPORT fileExists(const QString &filename, const QFileInfo & = QFileInfo());
 
+    /**
+     * @brief Checks whether it is a dir.
+     *
+     * Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
+     * files, see above.
+     */
+    bool OCSYNC_EXPORT isDir(const QString &filename, const QFileInfo& = QFileInfo());
+
+    /**
+     * @brief Checks whether it is a file.
+     *
+     * Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
+     * files, see above.
+     */
+    bool OCSYNC_EXPORT isFile(const QString &filename, const QFileInfo& fileInfo = QFileInfo());
+
+    /**
+     * @brief Checks whether the file is writable.
+     *
+     * Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
+     * files, see above.
+     */
+    bool OCSYNC_EXPORT isWritable(const QString &filename, const QFileInfo &fileInfo = QFileInfo());
+
+    bool OCSYNC_EXPORT isReadable(const QString &filename, const QFileInfo &fileInfo = QFileInfo());
+
+    bool OCSYNC_EXPORT isSymLink(const QString &filename, const QFileInfo &fileInfo = QFileInfo());
+
     /**
      * @brief Rename the file \a originFileName to \a destinationFileName.
      *
@@ -146,6 +180,9 @@ namespace FileSystem {
      *    the windows API functions work with the normal "unixoid" representation too.
      */
     QString OCSYNC_EXPORT pathtoUNC(const QString &str);
+
+    std::filesystem::perms OCSYNC_EXPORT filePermissionsWin(const QString &filename);
+    void OCSYNC_EXPORT setFilePermissionsWin(const QString &filename, const std::filesystem::perms &perms);
 #endif
 
     /**
index a0411e7f5c3d4b492204b917f2253c80b42083e6..5d0b462bd4697b7fa923f42b99dea7691df11732 100644 (file)
@@ -31,6 +31,7 @@
 #include "csync_exclude.h"
 
 #include "common/utility.h"
+#include "common/filesystembase.h"
 #include "../version.h"
 
 #include <QString>
@@ -402,7 +403,7 @@ bool ExcludedFiles::isExcluded(
         while (path.size() > basePath.size()) {
             QFileInfo fi(path);
             if (fi.fileName() != QStringLiteral(".sync-exclude.lst")
-                && (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.')))) {
+                && (FileSystem::isFileHidden(path) || fi.fileName().startsWith(QLatin1Char('.')))) {
                 return true;
             }
 
@@ -411,9 +412,8 @@ bool ExcludedFiles::isExcluded(
         }
     }
 
-    QFileInfo fi(filePath);
     ItemType type = ItemTypeFile;
-    if (fi.isDir()) {
+    if (OCC::FileSystem::isDir(filePath)) {
         type = ItemTypeDirectory;
     }
 
@@ -437,9 +437,7 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const QString &path, Ite
     if (filetype == ItemTypeDirectory) {
         const auto basePath = QString(_localPath + path + QLatin1Char('/'));
         const QString absolutePath = basePath + QStringLiteral(".sync-exclude.lst");
-        QFileInfo excludeFileInfo(absolutePath);
-
-        if (excludeFileInfo.isReadable()) {
+        if (FileSystem::isReadable(absolutePath)) {
             addExcludeFilePath(absolutePath);
             reloadExcludeFiles();
         } else {
index 27ca251a69911a252c44d96a0f7d190be92e4383..88a152b98e42d55af34f5a526cba112bfeb0819a 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "account.h"
 #include "folder.h"
+#include "common/filesystembase.h"
 
 #include <QPushButton>
 #include <QDir>
@@ -180,12 +181,12 @@ QString CaseClashFilenameDialog::caseClashConflictFile(const QString &conflictFi
     while(it.hasNext()) {
         const auto filePath = it.next();
         qCDebug(lcCaseClashConflictFialog) << filePath;
-        QFileInfo fileInfo(filePath);
 
-        if(fileInfo.isDir()) {
+        if (FileSystem::isDir(filePath)) {
             continue;
         }
 
+        QFileInfo fileInfo(filePath);
         const auto currentFileName = fileInfo.fileName();
         if (currentFileName.compare(conflictFileName, Qt::CaseInsensitive) == 0 &&
                 currentFileName != conflictFileName) {
index 6a1de573c1e735742a4c606896f7e9f9b8cfa0a6..780e4087b1dd4b77912c62cc106f0f26e5dd78c6 100644 (file)
@@ -80,18 +80,19 @@ bool ConflictSolver::deleteLocalVersion()
         return false;
     }
 
-    QFileInfo info(_localVersionFilename);
-    if (!info.exists()) {
+    if (!FileSystem::fileExists(_localVersionFilename)) {
         return false;
     }
 
-    const auto message = info.isDir() ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
+    QFileInfo info(_localVersionFilename);
+    const auto message = FileSystem::isDir(_localVersionFilename)
+        ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
                                       : tr("Do you want to delete the file <i>%1</i> permanently?").arg(info.fileName());
     const auto result = QMessageBox::question(_parentWidget, tr("Confirm deletion"), message, QMessageBox::Yes, QMessageBox::No);
     if (result != QMessageBox::Yes)
         return false;
 
-    if (info.isDir()) {
+    if (FileSystem::isDir(_localVersionFilename)) {
         return FileSystem::removeRecursively(_localVersionFilename);
     } else {
         return QFile(_localVersionFilename).remove();
@@ -118,7 +119,7 @@ bool ConflictSolver::renameLocalVersion()
     const auto targetFilename = [=] {
         uint i = 1;
         auto result = renamePattern.arg(i);
-        while (QFileInfo::exists(result)) {
+        while (FileSystem::fileExists(result)) {
             Q_ASSERT(i > 0);
             i++;
             result = renamePattern.arg(i);
@@ -146,8 +147,7 @@ bool ConflictSolver::overwriteRemoteVersion()
         return false;
     }
 
-    QFileInfo info(_localVersionFilename);
-    if (!info.exists()) {
+    if (!FileSystem::fileExists(_localVersionFilename)) {
         return false;
     }
 
index 5ac1e63e5d0094aff02ddb47e1a293ac20b141ee..37d83430ae117e801474731851391ad05c8b643c 100644 (file)
@@ -175,17 +175,17 @@ void Folder::checkLocalPath()
 
     _canonicalLocalPath = Utility::trailingSlashPath(_canonicalLocalPath);
 
-    if (fi.isDir() && fi.isReadable()) {
+    if (FileSystem::isDir(_definition.localPath) && FileSystem::isReadable(_definition.localPath)) {
         qCDebug(lcFolder) << "Checked local path ok";
     } else {
         // Check directory again
         if (!FileSystem::fileExists(_definition.localPath, fi)) {
             _syncResult.appendErrorString(tr("Local folder %1 does not exist.").arg(_definition.localPath));
             _syncResult.setStatus(SyncResult::SetupError);
-        } else if (!fi.isDir()) {
+        } else if (!FileSystem::isDir(_definition.localPath)) {
             _syncResult.appendErrorString(tr("%1 should be a folder but is not.").arg(_definition.localPath));
             _syncResult.setStatus(SyncResult::SetupError);
-        } else if (!fi.isReadable()) {
+        } else if (!FileSystem::isReadable(_definition.localPath)) {
             _syncResult.appendErrorString(tr("%1 is not readable.").arg(_definition.localPath));
             _syncResult.setStatus(SyncResult::SetupError);
         }
@@ -1471,8 +1471,9 @@ void Folder::warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QS
     // Note: This assumes we're getting file watcher notifications
     // for folders only on creation and deletion - if we got a notification
     // on content change that would create spurious warnings.
-    QFileInfo fi(_canonicalLocalPath + path);
-    if (!fi.exists())
+    const auto fullPath = _canonicalLocalPath + path;
+    QFileInfo fi(fullPath);
+    if (!FileSystem::fileExists(fullPath))
         return;
 
     bool ok = false;
@@ -1482,7 +1483,7 @@ void Folder::warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QS
     if (!blacklist.contains(path + "/"))
         return;
 
-    const auto message = fi.isDir()
+    const auto message = FileSystem::isDir(fullPath)
         ? tr("The folder %1 was created but was excluded from synchronization previously. "
              "Data inside it will not be synchronized.")
               .arg(fi.filePath())
index a5d68f1b70a3da7d99721c48e8efbe9359911538..46073c24874a0e199e9dcba889204d508b08289c 100644 (file)
@@ -1320,7 +1320,7 @@ QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const Acco
 
         QString path = folder->cleanPath() + '/';
         path += serverPath.mid(folder->remotePathTrailingSlash().length());
-        if (QFile::exists(path)) {
+        if (FileSystem::fileExists(path)) {
             re.append(path);
         }
     }
@@ -1754,19 +1754,19 @@ static QString checkPathValidityRecursive(const QString &path)
 #endif
     const QFileInfo selFile(path);
 
-    if (!selFile.exists()) {
+    if (!FileSystem::fileExists(path)) {
         QString parentPath = selFile.dir().path();
         if (parentPath != path)
             return checkPathValidityRecursive(parentPath);
         return FolderMan::tr("The selected path does not exist!");
     }
 
-    if (!selFile.isDir()) {
+    if (!FileSystem::isDir(path)) {
         return FolderMan::tr("The selected path is not a folder!");
     }
 
     #ifdef Q_OS_WIN
-    if (!selFile.isWritable()) {
+    if (!FileSystem::isWritable(path)) {
         // isWritable() doesn't cover all NTFS permissions
         // try creating and removing a test file, and make sure it is excluded from sync
         if (!Utility::canCreateFileInPath(path)) {
@@ -1774,7 +1774,7 @@ static QString checkPathValidityRecursive(const QString &path)
         }
     }
     #else
-    if (!selFile.isWritable()) {
+    if (!FileSystem::isWritable(path)) {
         return FolderMan::tr("You have no permission to write to the selected folder!");
     }
     #endif
@@ -1787,7 +1787,7 @@ static QString checkPathValidityRecursive(const QString &path)
 static QString canonicalPath(const QString &path)
 {
     QFileInfo selFile(path);
-    if (!selFile.exists()) {
+    if (!FileSystem::fileExists(path)) {
         const auto parentPath = selFile.dir().path();
 
         // It's possible for the parentPath to match the path
@@ -1880,7 +1880,7 @@ QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const Q
     int attempt = 1;
     forever {
         const auto isGood = FolderMan::instance()->checkPathValidityForNewFolder(folder, serverUrl).second.isEmpty() &&
-            (allowExisting == GoodPathStrategy::AllowOverrideExistingPath || !QFileInfo::exists(folder));
+            (allowExisting == GoodPathStrategy::AllowOverrideExistingPath || !FileSystem::fileExists(folder));
         if (isGood) {
             break;
         }
index 237d6454f8539cff7f69586a2aaf33eccbcd002f..fc7d05d685a455919f5a8479cf09501f782cdd3b 100644 (file)
@@ -84,7 +84,7 @@ void FolderWatcher::appendSubPaths(QDir dir, QStringList& subPaths) {
         QString path = dir.path() + "/" + newSubPaths[i];
         QFileInfo fileInfo(path);
         subPaths.append(path);
-        if (fileInfo.isDir()) {
+        if (FileSystem::isDir(path)) {
             QDir dir(path);
             appendSubPaths(dir, subPaths);
         }
@@ -176,9 +176,8 @@ int FolderWatcher::lockChangeDebouncingTimout() const
 
 void FolderWatcher::changeDetected(const QString &path)
 {
-    QFileInfo fileInfo(path);
     QStringList paths(path);
-    if (fileInfo.isDir()) {
+    if (FileSystem::isDir(path)) {
         QDir dir(path);
         appendSubPaths(dir, paths);
     }
index bb3ea49fec5eb215e7ee986383bc17c9a0daf3ac..b89d23808c5db02d1c17c1e7cd1d816b998c89b9 100644 (file)
@@ -152,7 +152,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
             // and new files in a folder, probably because of the folder's mtime
             // changing. We don't need them.
             const bool skip = curEntry->Action == FILE_ACTION_MODIFIED
-                && QFileInfo(longfile).isDir();
+                && FileSystem::isDir(longfile);
 
             if (!skip) {
                 emit changed(longfile);
index 321e43b600d84099f8a9b9a6d116f6b80ca0be37..f55b60f14fc644475ad086f3161b9973f6e5d85b 100644 (file)
@@ -40,6 +40,7 @@
 #include "tray/sortedactivitylistmodel.h"
 #include "tray/syncstatussummary.h"
 #include "tray/unifiedsearchresultslistmodel.h"
+#include "filesystem.h"
 
 #ifdef WITH_LIBCLOUDPROVIDERS
 #include "cloudproviders/cloudprovidermanager.h"
@@ -513,7 +514,7 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo &
         Folder *f = FolderMan::instance()->folder(folder);
         if (f) {
             QString fullPath = f->path() + '/' + progress._lastCompletedItem._file;
-            if (QFile(fullPath).exists()) {
+            if (FileSystem::fileExists(fullPath)) {
                 connect(action, &QAction::triggered, this, [this, fullPath] { this->slotOpenPath(fullPath); });
             } else {
                 action->setEnabled(false);
index e3914c1276199dcfe2f0f85d6a7e00c2b2bb9ff1..b4b5a913a28ce685026708015192b1ce37d4408b 100644 (file)
@@ -1058,8 +1058,8 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
     // If the parent doesn't accept new files, go to the root of the sync folder
     QFileInfo fileInfo(localFile);
     const auto parentRecord = parentDir.journalRecord();
-    if ((fileInfo.isFile() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
-        || (fileInfo.isDir() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
+    if ((FileSystem::isFile(localFile) && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
+        || (FileSystem::isDir(localFile) && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
         defaultDirAndName = QFileInfo(defaultDirAndName).fileName();
     }
 
@@ -1234,7 +1234,7 @@ void SocketApi::sendEncryptFolderCommandMenuEntries(const QFileInfo &fileInfo,
             !fileData.folder->accountState() ||
             !fileData.folder->accountState()->account() ||
             !fileData.folder->accountState()->account()->capabilities().clientSideEncryptionAvailable() ||
-            !fileInfo.isDir() ||
+            !FileSystem::isDir(fileInfo.absoluteFilePath()) ||
             isE2eEncryptedPath) {
         return;
     }
@@ -1262,7 +1262,7 @@ void SocketApi::sendLockFileCommandMenuEntries(const QFileInfo &fileInfo,
                                                const FileData &fileData,
                                                const OCC::SocketListener* const listener) const
 {
-    if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable()) {
+    if (!FileSystem::isDir(fileInfo.absoluteFilePath()) && syncFolder->accountState()->account()->capabilities().filesLockAvailable()) {
         if (syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::UnlockedItem) {
             listener->sendMessage(QLatin1String("MENU_ITEM:LOCK_FILE::") + tr("Lock file"));
         } else {
@@ -1280,7 +1280,7 @@ void SocketApi::sendLockFileInfoMenuEntries(const QFileInfo &fileInfo,
                                             const SyncJournalFileRecord &record) const
 {
     static constexpr auto SECONDS_PER_MINUTE = 60;
-    if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable() &&
+    if (!FileSystem::isDir(fileInfo.absoluteFilePath()) && syncFolder->accountState()->account()->capabilities().filesLockAvailable() &&
             syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::LockedItem) {
         listener->sendMessage(QLatin1String("MENU_ITEM:LOCKED_FILE_OWNER:d:") + tr("Locked by %1").arg(record._lockstate._lockOwnerDisplayName));
         const auto lockExpirationTime = record._lockstate._lockTime + record._lockstate._lockTimeout;
@@ -1381,7 +1381,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
         const QFileInfo fileInfo(fileData.localPath);
         sendLockFileInfoMenuEntries(fileInfo, syncFolder, fileData, listener, record);
 
-        if (!fileInfo.isDir()) {
+        if (!FileSystem::isDir(fileData.localPath)) {
             listener->sendMessage(QLatin1String("MENU_ITEM:ACTIVITY") + flagString + tr("Activity"));
         }
 
index 5246b0f11360e766ccf521d11f2ff1faab75e475..5610fb6a02ff704b2ac202ee80e0979f05e83b3f 100644 (file)
@@ -42,7 +42,7 @@ void SyncRunFileLog::start(const QString &folderPath)
     QString filename = logpath + QLatin1String("/") + filenameSingle + QLatin1String("_sync.log");
 
     int depthIndex = 2;
-    while(QFile::exists(filename)) {
+    while (FileSystem::fileExists(filename)) {
 
         QFile file(filename);
         file.open(QIODevice::ReadOnly| QIODevice::Text);
@@ -65,9 +65,8 @@ void SyncRunFileLog::start(const QString &folderPath)
     }
 
     // When the file is too big, just rename it to an old name.
-    QFileInfo info(filename);
-    bool exists = info.exists();
-    if (exists && info.size() > logfileMaxSize) {
+    bool exists = FileSystem::fileExists(filename);
+    if (exists && FileSystem::getSize(filename) > logfileMaxSize) {
         exists = false;
         QString newFilename = filename + QLatin1String(".1");
         QFile::remove(newFilename);
index 908f760bdd896730c7454270cbd53d8ac27fb5be..a817d2a7ce62eabe6aedf86a7db4eac182f026d0 100644 (file)
@@ -1,5 +1,6 @@
 #include "notificationhandler.h"
 #include "usermodel.h"
+#include "common/filesystembase.h"
 
 #include "accountmanager.h"
 #include "owncloudgui.h"
@@ -576,24 +577,24 @@ void User::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
                 continue;
             }
 
-            if (activity._syncFileItemStatus == SyncFileItem::Conflict && !QFileInfo::exists(f->path() + activity._file)) {
+            if (activity._syncFileItemStatus == SyncFileItem::Conflict && !FileSystem::fileExists(f->path() + activity._file)) {
                 _activityModel->removeActivityFromActivityList(activity);
                 continue;
             }
 
-            if (activity._syncFileItemStatus == SyncFileItem::FileLocked && !QFileInfo::exists(f->path() + activity._file)) {
+            if (activity._syncFileItemStatus == SyncFileItem::FileLocked && !FileSystem::fileExists(f->path() + activity._file)) {
                 _activityModel->removeActivityFromActivityList(activity);
                 continue;
             }
 
 
-            if (activity._syncFileItemStatus == SyncFileItem::FileIgnored && !QFileInfo::exists(f->path() + activity._file)) {
+            if (activity._syncFileItemStatus == SyncFileItem::FileIgnored && !FileSystem::fileExists(f->path() + activity._file)) {
                 _activityModel->removeActivityFromActivityList(activity);
                 continue;
             }
 
 
-            if (!QFileInfo::exists(f->path() + activity._file)) {
+            if (!FileSystem::fileExists(f->path() + activity._file)) {
                 _activityModel->removeActivityFromActivityList(activity);
                 continue;
             }
index 753c2a7c0fc68fef908666849fb1053df8b26743..dc758ba4ed4cf9829c1a284592e1e020265cd94f 100644 (file)
@@ -47,7 +47,7 @@ CaseClashConflictSolver::CaseClashConflictSolver(const QString &targetFilePath,
 #if !defined(QT_NO_DEBUG)
     QFileInfo targetFileInfo(_targetFilePath);
     Q_ASSERT(targetFileInfo.isAbsolute());
-    Q_ASSERT(QFileInfo::exists(_conflictFilePath));
+    Q_ASSERT(FileSystem::fileExists(_conflictFilePath));
 #endif
 }
 
index 23b743a46c6c296907cf87e565c7d70ac4e562e0..22a49f278f376aa672401cae6417c87c47249375 100644 (file)
@@ -606,7 +606,6 @@ void ProcessDirectoryJob::postProcessServerNew(const SyncFileItemPtr &item,
     if (!localEntry.isValid() &&
         item->_type == ItemTypeFile &&
         opts._vfs->mode() != Vfs::Off &&
-        !FileSystem::isLnkFile(item->_file) &&
         _pinState != PinState::AlwaysLocal &&
         !FileSystem::isExcludeFile(item->_file)) {
 
@@ -2165,7 +2164,7 @@ bool ProcessDirectoryJob::isVfsWithSuffix() const
 void ProcessDirectoryJob::computePinState(PinState parentState)
 {
     _pinState = parentState;
-    if (_queryLocal != ParentDontExist && QFileInfo::exists(_discoveryData->_localDir + _currentFolder._local)) {
+    if (_queryLocal != ParentDontExist && FileSystem::fileExists(_discoveryData->_localDir + _currentFolder._local)) {
         if (auto state = _discoveryData->_syncOptions._vfs->pinState(_currentFolder._local)) // ouch! pin local or original?
             _pinState = *state;
     }
index 866eccdef12efcd2ec854bc82c4b2542ff9beafb..cc65bf1363e008ba5a8e0dc25d67ef988f655baf 100644 (file)
@@ -268,7 +268,7 @@ bool FileSystem::removeRecursively(const QString &path, const std::function<void
         bool removeOk = false;
         // The use of isSymLink here is okay:
         // we never want to go into this branch for .lnk files
-        bool isDir = fi.isDir() && !fi.isSymLink() && !FileSystem::isJunction(fi.absoluteFilePath());
+        bool isDir = FileSystem::isDir(fi.absoluteFilePath()) && !FileSystem::isSymLink(fi.absoluteFilePath()) && !FileSystem::isJunction(fi.absoluteFilePath());
         if (isDir) {
             removeOk = removeRecursively(path + QLatin1Char('/') + di.fileName(), onDeleted, errors); // recursive
         } else {
index 7028512fe84b3ce016af48f4868aab8128687b92..b8c4735fe3a25583915d12067e9f3183cf153a1c 100644 (file)
@@ -24,9 +24,7 @@
 
 #include <ctime>
 #include <functional>
-#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
-#include <filesystem>
-#endif
+#include <functional>
 
 class QFile;
 
@@ -96,7 +94,6 @@ namespace FileSystem {
     bool OWNCLOUDSYNC_EXPORT fileChanged(const QString &fileName,
         qint64 previousSize,
         time_t previousMtime);
-
     /**
      * @brief Like !fileChanged() but with verbose logging if the file *did* change.
      */
index 4f41e124ed202b5050c3d3444629b78f0495f96a..d83aedf242128a50f4f9e9e45c9cbea37823a4eb 100644 (file)
@@ -1070,6 +1070,7 @@ Result<Vfs::ConvertToPlaceholderResult, QString> OwncloudPropagator::staticUpdat
     if (!dBresult) {
         return dBresult.error();
     }
+
     const auto result = vfs->convertToPlaceholder(fsPath, item, {}, updateType);
     if (!result) {
         return result.error();
@@ -1460,13 +1461,13 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
                 !_item->_remotePerm.hasPermission(RemotePermissions::CanMove) &&
                 !_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
                 try {
-                    if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
+                    if (FileSystem::fileExists(propagator()->fullLocalPath(_item->_file))) {
                         FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadOnly);
                         qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
                         std::filesystem::permissions(propagator()->fullLocalPath(_item->_file).toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
                         qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
                     }
-                    if (!_item->_renameTarget.isEmpty() && QFileInfo::exists(propagator()->fullLocalPath(_item->_renameTarget))) {
+                    if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(propagator()->fullLocalPath(_item->_renameTarget))) {
                         FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadOnly);
                         qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
                         std::filesystem::permissions(propagator()->fullLocalPath(_item->_renameTarget).toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
@@ -1485,13 +1486,13 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
                         !_item->_remotePerm.hasPermission(RemotePermissions::CanMove) ||
                         !_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
                 try {
-                    if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
+                    if (FileSystem::fileExists(propagator()->fullLocalPath(_item->_file))) {
                         FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadWrite);
                         qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
                         std::filesystem::permissions(propagator()->fullLocalPath(_item->_file).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
                         qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
                     }
-                    if (!_item->_renameTarget.isEmpty() && QFileInfo::exists(propagator()->fullLocalPath(_item->_renameTarget))) {
+                    if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(propagator()->fullLocalPath(_item->_renameTarget))) {
                         FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadWrite);
                         qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
                         std::filesystem::permissions(propagator()->fullLocalPath(_item->_renameTarget).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
index 84240ba6418dd466bdd83feb1c0f6bdea08541fc..a36a11ccd4992db8f1b3fbca63b21d1378e92686 100644 (file)
@@ -1007,7 +1007,7 @@ void PropagateDownloadFile::checksumValidateFailedAbortDownload(const QString &e
 void PropagateDownloadFile::deleteExistingFolder()
 {
     QString existingDir = propagator()->fullLocalPath(_item->_file);
-    if (!QFileInfo(existingDir).isDir()) {
+    if (!FileSystem::isDir(existingDir)) {
         return;
     }
 
@@ -1204,9 +1204,17 @@ void PropagateDownloadFile::downloadFinished()
     if (previousFileExists) {
         // Preserve the existing file permissions.
         const auto existingFile = QFileInfo{filename};
+#ifdef Q_OS_WIN
+        const auto existingPermissions = FileSystem::filePermissionsWin(filename);
+        const auto tmpFilePermissions = FileSystem::filePermissionsWin(_tmpFile.fileName());
+        if (existingPermissions != tmpFilePermissions) {
+            FileSystem::setFilePermissionsWin(_tmpFile.fileName(), existingPermissions);
+        }
+#else
         if (existingFile.permissions() != _tmpFile.permissions()) {
             _tmpFile.setPermissions(existingFile.permissions());
         }
+#endif
         preserveGroupOwnership(_tmpFile.fileName(), existingFile);
 
         // Make the file a hydrated placeholder if possible
@@ -1228,7 +1236,7 @@ void PropagateDownloadFile::downloadFinished()
     }
 
     const auto isConflict = (_item->_instruction == CSYNC_INSTRUCTION_CONFLICT
-                             && (QFileInfo(filename).isDir() || !FileSystem::fileEquals(filename, _tmpFile.fileName()))) ||
+                             && (FileSystem::isDir(filename) || !FileSystem::fileEquals(filename, _tmpFile.fileName()))) ||
         _item->_instruction == CSYNC_INSTRUCTION_CASE_CLASH_CONFLICT;
 
     if (isConflict) {
index 09cb179d385a943ea154c818aada329c180adbc7..401a8f27bf12460d6a3f868a88c8a6e20374f868 100644 (file)
@@ -18,6 +18,7 @@
 #include "account.h"
 #include "common/syncjournalfilerecord.h"
 #include "filesystem.h"
+#include "common/filesystembase.h"
 #include "common/asserts.h"
 #include <QFileInfo>
 #include <QFile>
@@ -253,7 +254,7 @@ void PropagateRemoteMove::finalize()
 
     const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget);
 
-    if (QFileInfo::exists(targetFile)) {
+    if (FileSystem::fileExists(targetFile)) {
         // Delete old db data.
         if (!propagator()->_journal->deleteFileRecord(_item->_originalFile)) {
             qCWarning(lcPropagateRemoteMove) << "could not delete file from local DB" << _item->_originalFile;
@@ -277,7 +278,7 @@ void PropagateRemoteMove::finalize()
         }
     }
 
-    if (!QFileInfo::exists(targetFile)) {
+    if (!FileSystem::fileExists(targetFile)) {
         propagator()->_journal->commit("Remote Rename");
         done(SyncFileItem::Success, {}, ErrorCategory::NoError);
         return;
index c64483ed6f1ed4cbb8c67cca0466542188f30cbd..6999459fbc686b1c156540ab41178ad4c4cf8970 100644 (file)
@@ -4,6 +4,7 @@
 #include "clientsideencryption.h"
 #include "foldermetadata.h"
 #include "encryptedfoldermetadatahandler.h"
+#include "filesystem.h"
 #include "account.h"
 #include <QFileInfo>
 #include <QDir>
@@ -182,7 +183,7 @@ void PropagateUploadEncrypted::slotUploadMetadataFinished(int statusCode, const
     qCDebug(lcPropagateUploadEncrypted) << "Finalizing the upload part, now the actuall uploader will take over";
     emit finalized(Utility::trailingSlashPath(outputInfo.path()) + outputInfo.fileName(),
                    Utility::trailingSlashPath(_remoteParentPath) + outputInfo.fileName(),
-                   outputInfo.size());
+                   FileSystem::getSize(_completeFileName));
 }
 
 } // namespace OCC
\ No newline at end of file
index 4b18a944c7f61965455444d074eb944b3d187048..313d4b334080ff4432b11238caf02492768a65c4 100644 (file)
@@ -163,8 +163,7 @@ void PropagateLocalMkdir::startLocalMkdir()
 
     // When turning something that used to be a file into a directory
     // we need to delete the file first.
-    QFileInfo fi(newDirStr);
-    if (fi.exists() && fi.isFile()) {
+    if (FileSystem::fileExists(newDirStr) && FileSystem::isFile(newDirStr)) {
         if (_deleteExistingFile) {
             QString removeError;
             if (!FileSystem::remove(newDirStr, &removeError)) {
@@ -282,7 +281,7 @@ void PropagateLocalRename::start()
     const auto existingFile = propagator()->fullLocalPath(previousNameInDb);
     const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget);
 
-    const auto fileAlreadyMoved = !QFileInfo::exists(propagator()->fullLocalPath(_item->_originalFile)) && QFileInfo::exists(existingFile);
+    const auto fileAlreadyMoved = !FileSystem::fileExists(propagator()->fullLocalPath(_item->_originalFile)) && FileSystem::fileExists(existingFile);
     auto pinState = OCC::PinState::Unspecified;
     if (!fileAlreadyMoved) {
         auto pinStateResult = vfs->pinState(propagator()->adjustRenamedPath(_item->_file));
@@ -294,7 +293,7 @@ void PropagateLocalRename::start()
     // if the file is a file underneath a moved dir, the _item->file is equal
     // to _item->renameTarget and the file is not moved as a result.
     qCDebug(lcPropagateLocalRename) << _item->_file << _item->_renameTarget << _item->_originalFile << previousNameInDb << (fileAlreadyMoved ? "original file has already moved" : "original file is still there");
-    Q_ASSERT(QFileInfo::exists(propagator()->fullLocalPath(_item->_originalFile)) || QFileInfo::exists(existingFile));
+    Q_ASSERT(FileSystem::fileExists(propagator()->fullLocalPath(_item->_originalFile)) || FileSystem::fileExists(existingFile));
     if (_item->_file != _item->_renameTarget) {
         propagator()->reportProgress(*_item, 0);
         qCDebug(lcPropagateLocalRename) << "MOVE " << existingFile << " => " << targetFile;
index 3470504e2709fb1d0790abca889ccdcc61589d75..3b7b01f86de873e1d23255d2b31ee92d288ed893 100644 (file)
@@ -287,7 +287,7 @@ void SyncEngine::conflictRecordMaintenance()
     const auto conflictRecordPaths = _journal->conflictRecordPaths();
     for (const auto &path : conflictRecordPaths) {
         auto fsPath = _propagator->fullLocalPath(QString::fromUtf8(path));
-        if (!QFileInfo::exists(fsPath)) {
+        if (!FileSystem::fileExists(fsPath)) {
             _journal->deleteConflictRecord(path);
         }
     }
@@ -326,7 +326,7 @@ void SyncEngine::caseClashConflictRecordMaintenance()
     const auto conflictRecordPaths = _journal->caseClashConflictRecordPaths();
     for (const auto &path : conflictRecordPaths) {
         const auto fsPath = _propagator->fullLocalPath(QString::fromUtf8(path));
-        if (!QFileInfo::exists(fsPath)) {
+        if (!FileSystem::fileExists(fsPath)) {
             _journal->deleteCaseClashConflictByPathRecord(path);
         }
     }
@@ -640,7 +640,7 @@ void SyncEngine::startSync()
     _discoveryPhase->_account = _account;
     _discoveryPhase->_excludes = _excludedFiles.data();
     const QString excludeFilePath = _localPath + QStringLiteral(".sync-exclude.lst");
-    if (QFile::exists(excludeFilePath)) {
+    if (FileSystem::fileExists(excludeFilePath)) {
         _discoveryPhase->_excludes->addExcludeFilePath(excludeFilePath);
         _discoveryPhase->_excludes->reloadExcludeFiles();
     }
@@ -1237,7 +1237,7 @@ void SyncEngine::wipeVirtualFiles(const QString &localPath, SyncJournalDb &journ
         // If the local file is a dehydrated placeholder, wipe it too.
         // Otherwise leave it to allow the next sync to have a new-new conflict.
         QString localFile = localPath + rec._path;
-        if (QFile::exists(localFile) && vfs.isDehydratedPlaceholder(localFile)) {
+        if (FileSystem::fileExists(localFile) && vfs.isDehydratedPlaceholder(localFile)) {
             qCDebug(lcEngine) << "Removing local dehydrated placeholder" << rec.path();
             QFile::remove(localFile);
         }
index 00aee1190829fa6aa34fa99bea15ee86c573bf5e..a483d2c066383d3539b1a86e9086ca5eec78d831 100644 (file)
@@ -784,12 +784,11 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
         return {};
     }
 
-    QFileInfo pathFileInfo(path);
-    if (!pathFileInfo.exists()) {
+    if (!FileSystem::fileExists(path)) {
         return {};
     }
 
-    if (pathFileInfo.isDir()) {
+    if (FileSystem::isDir(path)) {
         HANDLE handle = nullptr;
         const qint64 openResult = CfOpenFileWithOplock(path.toStdWString().data(), CF_OPEN_FILE_FLAG_NONE, &handle);
         if (openResult == S_OK) {
@@ -797,7 +796,7 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
         } else {
             qCWarning(lcCfApiWrapper) << "Could not open handle for " << path << " result: " << QString::fromWCharArray(_com_error(openResult).ErrorMessage());
         }
-    } else if (pathFileInfo.isFile()) {
+    } else if (FileSystem::isFile(path)) {
         const auto longpath = OCC::FileSystem::longWinPath(path);
         const auto handle = CreateFile(longpath.toStdWString().data(), 0, 0, nullptr,
                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
index 1b91250222575f898fa97e638da2f62edd306578..414caed1bf08caefabeb9373be8f5037df3763c3 100644 (file)
@@ -21,6 +21,7 @@
 #include "hydrationjob.h"
 #include "syncfileitem.h"
 #include "filesystem.h"
+#include "common/filesystembase.h"
 #include "common/syncjournaldb.h"
 #include "config.h"
 
@@ -48,7 +49,7 @@ bool registerShellExtension()
     // assume CFAPI_SHELL_EXTENSIONS_LIB_NAME is always in the same folder as the main executable
     // assume CFAPI_SHELL_EXTENSIONS_LIB_NAME is always in the same folder as the main executable
     const auto shellExtensionDllPath = QDir::toNativeSeparators(QString(QCoreApplication::applicationDirPath() + QStringLiteral("/") + CFAPI_SHELL_EXTENSIONS_LIB_NAME + QStringLiteral(".dll")));
-    if (!QFileInfo::exists(shellExtensionDllPath)) {
+    if (!OCC::FileSystem::fileExists(shellExtensionDllPath)) {
         Q_ASSERT(false);
         qCWarning(lcCfApi) << "Register CfAPI shell extensions failed. Dll does not exist in " << QCoreApplication::applicationDirPath();
         return false;
@@ -236,16 +237,6 @@ Result<void, QString> VfsCfApi::dehydratePlaceholder(const SyncFileItem &item)
 Result<Vfs::ConvertToPlaceholderResult, QString> VfsCfApi::convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &replacesFile, UpdateMetadataTypes updateType)
 {
     const auto localPath = QDir::toNativeSeparators(filename);
-
-    if (item._type != ItemTypeDirectory && OCC::FileSystem::isLnkFile(filename)) {
-        qCInfo(lcCfApi) << "File \"" << filename << "\" is a Windows shortcut. Not converting it to a placeholder.";
-        const auto pinState = pinStateLocal(localPath);
-        if (!pinState || *pinState != PinState::Excluded) {
-            setPinStateLocal(localPath, PinState::Excluded);
-        }
-        return Vfs::ConvertToPlaceholderResult::Ok;
-    }
-
     const auto replacesPath = QDir::toNativeSeparators(replacesFile);
 
     if (cfapi::findPlaceholderInfo(localPath)) {
@@ -281,8 +272,6 @@ bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData)
     const auto hasReparsePoint = (ffd->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
     const auto hasCloudTag = hasReparsePoint && (ffd->dwReserved0 & ~IO_REPARSE_TAG_CLOUD_MASK) == (IO_REPARSE_TAG_CLOUD & ~IO_REPARSE_TAG_CLOUD_MASK);
 
-    const auto isWindowsShortcut = !isDirectory && FileSystem::isLnkFile(stat->path);
-
     const auto isExcludeFile = !isDirectory && FileSystem::isExcludeFile(stat->path);
 
     stat->is_metadata_missing = !hasCloudTag;
@@ -298,7 +287,7 @@ bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData)
     } else if (isSparseFile && isPinned) {
         stat->type = ItemTypeVirtualFileDownload;
         return true;
-    } else if (!isSparseFile && isUnpinned && !isWindowsShortcut && !isExcludeFile) {
+    } else if (!isSparseFile && isUnpinned && !isExcludeFile) {
         stat->type = ItemTypeVirtualFileDehydration;
         return true;
     } else if (isSparseFile) {
@@ -522,9 +511,10 @@ int VfsCfApi::finalizeHydrationJob(const QString &requestId)
 VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinStates(const QString &folderPath, const Optional<PinState> &basePinState)
 {
     Q_ASSERT(!folderPath.endsWith('/'));
+    const auto fullPath = params().filesystemPath + folderPath;
     QFileInfo info(params().filesystemPath + folderPath);
 
-    if (!info.exists()) {
+    if (!FileSystem::fileExists(fullPath)) {
         return {};
     }
     const auto effectivePin = pinState(folderPath);
@@ -533,7 +523,7 @@ VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinState
                          : (*effectivePin == *basePinState) ? *effectivePin
                          : PinState::Inherited;
 
-    if (info.isDir()) {
+    if (FileSystem::isDir(fullPath)) {
         const auto dirState = HydratationAndPinStates {
             pinResult,
             {}
index 71dd2b9774b37fd7641120de5637c64b147b47c8..59c868b31fb690f73b4359725e1133c71e549593 100644 (file)
@@ -150,8 +150,7 @@ bool VfsSuffix::isDehydratedPlaceholder(const QString &filePath)
 {
     if (!filePath.endsWith(fileSuffix()))
         return false;
-    QFileInfo fi(filePath);
-    return fi.exists() && fi.size() == 1;
+    return FileSystem::fileExists(filePath) && FileSystem::getSize(filePath) == 1;
 }
 
 bool VfsSuffix::statTypeVirtualFile(csync_file_stat_t *stat, void *)
index 25163554601fa3dddb5ea7752cbf73e68ab01645..a3d84cbca8765fecb6b94ca7735ea94c2e1e9c80 100644 (file)
@@ -1363,9 +1363,8 @@ private slots:
         QVERIFY(!localFileLocked.isWritable());
     }
 
-    void testLinkFileDoesNotConvertToPlaceholder()
+    void testLinkFileDownload()
     {
-        // inspired by GH issue #6041
         FakeFolder fakeFolder{FileInfo{}};
         auto vfs = setupVfs(fakeFolder);
 
@@ -1375,8 +1374,11 @@ private slots:
         QVERIFY(fakeFolder.syncOnce());
         ItemCompletedSpy completeSpy(fakeFolder);
         QVERIFY(fakeFolder.syncOnce());
-        QVERIFY(!vfs->pinState("linkfile.lnk").isValid() || vfs->pinState("linkfile.lnk").get() == PinState::Excluded);
+        QVERIFY(vfs->pinState("linkfile.lnk").isValid());
         QVERIFY(itemInstruction(completeSpy, "linkfile.lnk", CSYNC_INSTRUCTION_NONE));
+        triggerDownload(fakeFolder, "linkfile.lnk");
+        QVERIFY(fakeFolder.syncOnce());
+        QVERIFY(itemInstruction(completeSpy, "linkfile.lnk", CSYNC_INSTRUCTION_SYNC));
     }
 
     void testFolderDoesNotUpdatePlaceholderMetadata()