diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 435cbcdb2..9aadc4307 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -82,21 +82,6 @@ void ProcessDirectoryJob::process() } _serverNormalQueryEntries.clear(); - for (auto &e : _localNormalQueryEntries) { - // Remove the virtual file suffix - auto name = e.name; - if (e.isVirtualFile && isVfsWithSuffix()) { - chopVirtualFileSuffix(name); - auto &entry = entries[name]; - // If there is both a virtual file and a real file, we must keep the real file - if (!entry.localEntry.isValid()) - entry.localEntry = std::move(e); - } else { - entries[name].localEntry = std::move(e); - } - } - _localNormalQueryEntries.clear(); - // fetch all the name from the DB auto pathU8 = _currentFolder._original.toUtf8(); if (!_discoveryData->_statedb->listFilesInPath(pathU8, [&](const SyncJournalFileRecord &rec) { @@ -109,6 +94,21 @@ void ProcessDirectoryJob::process() return; } + for (auto &e : _localNormalQueryEntries) { + // Normally for vfs-suffix files the local entries need the suffix removed. + // However, don't do it if "foo.owncloud" exists on the server or in the db + // (as a non-virtual file): we don't want to create two entries. + auto name = e.name; + if (e.isVirtualFile && isVfsWithSuffix() && entries.find(name) == entries.end()) { + chopVirtualFileSuffix(name); + // If there is both a virtual file and a real file, we must keep the real file + if (entries[name].localEntry.isValid()) + continue; + } + entries[name].localEntry = std::move(e); + } + _localNormalQueryEntries.clear(); + // // Iterate over entries and process them // @@ -123,7 +123,7 @@ void ProcessDirectoryJob::process() if (e.dbEntry.isVirtualFile()) { ASSERT(hasVirtualFileSuffix(e.dbEntry._path)); addVirtualFileSuffix(path._original); - } else if (e.localEntry.isVirtualFile) { + } else if (e.localEntry.isVirtualFile && !e.dbEntry.isValid()) { // We don't have a db entry - but it should be at this path addVirtualFileSuffix(path._original); } @@ -303,6 +303,18 @@ void ProcessDirectoryJob::processFile(PathTuple path, if (item->_type == ItemTypeVirtualFileDehydration) item->_type = ItemTypeFile; + // VFS suffixed files on the server are ignored + if (isVfsWithSuffix()) { + if (hasVirtualFileSuffix(serverEntry.name) + || (localEntry.isVirtualFile && !dbEntry.isVirtualFile() && hasVirtualFileSuffix(dbEntry._path))) { + item->_instruction = CSYNC_INSTRUCTION_IGNORE; + item->_errorString = tr("File has extension reserved for virtual files."); + _childIgnored = true; + emit _discoveryData->itemDiscovered(item); + return; + } + } + if (serverEntry.isValid()) { processFileAnalyzeRemoteInfo(item, path, localEntry, serverEntry, dbEntry); return; @@ -681,6 +693,9 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( item->_direction = SyncFileItem::Down; item->_instruction = CSYNC_INSTRUCTION_NEW; item->_type = ItemTypeVirtualFile; + } else { + qCInfo(lcDisco) << "Virtual file with non-virtual db entry, ignoring:" << item->_file; + item->_instruction = CSYNC_INSTRUCTION_IGNORE; } } } else if (!typeChange && ((dbEntry._modtime == localEntry.modtime && dbEntry._fileSize == localEntry.size) || localEntry.isDirectory)) { diff --git a/test/testsyncvirtualfiles.cpp b/test/testsyncvirtualfiles.cpp index 7c55eaaa0..f07c4e59a 100644 --- a/test/testsyncvirtualfiles.cpp +++ b/test/testsyncvirtualfiles.cpp @@ -742,8 +742,7 @@ private slots: void testNewVirtuals() { FakeFolder fakeFolder{ FileInfo() }; - SyncOptions syncOptions = vfsSyncOptions(fakeFolder); - fakeFolder.syncEngine().setSyncOptions(syncOptions); + fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder)); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); auto setPin = [&] (const QByteArray &path, PinState state) { @@ -791,6 +790,50 @@ private slots: QVERIFY(fakeFolder.currentLocalState().find("local/file1")); QVERIFY(fakeFolder.currentLocalState().find("unspec/file1.nextcloud")); } + + // Check what happens if vfs-suffixed files exist on the server or in the db + void testSuffixOnServerOrDb() + { + FakeFolder fakeFolder{ FileInfo() }; + + QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &))); + auto cleanup = [&]() { + completeSpy.clear(); + }; + cleanup(); + + // file1.nextcloud is happily synced with Vfs::Off + fakeFolder.remoteModifier().mkdir("A"); + fakeFolder.remoteModifier().insert("A/file1.nextcloud"); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + cleanup(); + + // Enable suffix vfs + fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder)); + + // Local changes of suffixed file do nothing + fakeFolder.localModifier().appendByte("A/file1.nextcloud"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(itemInstruction(completeSpy, "A/file1.nextcloud", CSYNC_INSTRUCTION_IGNORE)); + cleanup(); + + // Remote don't do anything either + fakeFolder.remoteModifier().appendByte("A/file1.nextcloud"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(itemInstruction(completeSpy, "A/file1.nextcloud", CSYNC_INSTRUCTION_IGNORE)); + cleanup(); + + // New files with a suffix aren't propagated downwards in the first place + fakeFolder.remoteModifier().insert("A/file2.nextcloud"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(itemInstruction(completeSpy, "A/file2.nextcloud", CSYNC_INSTRUCTION_IGNORE)); + QVERIFY(fakeFolder.currentRemoteState().find("A/file2.nextcloud")); + QVERIFY(!fakeFolder.currentLocalState().find("A/file2")); + QVERIFY(!fakeFolder.currentLocalState().find("A/file2.nextcloud")); + QVERIFY(!fakeFolder.currentLocalState().find("A/file2.nextcloud.nextcloud")); + cleanup(); + } }; QTEST_GUILESS_MAIN(TestSyncVirtualFiles)