detect remnants read-only folders to delete: try to delete them

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
This commit is contained in:
Matthieu Gallien 2024-09-03 18:07:57 +02:00
parent ab2002596e
commit 5b8144f806
No known key found for this signature in database
GPG key ID: 7D0F74F05C22F553
5 changed files with 157 additions and 0 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,34 @@ 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)
{
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";

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

@ -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 <typename T>
void SyncEngine::promptUserBeforePropagation(T &&lambda)
{

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);
@ -365,6 +369,8 @@ private:
void finishSync();
void handleRemnantReadOnlyFolders();
template <typename T>
void promptUserBeforePropagation(T &&lambda);

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");