diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 53f2ce22b78e5..368fc84a3c9a8 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -1871,7 +1871,7 @@ bool ProcessDirectoryJob::isVfsWithSuffix() const void ProcessDirectoryJob::computePinState(PinState parentState) { _pinState = parentState; - if (_queryLocal != ParentDontExist) { + if (_queryLocal != ParentDontExist && QFileInfo::exists(_discoveryData->_localDir + _currentFolder._local)) { if (auto state = _discoveryData->_syncOptions._vfs->pinState(_currentFolder._local)) // ouch! pin local or original? _pinState = *state; } diff --git a/src/libsync/propagateremotemove.cpp b/src/libsync/propagateremotemove.cpp index 882d7e077bd8b..d42ba326943e2 100644 --- a/src/libsync/propagateremotemove.cpp +++ b/src/libsync/propagateremotemove.cpp @@ -251,7 +251,7 @@ void PropagateRemoteMove::finalize() return; } auto &vfs = propagator()->syncOptions()._vfs; - auto pinState = vfs->pinState(_item->_originalFile); + auto pinState = vfs->pinState(_item->_renameTarget); const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget); diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp index 112f16e9f8826..ce507b7900509 100644 --- a/src/libsync/propagatorjobs.cpp +++ b/src/libsync/propagatorjobs.cpp @@ -222,11 +222,19 @@ void PropagateLocalRename::start() if (propagator()->_abortRequested) return; + auto &vfs = propagator()->syncOptions()._vfs; const auto previousNameInDb = propagator()->adjustRenamedPath(_item->_file); const auto existingFile = propagator()->fullLocalPath(propagator()->adjustRenamedPath(_item->_file)); const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget); const auto fileAlreadyMoved = !QFileInfo::exists(propagator()->fullLocalPath(_item->_originalFile)); + auto pinState = OCC::PinState::Unspecified; + if (!fileAlreadyMoved) { + auto pinStateResult = vfs->pinState(propagator()->adjustRenamedPath(_item->_file)); + if (pinStateResult) { + pinState = pinStateResult.get(); + } + } // if the file is a file underneath a moved dir, the _item->file is equal // to _item->renameTarget and the file is not moved as a result. @@ -269,10 +277,10 @@ void PropagateLocalRename::start() return; } - auto &vfs = propagator()->syncOptions()._vfs; - auto pinState = vfs->pinState(_item->_originalFile); - if (!vfs->setPinState(_item->_originalFile, PinState::Inherited)) { - qCWarning(lcPropagateLocalRename) << "Could not set pin state of" << _item->_originalFile << "to inherited"; + if (pinState != OCC::PinState::Unspecified && !vfs->setPinState(_item->_renameTarget, pinState)) { + qCWarning(lcPropagateLocalRename) << "Could not set pin state of" << _item->_renameTarget << "to old value" << pinState; + done(SyncFileItem::NormalError, tr("Error setting pin state")); + return; } const auto oldFile = _item->_file; @@ -331,11 +339,6 @@ void PropagateLocalRename::start() return; } } - if (pinState && *pinState != PinState::Inherited - && !vfs->setPinState(_item->_renameTarget, *pinState)) { - done(SyncFileItem::NormalError, tr("Error setting pin state")); - return; - } propagator()->_journal->commit("localRename"); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 1f046c15d8dc0..d1cbd438f3d92 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -377,6 +377,13 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) emit itemCompleted(item); return; } + } else { + if (!FileSystem::setModTime(filePath, item->_modtime)) { + item->_instruction = CSYNC_INSTRUCTION_ERROR; + item->_errorString = tr("Could not update file metadata: %1").arg(filePath); + emit itemCompleted(item); + return; + } } // Updating the db happens on success diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp index be41cfdc4ef1d..e36c4f7a8b589 100644 --- a/test/syncenginetestutils.cpp +++ b/test/syncenginetestutils.cpp @@ -1209,7 +1209,7 @@ void FakeFolder::fromDisk(QDir &dir, FileInfo &templateFi) continue; } char contentChar = content.at(0); - templateFi.children.insert(diskChild.fileName(), FileInfo { diskChild.fileName(), diskChild.size(), contentChar }); + templateFi.children.insert(diskChild.fileName(), FileInfo{diskChild.fileName(), diskChild.size(), contentChar, diskChild.lastModified()}); } } } diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h index ab4074df6155c..b0b8e1eed77f9 100644 --- a/test/syncenginetestutils.h +++ b/test/syncenginetestutils.h @@ -117,6 +117,7 @@ class FileInfo : public FileModifier FileInfo(const QString &name) : name{name} { } FileInfo(const QString &name, qint64 size) : name{name}, isDir{false}, size{size} { } FileInfo(const QString &name, qint64 size, char contentChar) : name{name}, isDir{false}, size{size}, contentChar{contentChar} { } + FileInfo(const QString &name, qint64 size, char contentChar, QDateTime mtime) : name{name}, isDir{false}, lastModified(mtime), size{size}, contentChar{contentChar} { } FileInfo(const QString &name, const std::initializer_list &children); void addChild(const FileInfo &info); diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 7a562bca5fb77..8a9100a09ba8c 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -1243,6 +1243,45 @@ private slots: auto expectedState = fakeFolder.currentLocalState(); QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } + + void testServerUpdatingMTimeShouldNotCreateConflicts() + { + constexpr auto testFile = "test.txt"; + constexpr auto CURRENT_MTIME = 1646057277; + + FakeFolder fakeFolder{ FileInfo{} }; + + fakeFolder.remoteModifier().insert(testFile); + fakeFolder.remoteModifier().setModTimeKeepEtag(testFile, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME - 2)); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + QCOMPARE(printDbData(fakeFolder.dbState()), printDbData(fakeFolder.currentRemoteState())); + const auto fileFirstSync = fakeFolder.currentLocalState().find(testFile); + QVERIFY(fileFirstSync); + QCOMPARE(fileFirstSync->lastModified.toSecsSinceEpoch(), CURRENT_MTIME - 2); + + fakeFolder.remoteModifier().setModTimeKeepEtag(testFile, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME - 1)); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::FilesystemOnly); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + QCOMPARE(printDbData(fakeFolder.dbState()), printDbData(fakeFolder.currentRemoteState())); + const auto fileSecondSync = fakeFolder.currentLocalState().find(testFile); + QVERIFY(fileSecondSync); + QCOMPARE(fileSecondSync->lastModified.toSecsSinceEpoch(), CURRENT_MTIME - 1); + + fakeFolder.remoteModifier().setModTime(testFile, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME)); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::FilesystemOnly); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + QCOMPARE(printDbData(fakeFolder.dbState()), printDbData(fakeFolder.currentRemoteState())); + const auto fileThirdSync = fakeFolder.currentLocalState().find(testFile); + QVERIFY(fileThirdSync); + QCOMPARE(fileThirdSync->lastModified.toSecsSinceEpoch(), CURRENT_MTIME); + } }; QTEST_GUILESS_MAIN(TestSyncEngine)