From b619ba5531285a1ca0e6ad2268354b71cbf1c097 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Nov 2023 16:13:34 +0100 Subject: [PATCH] gui/config: Add symlink synchronization as sync option This commit reverts the effects of commit 47be8fd808c0355fd0d98112517e9c5250d58cc7. --- src/gui/folder.cpp | 1 + src/gui/generalsettings.cpp | 4 ++++ src/gui/generalsettings.ui | 11 +++++++++++ src/libsync/configfile.cpp | 12 ++++++++++++ src/libsync/configfile.h | 3 +++ src/libsync/discovery.cpp | 20 +++++++++++++------- src/libsync/discovery.h | 7 ++++--- src/libsync/syncengine.cpp | 2 ++ src/libsync/syncoptions.h | 3 +++ 9 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 21f3423460326..7bbf2e67e0838 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1096,6 +1096,7 @@ SyncOptions Folder::initializeSyncOptions() const auto newFolderLimit = cfgFile.newBigFolderSizeLimit(); opt._newBigFolderSizeLimit = newFolderLimit.first ? newFolderLimit.second * 1000LL * 1000LL : -1; // convert from MB to B opt._confirmExternalStorage = cfgFile.confirmExternalStorage(); + opt._synchronizeSymlinks = cfgFile.synchronizeSymlinks(); opt._moveFilesToTrash = cfgFile.moveToTrash(); opt._vfs = _vfs; opt._parallelNetworkJobs = _accountState->account()->isHttp2Supported() ? 20 : 6; diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 114aab7cd9dd8..00ed6f6e50393 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -191,6 +191,7 @@ GeneralSettings::GeneralSettings(QWidget *parent) connect(_ui->existingFolderLimitCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->stopExistingFolderNowBigSyncCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newExternalStorage, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); + connect(_ui->synchronizeSymlinks, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->moveFilesToTrashCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); #ifndef WITH_CRASHREPORTER @@ -259,6 +260,7 @@ void GeneralSettings::loadMiscSettings() _ui->showInExplorerNavigationPaneCheckBox->setChecked(cfgFile.showInExplorerNavigationPane()); _ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter()); _ui->newExternalStorage->setChecked(cfgFile.confirmExternalStorage()); + _ui->synchronizeSymlinks->setChecked(cfgFile.synchronizeSymlinks()); _ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons()); _ui->moveFilesToTrashCheckBox->setChecked(cfgFile.moveToTrash()); @@ -270,6 +272,7 @@ void GeneralSettings::loadMiscSettings() _ui->stopExistingFolderNowBigSyncCheckBox->setEnabled(_ui->existingFolderLimitCheckBox->isChecked()); _ui->stopExistingFolderNowBigSyncCheckBox->setChecked(_ui->existingFolderLimitCheckBox->isChecked() && cfgFile.stopSyncingExistingFoldersOverLimit()); _ui->newExternalStorage->setChecked(cfgFile.confirmExternalStorage()); + _ui->synchronizeSymlinks->setChecked(cfgFile.synchronizeSymlinks()); _ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons()); } @@ -447,6 +450,7 @@ void GeneralSettings::saveMiscSettings() cfgFile.setMoveToTrash(_ui->moveFilesToTrashCheckBox->isChecked()); cfgFile.setNewBigFolderSizeLimit(newFolderLimitEnabled, _ui->newFolderLimitSpinBox->value()); cfgFile.setConfirmExternalStorage(_ui->newExternalStorage->isChecked()); + cfgFile.setSynchronizeSymlinks(_ui->synchronizeSymlinks->isChecked()); cfgFile.setNotifyExistingFoldersOverLimit(existingFolderLimitEnabled); cfgFile.setStopSyncingExistingFoldersOverLimit(stopSyncingExistingFoldersOverLimit); diff --git a/src/gui/generalsettings.ui b/src/gui/generalsettings.ui index 0bbb5483dc6dc..4f4d60abfe16c 100644 --- a/src/gui/generalsettings.ui +++ b/src/gui/generalsettings.ui @@ -139,6 +139,17 @@ + + + + + + [experimental] Synchronize symlinks + + + + + diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 60cb2cc71aeba..d275abaaffa23 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -102,6 +102,7 @@ static constexpr char useNewBigFolderSizeLimitC[] = "useNewBigFolderSizeLimit"; static constexpr char notifyExistingFoldersOverLimitC[] = "notifyExistingFoldersOverLimit"; static constexpr char stopSyncingExistingFoldersOverLimitC[] = "stopSyncingExistingFoldersOverLimit"; static constexpr char confirmExternalStorageC[] = "confirmExternalStorage"; +static constexpr char synchronizeSymlinksC[] = "synchronizeSymlinks"; static constexpr char moveToTrashC[] = "moveToTrash"; static constexpr char certPath[] = "http_certificatePath"; @@ -947,6 +948,12 @@ bool ConfigFile::confirmExternalStorage() const return getPolicySetting(QLatin1String(confirmExternalStorageC), fallback).toBool(); } +bool ConfigFile::synchronizeSymlinks() const +{ + const auto fallback = getValue(synchronizeSymlinksC, QString(), false); + return getPolicySetting(QLatin1String(synchronizeSymlinksC), fallback).toBool(); +} + bool ConfigFile::useNewBigFolderSizeLimit() const { const auto fallback = getValue(useNewBigFolderSizeLimitC, QString(), true); @@ -981,6 +988,11 @@ void ConfigFile::setConfirmExternalStorage(bool isChecked) setValue(confirmExternalStorageC, isChecked); } +void ConfigFile::setSynchronizeSymlinks(bool isChecked) +{ + setValue(synchronizeSymlinksC, isChecked); +} + bool ConfigFile::moveToTrash() const { return getValue(moveToTrashC, QString(), false).toBool(); diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index 21e58412afcc5..fa193d406e143 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -149,6 +149,9 @@ class OWNCLOUDSYNC_EXPORT ConfigFile [[nodiscard]] bool confirmExternalStorage() const; void setConfirmExternalStorage(bool); + [[nodiscard]] bool synchronizeSymlinks() const; + void setSynchronizeSymlinks(bool); + /** If we should move the files deleted on the server in the trash */ [[nodiscard]] bool moveToTrash() const; void setMoveToTrash(bool); diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index cc32053263f4d..f223ff641d18b 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -39,16 +39,17 @@ namespace OCC { Q_LOGGING_CATEGORY(lcDisco, "nextcloud.sync.discovery", QtInfoMsg) -ProcessDirectoryJob::ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, qint64 lastSyncTimestamp, QObject *parent) +ProcessDirectoryJob::ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, qint64 lastSyncTimestamp, bool synchronizeSymlinks, QObject *parent) : QObject(parent) , _lastSyncTimestamp(lastSyncTimestamp) , _discoveryData(data) + , _synchronizeSymlinks(synchronizeSymlinks) { qCDebug(lcDisco) << data; computePinState(basePinState); } -ProcessDirectoryJob::ProcessDirectoryJob(const PathTuple &path, const SyncFileItemPtr &dirItem, QueryMode queryLocal, QueryMode queryServer, qint64 lastSyncTimestamp, ProcessDirectoryJob *parent) +ProcessDirectoryJob::ProcessDirectoryJob(const PathTuple &path, const SyncFileItemPtr &dirItem, QueryMode queryLocal, QueryMode queryServer, qint64 lastSyncTimestamp, bool synchronizeSymlinks, ProcessDirectoryJob *parent) : QObject(parent) , _dirItem(dirItem) , _lastSyncTimestamp(lastSyncTimestamp) @@ -56,18 +57,20 @@ ProcessDirectoryJob::ProcessDirectoryJob(const PathTuple &path, const SyncFileIt , _queryLocal(queryLocal) , _discoveryData(parent->_discoveryData) , _currentFolder(path) + , _synchronizeSymlinks(synchronizeSymlinks) { qCDebug(lcDisco) << path._server << queryServer << path._local << queryLocal << lastSyncTimestamp; computePinState(parent->_pinState); } -ProcessDirectoryJob::ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, const PathTuple &path, const SyncFileItemPtr &dirItem, QueryMode queryLocal, qint64 lastSyncTimestamp, QObject *parent) +ProcessDirectoryJob::ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, const PathTuple &path, const SyncFileItemPtr &dirItem, QueryMode queryLocal, qint64 lastSyncTimestamp, bool synchronizeSymlinks, QObject *parent) : QObject(parent) , _dirItem(dirItem) , _lastSyncTimestamp(lastSyncTimestamp) , _queryLocal(queryLocal) , _discoveryData(data) , _currentFolder(path) + , _synchronizeSymlinks(synchronizeSymlinks) { computePinState(basePinState); } @@ -306,7 +309,9 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, const Entries &ent } } - if (excluded == CSYNC_NOT_EXCLUDED) { + bool isSymlink = entries.localEntry.isSymLink || entries.serverEntry.isSymLink; + // All not excluded files and additionally all symlinks if symlink synchronization is disabled + if (excluded == CSYNC_NOT_EXCLUDED && !(_synchronizeSymlinks && isSymlink)) { return false; } else if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED || excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) { emit _discoveryData->silentlyExcluded(path); @@ -326,7 +331,8 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, const Entries &ent return true; } - if (entries.localEntry.isSymLink) { + if (entries.localEntry.isSymLink && !_synchronizeSymlinks) { + item->_errorString = tr("Symbolic links are not supported in syncing."); } else { switch (excluded) { case CSYNC_NOT_EXCLUDED: @@ -1649,7 +1655,7 @@ void ProcessDirectoryJob::processFileFinalize( } if (recurse) { auto job = new ProcessDirectoryJob(path, item, recurseQueryLocal, recurseQueryServer, - _lastSyncTimestamp, this); + _lastSyncTimestamp, _synchronizeSymlinks, this); job->setInsideEncryptedTree(isInsideEncryptedTree() || item->isEncrypted()); if (removed) { job->setParent(_discoveryData); @@ -1693,7 +1699,7 @@ void ProcessDirectoryJob::processBlacklisted(const PathTuple &path, const OCC::L qCInfo(lcDisco) << "Discovered (blacklisted) " << item->_file << item->_instruction << item->_direction << item->isDirectory(); if (item->isDirectory() && item->_instruction != CSYNC_INSTRUCTION_IGNORE) { - auto job = new ProcessDirectoryJob(path, item, NormalQuery, InBlackList, _lastSyncTimestamp, this); + auto job = new ProcessDirectoryJob(path, item, NormalQuery, InBlackList, _lastSyncTimestamp, _synchronizeSymlinks, this); connect(job, &ProcessDirectoryJob::finished, this, &ProcessDirectoryJob::subJobFinished); _queuedJobs.push_back(job); } else { diff --git a/src/libsync/discovery.h b/src/libsync/discovery.h index eaa2657697a91..78116bda6fb13 100644 --- a/src/libsync/discovery.h +++ b/src/libsync/discovery.h @@ -101,15 +101,15 @@ class ProcessDirectoryJob : public QObject * The base pin state is used if the root dir's pin state can't be retrieved. */ explicit ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, - qint64 lastSyncTimestamp, QObject *parent); + qint64 lastSyncTimestamp, bool synchronizeSymlinks, QObject *parent); /// For creating subjobs explicit ProcessDirectoryJob(const PathTuple &path, const SyncFileItemPtr &dirItem, - QueryMode queryLocal, QueryMode queryServer, qint64 lastSyncTimestamp, + QueryMode queryLocal, QueryMode queryServer, qint64 lastSyncTimestamp, bool synchronizeSymlinks, ProcessDirectoryJob *parent); explicit ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, const PathTuple &path, const SyncFileItemPtr &dirItem, - QueryMode queryLocal, qint64 lastSyncTimestamp, QObject *parent); + QueryMode queryLocal, qint64 lastSyncTimestamp, bool synchronizeSymlinks, QObject *parent); void start(); /** Start up to nbJobs, return the number of job started; emit finished() when done */ @@ -296,6 +296,7 @@ class ProcessDirectoryJob : public QObject bool _childIgnored = false; // The directory contains ignored item that would prevent deletion PinState _pinState = PinState::Unspecified; // The directory's pin-state, see computePinState() bool _isInsideEncryptedTree = false; // this directory is encrypted or is within the tree of directories with root directory encrypted + const bool _synchronizeSymlinks = false; signals: void finished(); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 07befe1844c93..aa2c22f4c73fb 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -715,6 +715,7 @@ void SyncEngine::startSync() singleItemDiscoveryOptions().discoveryDirItem, localQueryMode, _journal->keyValueStoreGetInt("last_sync", 0), + _syncOptions._synchronizeSymlinks, _discoveryPhase.data() ); } else { @@ -722,6 +723,7 @@ void SyncEngine::startSync() _discoveryPhase.data(), PinState::AlwaysLocal, _journal->keyValueStoreGetInt("last_sync", 0), + _syncOptions._synchronizeSymlinks, _discoveryPhase.data() ); } diff --git a/src/libsync/syncoptions.h b/src/libsync/syncoptions.h index 092ac6fb6f1c6..d3e91b79c4340 100644 --- a/src/libsync/syncoptions.h +++ b/src/libsync/syncoptions.h @@ -42,6 +42,9 @@ class OWNCLOUDSYNC_EXPORT SyncOptions /** If a confirmation should be asked for external storages */ bool _confirmExternalStorage = false; + /** If symlinks should be synchronized to the server as symlinks */ + bool _synchronizeSymlinks = false; + /** If remotely deleted files are needed to move to trash */ bool _moveFilesToTrash = false;