diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 194606aa111cb..8316d085ff1b7 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -577,6 +577,7 @@ void OwncloudPropagator::start(SyncFileItemVector &&items) } else { qCWarning(lcPropagator) << "WARNING: Job within a removed directory? This should not happen!" << item->_file << item->_instruction; + Q_ASSERT(false); } } diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index dac82eef035d8..d81a98c243064 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -321,7 +321,9 @@ void PropagateUploadFileNG::finishUpload() const auto fileSize = _fileToUpload._size; headers[QByteArrayLiteral("OC-Total-Length")] = QByteArray::number(fileSize); if (_item->_lockOwnerType == SyncFileItem::LockOwnerType::TokenLock && - _item->_locked == SyncFileItem::LockStatus::LockedItem) { + _item->_locked == SyncFileItem::LockStatus::LockedItem && + _item->_instruction != CSYNC_INSTRUCTION_NEW && + _item->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE) { headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _fileToUpload._file + "> (_lockToken.toUtf8() + ">)").toUtf8(); } diff --git a/src/libsync/propagateuploadv1.cpp b/src/libsync/propagateuploadv1.cpp index 439fb8d6a19ab..c162a7660807c 100644 --- a/src/libsync/propagateuploadv1.cpp +++ b/src/libsync/propagateuploadv1.cpp @@ -95,7 +95,9 @@ void PropagateUploadFileV1::startNextChunk() QString path = _fileToUpload._file; if (_item->_lockOwnerType == SyncFileItem::LockOwnerType::TokenLock && - _item->_locked == SyncFileItem::LockStatus::LockedItem) { + _item->_locked == SyncFileItem::LockStatus::LockedItem && + _item->_instruction != CSYNC_INSTRUCTION_NEW && + _item->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE) { headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _fileToUpload._file + "> (_lockToken.toUtf8() + ">)").toUtf8(); } diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp index bf4120cb06012..d069f2085e83a 100644 --- a/test/syncenginetestutils.cpp +++ b/test/syncenginetestutils.cpp @@ -23,6 +23,8 @@ #include #include +using namespace Qt::StringLiterals; + PathComponents::PathComponents(const char *path) : PathComponents { QString::fromUtf8(path) } { @@ -66,7 +68,9 @@ void DiskFileModifier::remove(const QString &relativePath) void DiskFileModifier::insert(const QString &relativePath, qint64 size, char contentChar) { QFile file { _rootDir.filePath(relativePath) }; - QVERIFY(!file.exists()); + if (!file.exists()) { + QVERIFY(!file.exists()); + } file.open(QFile::WriteOnly); QByteArray buf(1024, contentChar); for (int x = 0; x < size / buf.size(); ++x) { @@ -317,7 +321,9 @@ FileInfo *FileInfo::create(const QString &relativePath, qint64 size, char conten { const PathComponents pathComponents { relativePath }; FileInfo *parent = findInvalidatingEtags(pathComponents.parentDirComponents()); - Q_ASSERT(parent); + if (!parent) { + return nullptr; + } FileInfo &child = parent->children[pathComponents.fileName()] = FileInfo { pathComponents.fileName(), size }; child.parentPath = parent->path(); child.contentChar = contentChar; @@ -533,6 +539,9 @@ FileInfo *FakePutReply::perform(FileInfo &remoteRootFileInfo, const QNetworkRequ } else { // Assume that the file is filled with the same character fileInfo = remoteRootFileInfo.create(fileName, putPayload.size(), putPayload.isEmpty() ? ' ' : putPayload.at(0)); + if (!fileInfo) { + return fileInfo; + } } fileInfo->lastModified = OCC::Utility::qDateTimeFromTime_t(request.rawHeader("X-OC-Mtime").toLongLong()); remoteRootFileInfo.find(fileName, /*invalidateEtags=*/FileInfo::EtagsAction::Invalidate); @@ -541,6 +550,13 @@ FileInfo *FakePutReply::perform(FileInfo &remoteRootFileInfo, const QNetworkRequ void FakePutReply::respond() { + if (!fileInfo) { + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 412); + emit metaDataChanged(); + emit finished(); + return; + } + emit uploadProgress(fileInfo->size, fileInfo->size); setRawHeader("OC-ETag", fileInfo->etag); setRawHeader("ETag", fileInfo->etag); @@ -1251,6 +1267,7 @@ FakeFolder::FakeFolder(const FileInfo &fileTemplate, const OCC::Optional(localPath() + QStringLiteral(".sync_test.db")); _syncEngine = std::make_unique(_account, localPath(), OCC::SyncOptions{}, remotePath, _journalDb.get()); // Ignore temporary files from the download. (This is in the default exclude list, but we don't load it) + _syncEngine->excludedFiles().addManualExclude(u".~lock.*#"_s); _syncEngine->excludedFiles().addManualExclude(QStringLiteral("]*.~*")); // handle aboutToRemoveAllFiles with a timeout in case our test does not handle it diff --git a/test/testlockfile.cpp b/test/testlockfile.cpp index e2779a655b62c..044d11dff5ac5 100644 --- a/test/testlockfile.cpp +++ b/test/testlockfile.cpp @@ -15,6 +15,8 @@ #include #include +using namespace Qt::StringLiterals; + class TestLockFile : public QObject { Q_OBJECT @@ -871,6 +873,92 @@ private slots: QVERIFY(fakeFolder.syncOnce()); } + + void testUploadLockedFilesInDeletedFolder() + { + FakeFolder fakeFolder{FileInfo{}}; + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + int nGET = 0, nPUT = 0; + auto verifyLackOfTokenIfHeader = false; + QObject parent; + fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * { + Q_UNUSED(outgoingData) + Q_UNUSED(request) + + if (op == QNetworkAccessManager::PutOperation) { + ++nPUT; + if (verifyLackOfTokenIfHeader) { + if (request.hasRawHeader("If")) { + Q_ASSERT(false); + } + } + } else if (op == QNetworkAccessManager::GetOperation) { + ++nGET; + } + + return nullptr; + }); + + ItemCompletedSpy completeSpy(fakeFolder); + + const auto cleanUpHelper = [&nGET, &nPUT, &completeSpy] () { + nGET = 0; + nPUT = 0; + completeSpy.clear(); + }; + + fakeFolder.localModifier().mkdir(u"parent"_s); + + cleanUpHelper(); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(nGET, 0); + QCOMPARE(nPUT, 0); + + fakeFolder.localModifier().mkdir(u"parent/child"_s); + + cleanUpHelper(); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(nGET, 0); + QCOMPARE(nPUT, 0); + + fakeFolder.localModifier().insert(u"parent/child/hello.odt"_s); + + cleanUpHelper(); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(nGET, 0); + QCOMPARE(nPUT, 1); + + OCC::SyncJournalFileRecord record; + QVERIFY(fakeFolder.syncJournal().getFileRecord(u"parent/child/hello.odt"_s, &record)); + QVERIFY(record.isValid()); + record._lockstate._locked = true; + record._lockstate._lockToken = u"azertyuiop"_s; + record._lockstate._lockOwnerType = static_cast(OCC::SyncFileItem::LockOwnerType::TokenLock); + QVERIFY(fakeFolder.syncJournal().setFileRecord(record)); + qDebug() << fakeFolder.localPath() + u"parent/child/.~lock.hello.odt#"_s; + QFile newLockFile(fakeFolder.localPath() + u"parent/child/.~lock.hello.odt#"_s); + newLockFile.open(QFile::OpenModeFlag::NewOnly); + OCC::FileSystem::setFileHidden(fakeFolder.localPath() + u"parent/child/.~lock.hello.odt#"_s, true); + + fakeFolder.remoteModifier().remove(u"parent/child"_s); + fakeFolder.localModifier().insert(u"parent/child/hello.odt"_s, 128); + + cleanUpHelper(); + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem, {u"parent"_s, u"parent/child"_s, u"parent/child/.~lock.hello.odt#"_s, u"parent/child/hello.odt"_s}); + QVERIFY(!fakeFolder.syncOnce()); + QCOMPARE(nGET, 0); + QCOMPARE(nPUT, 1); + fakeFolder.syncJournal().wipeErrorBlacklistCategory(OCC::SyncJournalErrorBlacklistRecord::Normal); + verifyLackOfTokenIfHeader = true; + + fakeFolder.remoteModifier().mkdir(u"parent/child"_s); + cleanUpHelper(); + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem, {u"parent"_s, u"parent/child"_s, u"parent/child/.~lock.hello.odt#"_s, u"parent/child/hello.odt"_s}); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(nGET, 0); + QCOMPARE(nPUT, 1); + } }; QTEST_GUILESS_MAIN(TestLockFile)