From: Matthieu Gallien Date: Tue, 3 Sep 2024 16:07:57 +0000 (+0200) Subject: detect remnants read-only folders to delete: try to delete them X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~6^2~24^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=5b8144f806514c90ce0294d7b4f974cfd660b998;p=nextcloud-desktop.git detect remnants read-only folders to delete: try to delete them Signed-off-by: Matthieu Gallien --- diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 5c7620c6a..52baa342f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -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,34 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, std::functio msgBox->open(); } +void Folder::slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback) +{ + const auto msg = tr("Do you want to clean up remnant read-only folders left over from previous failed synchronization attempts."); + auto msgBox = new QMessageBox(QMessageBox::Question, tr("Remove remnant invalid folders?"), + msg, QMessageBox::NoButton); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint); + msgBox->addButton(tr("Proceed to remove remnant folders"), QMessageBox::AcceptRole); + const auto keepBtn = msgBox->addButton(tr("Do nothing"), QMessageBox::RejectRole); + setSyncPaused(true); + connect(msgBox, &QMessageBox::finished, this, [msgBox, keepBtn, callback, folders, localPath, this] { + const bool cancel = msgBox->clickedButton() == keepBtn; + if (!cancel) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + } + callback(cancel); + if (cancel) { + setSyncPaused(true); + } + }); + connect(this, &Folder::destroyed, msgBox, &QMessageBox::deleteLater); + msgBox->open(); +} + void Folder::removeLocalE2eFiles() { qCDebug(lcFolder) << "Removing local E2EE files"; diff --git a/src/gui/folder.h b/src/gui/folder.h index e3bed9f7f..c79153a86 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -335,6 +335,10 @@ public slots: // connected to the corresponding signals in the SyncEngine void slotAboutToRemoveAllFiles(OCC::SyncFileItem::Direction, std::function callback); + void slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback); + /** * Starts a sync operation * diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index eb114f553..9be7992a5 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -817,6 +817,10 @@ void SyncEngine::slotDiscoveryFinished() return; } + if (!_remnantReadOnlyFolders.isEmpty()) { + handleRemnantReadOnlyFolders(); + return; + } finishSync(); } @@ -1101,6 +1105,13 @@ void SyncEngine::finishSync() 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 void SyncEngine::promptUserBeforePropagation(T &&lambda) { diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 278c5569d..2d824f331 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -189,6 +189,10 @@ signals: */ void aboutToRemoveAllFiles(OCC::SyncFileItem::Direction direction, std::function f); + void aboutToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function f); + // A new folder was discovered and was not synced because of the confirmation feature void newBigFolder(const QString &folder, bool isExternal); @@ -365,6 +369,8 @@ private: void finishSync(); + void handleRemnantReadOnlyFolders(); + template void promptUserBeforePropagation(T &&lambda); diff --git a/test/testpermissions.cpp b/test/testpermissions.cpp index ee1ddbcdb..f334b22de 100644 --- a/test/testpermissions.cpp +++ b/test/testpermissions.cpp @@ -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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function 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 &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder");