diff --git a/src/csync/vio/csync_vio_local.h b/src/csync/vio/csync_vio_local.h index 04bd057ea4d5e..735dff5040ccb 100644 --- a/src/csync/vio/csync_vio_local.h +++ b/src/csync/vio/csync_vio_local.h @@ -30,8 +30,8 @@ class Vfs; csync_vio_handle_t OCSYNC_EXPORT *csync_vio_local_opendir(const QString &name); int OCSYNC_EXPORT csync_vio_local_closedir(csync_vio_handle_t *dhandle); -std::unique_ptr OCSYNC_EXPORT csync_vio_local_readdir(csync_vio_handle_t *dhandle, OCC::Vfs *vfs); +std::unique_ptr OCSYNC_EXPORT csync_vio_local_readdir(csync_vio_handle_t *dhandle, OCC::Vfs *vfs, bool checkPermissionsValidity); -int OCSYNC_EXPORT csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf); +int OCSYNC_EXPORT csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf, bool checkPermissionsValidity); #endif /* _CSYNC_VIO_LOCAL_H */ diff --git a/src/csync/vio/csync_vio_local_unix.cpp b/src/csync/vio/csync_vio_local_unix.cpp index 8f319a3e4b23f..764dffa25b05d 100644 --- a/src/csync/vio/csync_vio_local_unix.cpp +++ b/src/csync/vio/csync_vio_local_unix.cpp @@ -35,8 +35,8 @@ #include "vio/csync_vio_local.h" #include "common/vfs.h" -#include -#include +#include +#include Q_LOGGING_CATEGORY(lcCSyncVIOLocal, "nextcloud.sync.csync.vio_local", QtInfoMsg) @@ -49,7 +49,7 @@ struct csync_vio_handle_t { QByteArray path; }; -static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf); +static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf, bool checkPermissionsValidity); csync_vio_handle_t *csync_vio_local_opendir(const QString &name) { auto handle = std::make_unique(); @@ -71,7 +71,7 @@ int csync_vio_local_closedir(csync_vio_handle_t *dhandle) { return rc; } -std::unique_ptr csync_vio_local_readdir(csync_vio_handle_t *handle, OCC::Vfs *vfs) { +std::unique_ptr csync_vio_local_readdir(csync_vio_handle_t *handle, OCC::Vfs *vfs, bool checkPermissionsValidity) { struct _tdirent *dirent = nullptr; std::unique_ptr file_stat; @@ -114,7 +114,7 @@ std::unique_ptr csync_vio_local_readdir(csync_vio_handle_t *h if (file_stat->path.isNull()) return file_stat; - if (_csync_vio_local_stat_mb(fullPath.constData(), file_stat.get()) < 0) { + if (_csync_vio_local_stat_mb(fullPath.constData(), file_stat.get(), checkPermissionsValidity) < 0) { // Will get excluded by _csync_detect_update. file_stat->type = ItemTypeSkip; } @@ -131,12 +131,12 @@ std::unique_ptr csync_vio_local_readdir(csync_vio_handle_t *h } -int csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf) +int csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf, bool checkPermissionsValidity) { - return _csync_vio_local_stat_mb(QFile::encodeName(uri).constData(), buf); + return _csync_vio_local_stat_mb(QFile::encodeName(uri).constData(), buf, checkPermissionsValidity); } -static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf) +static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf, bool checkPermissionsValidity) { csync_stat_t sb; @@ -169,7 +169,9 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf buf->inode = sb.st_ino; buf->modtime = sb.st_mtime; buf->size = sb.st_size; - buf->isPermissionsInvalid = (sb.st_mode & S_IWOTH) == S_IWOTH; + if (checkPermissionsValidity) { + buf->isPermissionsInvalid = (sb.st_mode & S_IWOTH) == S_IWOTH; + } return 0; } diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index ad23815a4451a..609bd69437459 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -943,7 +943,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it if (!base.isDirectory()) { csync_file_stat_t buf; - if (csync_vio_local_stat(_discoveryData->_localDir + originalPathAdjusted, &buf)) { + if (csync_vio_local_stat(_discoveryData->_localDir + originalPathAdjusted, &buf, true)) { qCInfo(lcDisco) << "Local file does not exist anymore." << originalPathAdjusted; return; } @@ -2137,7 +2137,7 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery() void ProcessDirectoryJob::startAsyncLocalQuery() { QString localPath = _discoveryData->_localDir + _currentFolder._local; - auto localJob = new DiscoverySingleLocalDirectoryJob(_discoveryData->_account, localPath, _discoveryData->_syncOptions._vfs.data()); + auto localJob = new DiscoverySingleLocalDirectoryJob(_discoveryData->_account, localPath, _discoveryData->_syncOptions._vfs.data(), _discoveryData->_fileSystemReliablePermissions); _discoveryData->_currentlyActiveJobs++; _pendingAsyncJobs++; diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 6cd226f2ee1a5..3c16be2241031 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -284,8 +284,17 @@ void DiscoveryPhase::slotItemDiscovered(const OCC::SyncFileItemPtr &item) } } -DiscoverySingleLocalDirectoryJob::DiscoverySingleLocalDirectoryJob(const AccountPtr &account, const QString &localPath, OCC::Vfs *vfs, QObject *parent) - : QObject(parent), QRunnable(), _localPath(localPath), _account(account), _vfs(vfs) +DiscoverySingleLocalDirectoryJob::DiscoverySingleLocalDirectoryJob(const AccountPtr &account, + const QString &localPath, + OCC::Vfs *vfs, + bool fileSystemReliablePermissions, + QObject *parent) + : QObject{parent} + , QRunnable{} + , _localPath{localPath} + , _account{account} + , _vfs{vfs} + , _fileSystemReliablePermissions{fileSystemReliablePermissions} { qRegisterMetaType >("QVector"); } @@ -318,7 +327,7 @@ void DiscoverySingleLocalDirectoryJob::run() { QVector results; while (true) { errno = 0; - auto dirent = csync_vio_local_readdir(dh, _vfs); + auto dirent = csync_vio_local_readdir(dh, _vfs, _fileSystemReliablePermissions); if (!dirent) break; if (dirent->type == ItemTypeSkip) diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index bb932f568b4db..cd43b915166c9 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -119,7 +119,11 @@ class DiscoverySingleLocalDirectoryJob : public QObject, public QRunnable { Q_OBJECT public: - explicit DiscoverySingleLocalDirectoryJob(const AccountPtr &account, const QString &localPath, OCC::Vfs *vfs, QObject *parent = nullptr); + explicit DiscoverySingleLocalDirectoryJob(const AccountPtr &account, + const QString &localPath, + OCC::Vfs *vfs, + bool fileSystemReliablePermissions, + QObject *parent = nullptr); void run() override; signals: @@ -134,6 +138,7 @@ private slots: QString _localPath; AccountPtr _account; OCC::Vfs* _vfs; + bool _fileSystemReliablePermissions = false; public: }; @@ -343,6 +348,8 @@ class DiscoveryPhase : public QObject bool _noCaseConflictRecordsInDb = false; + bool _fileSystemReliablePermissions = false; + QSet _topLevelE2eeFolderPaths; signals: diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 2f288b85fe5bd..e6d94fc5489bc 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -184,7 +184,7 @@ time_t FileSystem::getModTime(const QString &filename) { csync_file_stat_t stat; time_t result = -1; - if (csync_vio_local_stat(filename, &stat) != -1 && (stat.modtime != 0)) { + if (csync_vio_local_stat(filename, &stat, true) != -1 && (stat.modtime != 0)) { result = stat.modtime; } else { result = Utility::qDateTimeToTime_t(QFileInfo(filename).lastModified()); @@ -319,7 +319,7 @@ bool FileSystem::removeRecursively(const QString &path, const std::function #include #include +#include #include namespace OCC { @@ -638,6 +639,19 @@ void SyncEngine::startSync() _remnantReadOnlyFolders.clear(); _discoveryPhase = std::make_unique(); + +#if defined Q_OS_LINUX + const auto fileSystemInfo = QStorageInfo{_localPath}; + qCInfo(lcEngine()) << "File system type for current sync folder:" << fileSystemInfo.fileSystemType(); + if (fileSystemInfo.fileSystemType() == "NTFS") { + _discoveryPhase->_fileSystemReliablePermissions = false; + } else { + _discoveryPhase->_fileSystemReliablePermissions = true; + } +#else + _discoveryPhase->_fileSystemReliablePermissions = true; +#endif + _discoveryPhase->_leadingAndTrailingSpacesFilesAllowed = _leadingAndTrailingSpacesFilesAllowed; _discoveryPhase->_account = _account; _discoveryPhase->_excludes = _excludedFiles.data(); diff --git a/test/csync/vio_tests/check_vio_ext.cpp b/test/csync/vio_tests/check_vio_ext.cpp index 7542271216f6c..2295c3b9f352b 100644 --- a/test/csync/vio_tests/check_vio_ext.cpp +++ b/test/csync/vio_tests/check_vio_ext.cpp @@ -147,7 +147,7 @@ static void create_dirs( const char *path ) * whole tree. * */ -static void traverse_dir(void **state, const QString &dir, int *cnt) +static void traverse_dir(void **state, const QString &dir, int *cnt, bool checkPermissionsValidity) { csync_vio_handle_t *dh = nullptr; std::unique_ptr dirent; @@ -161,7 +161,7 @@ static void traverse_dir(void **state, const QString &dir, int *cnt) assert_non_null(dh); OCC::Vfs *vfs = nullptr; - while( (dirent = csync_vio_local_readdir(dh, vfs)) ) { + while( (dirent = csync_vio_local_readdir(dh, vfs, checkPermissionsValidity)) ) { assert_non_null(dirent.get()); if (!dirent->original_path.isEmpty()) { sv->ignored_dir = dirent->original_path; @@ -190,7 +190,7 @@ static void traverse_dir(void **state, const QString &dir, int *cnt) } output(subdir_out.constData()); if( is_dir ) { - traverse_dir(state, QString::fromUtf8(subdir), cnt); + traverse_dir(state, QString::fromUtf8(subdir), cnt, checkPermissionsValidity); } } @@ -210,11 +210,12 @@ static void check_readdir_shorttree(void **state) { auto sv = (statevar*) *state; + bool checkPermissionsValidity = true; const char *t1 = "alibaba/und/die/vierzig/räuber/"; create_dirs( t1 ); int files_cnt = 0; - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt, checkPermissionsValidity); assert_string_equal(sv->result.constData(), QString::fromUtf8(" %1/alibaba" @@ -233,6 +234,7 @@ static void check_readdir_with_content(void **state) auto sv = (statevar*) *state; int files_cnt = 0; + bool checkPermissionsValidity = true; const char *t1 = "warum/nur/40/Räuber/"; create_dirs( t1 ); @@ -240,7 +242,7 @@ static void check_readdir_with_content(void **state) create_file( t1, "пя́тница.txt", "Am Freitag tanzt der Ürk"); - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt, checkPermissionsValidity); assert_string_equal(sv->result.constData(), QString::fromUtf8(" %1/warum" @@ -259,6 +261,8 @@ static void check_readdir_longtree(void **state) { auto sv = (statevar*) *state; + bool checkPermissionsValidity = true; + /* Strange things here: Compilers only support strings with length of 4k max. * The expected result string is longer, so it needs to be split up in r1, r2 and r3 */ @@ -318,7 +322,7 @@ static void check_readdir_longtree(void **state) /* assemble the result string ... */ const auto result = (r1 + r2 + r3).toUtf8(); int files_cnt = 0; - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt, checkPermissionsValidity); assert_int_equal(files_cnt, 0); /* and compare. */ assert_string_equal(sv->result.constData(), result.constData()); @@ -333,6 +337,7 @@ static void check_readdir_bigunicode(void **state) // 3: ? ASCII: 191 - BF // 4: ASCII: 32 - 20 + bool checkPermissionsValidity = true; QString p = QStringLiteral("%1/%2").arg(CSYNC_TEST_DIR, QStringLiteral("goodone/")); int rc = oc_mkdir(p); assert_int_equal(rc, 0); @@ -344,7 +349,7 @@ static void check_readdir_bigunicode(void **state) assert_int_equal(rc, 0); int files_cnt = 0; - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt, checkPermissionsValidity); const auto expected_result = QStringLiteral(" %1/goodone" " %1/goodone/ugly\xEF\xBB\xBF\x32.txt") .arg(CSYNC_TEST_DIR); diff --git a/test/testlongpath.cpp b/test/testlongpath.cpp index 442d3354a0b6a..a454db9cacde7 100644 --- a/test/testlongpath.cpp +++ b/test/testlongpath.cpp @@ -139,7 +139,7 @@ private Q_SLOTS: file.close(); csync_file_stat_t buf; - QVERIFY(csync_vio_local_stat(longPath.filePath(), &buf) != -1); + QVERIFY(csync_vio_local_stat(longPath.filePath(), &buf, true) != -1); QVERIFY(buf.size == data.size()); QVERIFY(buf.size == longPath.size());