From 241101b9136391abdfde6ef35feac5790d71d9d0 Mon Sep 17 00:00:00 2001 From: Dries Mys Date: Sun, 16 Jul 2023 00:05:20 +0200 Subject: [PATCH] Create placeholder while dehydrating if needed When replacing an OnlineOnly file by another one, the file maintains it OnlineOnly pin state, but it is converted to a regular file. So, the dehydration should convert the regular file to a (dehydrated) placeholder instead of trying to update the (non-existing) placeholder. Closes #4274 Signed-off-by: Dries Mys --- src/libsync/vfs/cfapi/cfapiwrapper.cpp | 52 ++++++++++++++++---------- test/testsynccfapi.cpp | 32 ++++++++++++++++ 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp index 1849deb74bed..ee729896300a 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.cpp +++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp @@ -796,29 +796,41 @@ OCC::Result OCC::CfApiWrapper::de return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(path).arg(modtime)}; } - const auto info = findPlaceholderInfo(path); - if (!info) { - return { "Can't update non existing placeholder info" }; - } - const auto fileIdentity = QString::fromUtf8(fileId).toStdWString(); const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t); - CF_FILE_RANGE dehydrationRange; - dehydrationRange.StartingOffset.QuadPart = 0; - dehydrationRange.Length.QuadPart = size; - - const qint64 result = CfUpdatePlaceholder(handleForPath(path).get(), nullptr, - fileIdentity.data(), sizeToDWORD(fileIdentitySize), - &dehydrationRange, - 1, - CF_UPDATE_FLAG_MARK_IN_SYNC | CF_UPDATE_FLAG_DEHYDRATE, - nullptr, - nullptr); - - if (result != S_OK) { - qCWarning(lcCfApiWrapper) << "Couldn't update placeholder info for" << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage()); - return { "Couldn't update placeholder info" }; + const auto info = findPlaceholderInfo(path); + if (info) { + CF_FILE_RANGE dehydrationRange; + dehydrationRange.StartingOffset.QuadPart = 0; + dehydrationRange.Length.QuadPart = size; + + const qint64 result = CfUpdatePlaceholder(handleForPath(path).get(), + nullptr, + fileIdentity.data(), + sizeToDWORD(fileIdentitySize), + &dehydrationRange, + 1, + CF_UPDATE_FLAG_MARK_IN_SYNC | CF_UPDATE_FLAG_DEHYDRATE, + nullptr, + nullptr); + + if (result != S_OK) { + qCWarning(lcCfApiWrapper) << "Couldn't update placeholder info for" << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage()); + return {"Couldn't update placeholder info"}; + } + } else { + const qint64 result = CfConvertToPlaceholder(handleForPath(path).get(), + fileIdentity.data(), + sizeToDWORD(fileIdentitySize), + CF_CONVERT_FLAG_MARK_IN_SYNC | CF_CONVERT_FLAG_DEHYDRATE, + nullptr, + nullptr); + + if (result != S_OK) { + qCWarning(lcCfApiWrapper) << "Couldn't convert to placeholder" << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage()); + return {"Couldn't convert to placeholder"}; + } } return OCC::Vfs::ConvertToPlaceholderResult::Ok; diff --git a/test/testsynccfapi.cpp b/test/testsynccfapi.cpp index 95dd076ffc38..1576f5330069 100644 --- a/test/testsynccfapi.cpp +++ b/test/testsynccfapi.cpp @@ -120,6 +120,38 @@ private slots: QTest::newRow("skip local discovery") << false; } + void testReplaceOnlineOnlyFile() + { + FakeFolder fakeFolder{FileInfo{}}; + auto vfs = setupVfs(fakeFolder); + + // Create a new local (non-placeholder) file + fakeFolder.localModifier().insert("file"); + QVERIFY(!vfs->pinState("file").isValid()); + + CopyFile(QString(fakeFolder.localPath() + "file").toStdWString().data(), QString(fakeFolder.localPath() + "file1").toStdWString().data(), false); + + // Sync the files: files should be converted to placeholder files + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(vfs->pinState("file").isValid()); + + // Convert to Online Only + ::setPinState(fakeFolder.localPath() + "file", PinState::OnlineOnly, cfapi::Recurse); + + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(*vfs->pinState("file"), PinState::OnlineOnly); + CFVERIFY_VIRTUAL(fakeFolder, "file"); + + // Replace the file + CopyFile(QString(fakeFolder.localPath() + "file1").toStdWString().data(), QString(fakeFolder.localPath() + "file").toStdWString().data(), false); + + // Sync again: file should be correctly dehydrated again without error. + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(vfs->pinState("file").isValid()); + QCOMPARE(*vfs->pinState("file"), PinState::OnlineOnly); + CFVERIFY_VIRTUAL(fakeFolder, "file"); + } + void testVirtualFileLifecycle() { QFETCH(bool, doLocalDiscovery);