From 4ca26ef27339267ac233d01a904ed2d9fb99bfa9 Mon Sep 17 00:00:00 2001
From: Matthieu Gallien <matthieu.gallien@nextcloud.com>
Date: Fri, 21 Jan 2022 19:03:23 +0100
Subject: [PATCH] use proper API to dehydrate a placeholder file

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
---
 src/libsync/vfs/cfapi/cfapiwrapper.cpp | 32 ++++++++++++++++++++++++++
 src/libsync/vfs/cfapi/cfapiwrapper.h   |  1 +
 src/libsync/vfs/cfapi/vfs_cfapi.cpp    | 27 ++++++++--------------
 3 files changed, 43 insertions(+), 17 deletions(-)

diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp
index 59bf581dc2389..8fa3aeb9a39d7 100644
--- a/src/libsync/vfs/cfapi/cfapiwrapper.cpp
+++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp
@@ -740,6 +740,38 @@ OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::up
     return OCC::Vfs::ConvertToPlaceholderResult::Ok;
 }
 
+OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::dehydratePlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId)
+{
+    Q_ASSERT(handle);
+
+    if (modtime <= 0) {
+        return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(pathForHandle(handle)).arg(modtime)};
+    }
+
+    const auto info = findPlaceholderInfo(handle);
+    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(handle.get(), nullptr,
+                                              fileIdentity.data(), sizeToDWORD(fileIdentitySize),
+                                              &dehydrationRange, 1, CF_UPDATE_FLAG_MARK_IN_SYNC, nullptr, nullptr);
+
+    if (result != S_OK) {
+        qCWarning(lcCfApiWrapper) << "Couldn't update placeholder info for" << pathForHandle(handle) << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage());
+        return { "Couldn't update placeholder info" };
+    }
+
+    return OCC::Vfs::ConvertToPlaceholderResult::Ok;
+}
+
 OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::convertToPlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath)
 {
     Q_UNUSED(modtime);
diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.h b/src/libsync/vfs/cfapi/cfapiwrapper.h
index e6f4ac8b93b18..5993a7f461b2a 100644
--- a/src/libsync/vfs/cfapi/cfapiwrapper.h
+++ b/src/libsync/vfs/cfapi/cfapiwrapper.h
@@ -94,6 +94,7 @@ NEXTCLOUD_CFAPI_EXPORT Result<OCC::Vfs::ConvertToPlaceholderResult, QString> set
 NEXTCLOUD_CFAPI_EXPORT Result<void, QString> createPlaceholderInfo(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId);
 NEXTCLOUD_CFAPI_EXPORT Result<OCC::Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderInfo(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath = QString());
 NEXTCLOUD_CFAPI_EXPORT Result<OCC::Vfs::ConvertToPlaceholderResult, QString> convertToPlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath);
+NEXTCLOUD_CFAPI_EXPORT Result<OCC::Vfs::ConvertToPlaceholderResult, QString> dehydratePlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId);
 
 }
 
diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.cpp b/src/libsync/vfs/cfapi/vfs_cfapi.cpp
index 1adeb5d686b85..aec9289ac8f10 100644
--- a/src/libsync/vfs/cfapi/vfs_cfapi.cpp
+++ b/src/libsync/vfs/cfapi/vfs_cfapi.cpp
@@ -132,26 +132,19 @@ Result<void, QString> VfsCfApi::createPlaceholder(const SyncFileItem &item)
 
 Result<void, QString> VfsCfApi::dehydratePlaceholder(const SyncFileItem &item)
 {
-    const auto previousPin = pinState(item._file);
-
-    if (!FileSystem::remove(_setupParams.filesystemPath + item._file)) {
-        return QStringLiteral("Couldn't remove %1 to fulfill dehydration").arg(item._file);
-    }
-
-    const auto r = createPlaceholder(item);
-    if (!r) {
-        return r;
-    }
-
-    if (previousPin) {
-        if (*previousPin == PinState::AlwaysLocal) {
-            setPinState(item._file, PinState::Unspecified);
+    const auto localPath = QDir::toNativeSeparators(_setupParams.filesystemPath + item._file);
+    const auto handle = cfapi::handleForPath(localPath);
+    if (handle) {
+        auto result = cfapi::dehydratePlaceholder(handle, item._modtime, item._size, item._fileId);
+        if (result) {
+            return {};
         } else {
-            setPinState(item._file, *previousPin);
+            return result.error();
         }
+    } else {
+        qCWarning(lcCfApi) << "Couldn't update metadata for non existing file" << localPath;
+        return {QStringLiteral("Couldn't update metadata")};
     }
-
-    return {};
 }
 
 Result<Vfs::ConvertToPlaceholderResult, QString> VfsCfApi::convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &replacesFile)