Merge pull request #7061 from nextcloud/bugfix/deleteReadOnlyRemnantsFolders

Bugfix/delete read only remnants folders
This commit is contained in:
Matthieu Gallien 2024-09-12 09:42:46 +02:00 committed by GitHub
commit 341263b97e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 316 additions and 115 deletions

View File

@ -101,6 +101,8 @@ Folder::Folder(const FolderDefinition &definition,
connect(_engine.data(), &SyncEngine::aboutToRemoveAllFiles,
this, &Folder::slotAboutToRemoveAllFiles);
connect(_engine.data(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
this, &Folder::slotNeedToRemoveRemnantsReadOnlyFolders);
connect(_engine.data(), &SyncEngine::transmissionProgress, this, &Folder::slotTransmissionProgress);
connect(_engine.data(), &SyncEngine::itemCompleted,
this, &Folder::slotItemCompleted);
@ -1664,6 +1666,27 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, std::functio
msgBox->open();
}
void Folder::slotNeedToRemoveRemnantsReadOnlyFolders(const QList<SyncFileItemPtr> &folders,
const QString &localPath,
std::function<void (bool)> callback)
{
auto listOfFolders = QStringList{};
for (const auto &oneFolder : folders) {
listOfFolders.push_back(oneFolder->_file);
}
qCInfo(lcFolder()) << "will delete invalid read-only folders:" << listOfFolders.join(", ");
setSyncPaused(true);
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(true);
setSyncPaused(false);
_lastEtag.clear();
slotScheduleThisFolder();
}
void Folder::removeLocalE2eFiles()
{
qCDebug(lcFolder) << "Removing local E2EE files";

View File

@ -335,6 +335,10 @@ public slots:
// connected to the corresponding signals in the SyncEngine
void slotAboutToRemoveAllFiles(OCC::SyncFileItem::Direction, std::function<void(bool)> callback);
void slotNeedToRemoveRemnantsReadOnlyFolders(const QList<SyncFileItemPtr> &folders,
const QString &localPath,
std::function<void(bool)> callback);
/**
* Starts a sync operation
*

View File

@ -1832,12 +1832,14 @@ bool ProcessDirectoryJob::checkPermissions(const OCC::SyncFileItemPtr &item)
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
qCWarning(lcDisco) << "unexpected new folder in a read-only folder will be made read-write" << localPath;
FileSystem::setFolderPermissions(localPath, FileSystem::FolderPermissions::ReadWrite);
emit _discoveryData->remnantReadOnlyFolderDiscovered(item);
#endif
return false;
} else if (!item->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddFile)) {
qCWarning(lcDisco) << "checkForPermission: ERROR" << item->_file;
item->_instruction = CSYNC_INSTRUCTION_ERROR;
item->_errorString = tr("Not allowed because you don't have permission to add files in that folder");
emit _discoveryData->remnantReadOnlyFolderDiscovered(item);
return false;
}
break;
@ -2038,6 +2040,7 @@ int ProcessDirectoryJob::processSubJobs(int nbJobs)
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
qCWarning(lcDisco) << "unexpected new folder in a read-only folder will be made read-write" << localPath;
FileSystem::setFolderPermissions(localPath, FileSystem::FolderPermissions::ReadWrite);
emit _discoveryData->remnantReadOnlyFolderDiscovered(_dirItem);
#endif
}

View File

@ -357,6 +357,7 @@ signals:
void addErrorToGui(const SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category);
void remnantReadOnlyFolderDiscovered(const OCC::SyncFileItemPtr &item);
private slots:
void slotItemDiscovered(const OCC::SyncFileItemPtr &item);
};

View File

@ -273,6 +273,12 @@ bool FileSystem::removeRecursively(const QString &path, const std::function<void
removeOk = removeRecursively(path + QLatin1Char('/') + di.fileName(), onDeleted, errors); // recursive
} else {
QString removeError;
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
const auto fileInfo = QFileInfo{di.filePath()};
const auto parentFolderPath = fileInfo.dir().absolutePath();
const auto parentPermissionsHandler = FileSystem::FilePermissionsRestore{parentFolderPath, FileSystem::FolderPermissions::ReadWrite};
#endif
removeOk = FileSystem::remove(di.filePath(), &removeError);
if (removeOk) {
if (onDeleted)
@ -289,6 +295,12 @@ bool FileSystem::removeRecursively(const QString &path, const std::function<void
allRemoved = false;
}
if (allRemoved) {
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
const auto fileInfo = QFileInfo{path};
const auto parentFolderPath = fileInfo.dir().absolutePath();
const auto parentPermissionsHandler = FileSystem::FilePermissionsRestore{parentFolderPath, FileSystem::FolderPermissions::ReadWrite};
FileSystem::setFolderPermissions(path, FileSystem::FolderPermissions::ReadWrite);
#endif
allRemoved = QDir().rmdir(path);
if (allRemoved) {
if (onDeleted)

View File

@ -630,6 +630,8 @@ void SyncEngine::startSync()
_progressInfo->_status = ProgressInfo::Discovery;
emit transmissionProgress(*_progressInfo);
_remnantReadOnlyFolders.clear();
_discoveryPhase.reset(new DiscoveryPhase);
_discoveryPhase->_leadingAndTrailingSpacesFilesAllowed = _leadingAndTrailingSpacesFilesAllowed;
_discoveryPhase->_account = _account;
@ -683,6 +685,7 @@ void SyncEngine::startSync()
connect(_discoveryPhase.data(), &DiscoveryPhase::finished, this, &SyncEngine::slotDiscoveryFinished);
connect(_discoveryPhase.data(), &DiscoveryPhase::silentlyExcluded,
_syncFileStatusTracker.data(), &SyncFileStatusTracker::slotAddSilentlyExcluded);
connect(_discoveryPhase.data(), &DiscoveryPhase::remnantReadOnlyFolderDiscovered, this, &SyncEngine::remnantReadOnlyFolderDiscovered);
ProcessDirectoryJob *discoveryJob = nullptr;
@ -798,102 +801,6 @@ void SyncEngine::slotDiscoveryFinished()
_progressInfo->_status = ProgressInfo::Reconcile;
emit transmissionProgress(*_progressInfo);
// qCInfo(lcEngine) << "Permissions of the root folder: " << _csync_ctx->remote.root_perms.toString();
auto finish = [this]{
auto databaseFingerprint = _journal->dataFingerprint();
// If databaseFingerprint is empty, this means that there was no information in the database
// (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint)
if (!databaseFingerprint.isEmpty() && _discoveryPhase
&& _discoveryPhase->_dataFingerprint != databaseFingerprint) {
qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint;
restoreOldFiles(_syncItems);
}
if (_discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) {
slotScheduleFilesDelayedSync();
} else if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) {
_anotherSyncNeeded = ImmediateFollowUp;
}
if (!_discoveryPhase->_filesUnscheduleSync.empty()) {
slotUnscheduleFilesDelayedSync();
}
if (_discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) {
for (const auto &item : qAsConst(_syncItems)) {
if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) {
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
}
}
_anotherSyncNeeded = ImmediateFollowUp;
}
Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end()));
qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms";
_localDiscoveryPaths.clear();
// To announce the beginning of the sync
emit aboutToPropagate(_syncItems);
qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms";
// it's important to do this before ProgressInfo::start(), to announce start of new sync
_progressInfo->_status = ProgressInfo::Propagation;
emit transmissionProgress(*_progressInfo);
_progressInfo->startEstimateUpdates();
// post update phase script: allow to tweak stuff by a custom script in debug mode.
if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) {
#ifndef NDEBUG
const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT");
qCDebug(lcEngine) << "Post Update Script: " << script;
auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
if (scriptArgs.size() > 0) {
const auto scriptExecutable = scriptArgs.takeFirst();
QProcess::execute(scriptExecutable, scriptArgs);
}
#else
qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
#endif
}
// do a database commit
_journal->commit(QStringLiteral("post treewalk"));
_propagator = QSharedPointer<OwncloudPropagator>(
new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList));
_propagator->setSyncOptions(_syncOptions);
connect(_propagator.data(), &OwncloudPropagator::itemCompleted,
this, &SyncEngine::slotItemCompleted);
connect(_propagator.data(), &OwncloudPropagator::progress,
this, &SyncEngine::slotProgress);
connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection);
connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile);
connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile);
connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage);
connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage);
connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem);
// apply the network limits to the propagator
setNetworkLimits(_uploadLimit, _downloadLimit);
deleteStaleDownloadInfos(_syncItems);
deleteStaleUploadInfos(_syncItems);
deleteStaleErrorBlacklistEntries(_syncItems);
_journal->commit(QStringLiteral("post stale entry removal"));
// Emit the started signal only after the propagator has been set up.
if (_needsUpdate)
Q_EMIT started();
_propagator->start(std::move(_syncItems));
qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms";
};
const auto displayDialog = ConfigFile().promptDeleteFiles() && !_syncOptions.isCmd();
if (!_hasNoneFiles && _hasRemoveFile && displayDialog) {
qCInfo(lcEngine) << "All the files are going to be changed, asking the user";
@ -904,27 +811,18 @@ void SyncEngine::slotDiscoveryFinished()
}
}
QPointer<QObject> guard = new QObject();
QPointer<QObject> self = this;
auto callback = [this, self, finish, guard](bool cancel) -> void {
// use a guard to ensure its only called once...
// qpointer to self to ensure we still exist
if (!guard || !self) {
return;
}
guard->deleteLater();
if (cancel) {
qCInfo(lcEngine) << "User aborted sync";
finalize(false);
return;
} else {
finish();
}
};
emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback);
promptUserBeforePropagation([this, side](auto &&callback){
emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback);
});
return;
}
finish();
if (!_remnantReadOnlyFolders.isEmpty()) {
handleRemnantReadOnlyFolders();
return;
}
finishSync();
}
void SyncEngine::slotCleanPollsJobAborted(const QString &error, const ErrorCategory errorCategory)
@ -1101,6 +999,138 @@ void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems)
}
}
void SyncEngine::cancelSyncOrContinue(bool cancel)
{
if (cancel) {
qCInfo(lcEngine) << "User aborted sync";
finalize(false);
} else {
finishSync();
}
}
void SyncEngine::finishSync()
{
auto databaseFingerprint = _journal->dataFingerprint();
// If databaseFingerprint is empty, this means that there was no information in the database
// (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint)
if (!databaseFingerprint.isEmpty() && _discoveryPhase
&& _discoveryPhase->_dataFingerprint != databaseFingerprint) {
qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint;
restoreOldFiles(_syncItems);
}
if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) {
slotScheduleFilesDelayedSync();
} else if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) {
_anotherSyncNeeded = ImmediateFollowUp;
}
if (_discoveryPhase && !_discoveryPhase->_filesUnscheduleSync.empty()) {
slotUnscheduleFilesDelayedSync();
}
if (_discoveryPhase && _discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) {
for (const auto &item : qAsConst(_syncItems)) {
if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) {
// item->_instruction = CSYNC_INSTRUCTION_IGNORE;
}
}
_anotherSyncNeeded = ImmediateFollowUp;
}
Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end()));
qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms";
_localDiscoveryPaths.clear();
// To announce the beginning of the sync
emit aboutToPropagate(_syncItems);
qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms";
// it's important to do this before ProgressInfo::start(), to announce start of new sync
_progressInfo->_status = ProgressInfo::Propagation;
emit transmissionProgress(*_progressInfo);
_progressInfo->startEstimateUpdates();
// post update phase script: allow to tweak stuff by a custom script in debug mode.
if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) {
#ifndef NDEBUG
const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT");
qCDebug(lcEngine) << "Post Update Script: " << script;
auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
if (scriptArgs.size() > 0) {
const auto scriptExecutable = scriptArgs.takeFirst();
QProcess::execute(scriptExecutable, scriptArgs);
}
#else
qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
#endif
}
// do a database commit
_journal->commit(QStringLiteral("post treewalk"));
_propagator = QSharedPointer<OwncloudPropagator>(
new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList));
_propagator->setSyncOptions(_syncOptions);
connect(_propagator.data(), &OwncloudPropagator::itemCompleted,
this, &SyncEngine::slotItemCompleted);
connect(_propagator.data(), &OwncloudPropagator::progress,
this, &SyncEngine::slotProgress);
connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection);
connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile);
connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile);
connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage);
connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage);
connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem);
// apply the network limits to the propagator
setNetworkLimits(_uploadLimit, _downloadLimit);
deleteStaleDownloadInfos(_syncItems);
deleteStaleUploadInfos(_syncItems);
deleteStaleErrorBlacklistEntries(_syncItems);
_journal->commit(QStringLiteral("post stale entry removal"));
// Emit the started signal only after the propagator has been set up.
if (_needsUpdate)
Q_EMIT started();
_propagator->start(std::move(_syncItems));
qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms";
}
void SyncEngine::handleRemnantReadOnlyFolders()
{
promptUserBeforePropagation([this](auto &&callback) {
emit aboutToRemoveRemnantsReadOnlyFolders(_remnantReadOnlyFolders, _localPath, callback);
});
}
template <typename T>
void SyncEngine::promptUserBeforePropagation(T &&lambda)
{
QPointer<QObject> guard = new QObject();
QPointer<QObject> self = this;
auto callback = [this, self, guard](bool cancel) -> void {
// use a guard to ensure its only called once...
// qpointer to self to ensure we still exist
if (!guard || !self) {
return;
}
guard->deleteLater();
cancelSyncOrContinue(cancel);
};
lambda(callback);
}
void SyncEngine::slotAddTouchedFile(const QString &fn)
{
QElapsedTimer now;
@ -1486,6 +1516,11 @@ void SyncEngine::slotCleanupScheduledSyncTimers()
}
}
void SyncEngine::remnantReadOnlyFolderDiscovered(const SyncFileItemPtr &item)
{
_remnantReadOnlyFolders.push_back(item);
}
void SyncEngine::slotUnscheduleFilesDelayedSync()
{
if (!_discoveryPhase || _discoveryPhase->_filesUnscheduleSync.empty()) {

View File

@ -189,6 +189,10 @@ signals:
*/
void aboutToRemoveAllFiles(OCC::SyncFileItem::Direction direction, std::function<void(bool)> f);
void aboutToRemoveRemnantsReadOnlyFolders(const QList<SyncFileItemPtr> &folders,
const QString &localPath,
std::function<void(bool)> f);
// A new folder was discovered and was not synced because of the confirmation feature
void newBigFolder(const QString &folder, bool isExternal);
@ -240,6 +244,8 @@ private slots:
void slotUnscheduleFilesDelayedSync();
void slotCleanupScheduledSyncTimers();
void remnantReadOnlyFolderDiscovered(const OCC::SyncFileItemPtr &item);
private:
// Some files need a sync run to be executed at a specified time after
// their status is scheduled to change (e.g. lock status will expire in
@ -359,6 +365,15 @@ private:
*/
void restoreOldFiles(SyncFileItemVector &syncItems);
void cancelSyncOrContinue(bool cancel);
void finishSync();
void handleRemnantReadOnlyFolders();
template <typename T>
void promptUserBeforePropagation(T &&lambda);
// true if there is at least one file which was not changed on the server
bool _hasNoneFiles = false;
@ -404,6 +419,8 @@ private:
QVector<QSharedPointer<ScheduledSyncTimer>> _scheduledSyncTimers;
SingleItemDiscoveryOptions _singleItemDiscoveryOptions;
QList<SyncFileItemPtr> _remnantReadOnlyFolders;
};
}

View File

@ -59,12 +59,22 @@ SyncFileItemPtr findDiscoveryItem(const SyncFileItemVector &spy, const QString &
bool itemInstruction(const ItemCompletedSpy &spy, const QString &path, const SyncInstructions instr)
{
auto item = spy.findItem(path);
const auto checkHelper = [item, instr]() {
QCOMPARE(item->_instruction, instr);
};
checkHelper();
return item->_instruction == instr;
}
bool discoveryInstruction(const SyncFileItemVector &spy, const QString &path, const SyncInstructions instr)
{
auto item = findDiscoveryItem(spy, path);
const auto checkHelper = [item, instr]() {
QCOMPARE(item->_instruction, instr);
};
checkHelper();
return item->_instruction == instr;
}
@ -87,6 +97,14 @@ private slots:
FakeFolder fakeFolder{ FileInfo() };
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
qDebug() << "aboutToRemoveRemnantsReadOnlyFolders called";
Q_UNUSED(folders);
Q_UNUSED(localPath);
callback(false);
});
// Some of this test depends on the order of discovery. With threading
// that order becomes effectively random, but we want to make sure to test
// all cases and thus disable threading.
@ -424,6 +442,13 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
Q_UNUSED(folders)
Q_UNUSED(localPath)
callback(false);
});
// Some of this test depends on the order of discovery. With threading
// that order becomes effectively random, but we want to make sure to test
// all cases and thus disable threading.
@ -543,6 +568,14 @@ private slots:
void testAllowedMoveForbiddenDelete() {
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
// Some of this test depends on the order of discovery. With threading
// that order becomes effectively random, but we want to make sure to test
// all cases and thus disable threading.
@ -591,6 +624,15 @@ private slots:
void testParentMoveNotAllowedChildrenRestored()
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &lm = fakeFolder.localModifier();
auto &rm = fakeFolder.remoteModifier();
rm.mkdir("forbidden-move");
@ -643,6 +685,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");
@ -660,6 +710,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readWriteFolder");
@ -678,6 +736,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("testFolder");
@ -714,6 +780,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("testFolder");
@ -774,6 +848,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");
@ -804,6 +886,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");
@ -836,6 +926,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");
@ -871,6 +969,14 @@ private slots:
{
FakeFolder fakeFolder{FileInfo{}};
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
for(const auto &oneFolder : folders) {
FileSystem::removeRecursively(localPath + oneFolder->_file);
}
callback(false);
});
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");