Fix CF_PLACEHOLDER_BASIC_INFO.FileIdentity

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
Signed-off-by: Hannah von Reth <h.vonreth@opencloud.eu>
This commit is contained in:
Hannah von Reth 2025-06-04 10:06:37 +02:00 committed by Jyrki Gadinger
parent c139f91145
commit 4d2966771d
2 changed files with 77 additions and 93 deletions

View File

@ -27,6 +27,7 @@
#include "config.h"
Q_LOGGING_CATEGORY(lcCfApiWrapper, "nextcloud.sync.vfs.cfapi.wrapper", QtInfoMsg)
using namespace Qt::Literals::StringLiterals;
#define FIELD_SIZE( type, field ) ( sizeof( ( (type*)0 )->field ) )
#define CF_SIZE_OF_OP_PARAM( field ) \
@ -34,6 +35,7 @@ Q_LOGGING_CATEGORY(lcCfApiWrapper, "nextcloud.sync.vfs.cfapi.wrapper", QtInfoMsg
FIELD_SIZE( CF_OPERATION_PARAMETERS, field ) )
namespace {
constexpr auto syncRootFlagsFull = 34;
constexpr auto syncRootFlagsNoCfApiContextMenu = 2;
@ -41,6 +43,9 @@ constexpr auto syncRootManagerRegKey = R"(SOFTWARE\Microsoft\Windows\CurrentVers
constexpr auto forbiddenLeadingCharacterInPath = "#";
}
QString createErrorMessageForPlaceholderUpdateAndCreate(const QString &path, const QString &originalErrorMessage)
{
const auto pathFromNativeSeparators = QDir::fromNativeSeparators(path);
@ -304,47 +309,44 @@ OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderStat
CfApiUpdateMetadataType updateType)
{
if (updateType == CfApiUpdateMetadataType::AllMetadata && modtime <= 0) {
return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(path).arg(modtime)};
}
return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(path).arg(modtime)};
}
const auto info = replacesPath.isEmpty() ? OCC::CfApiWrapper::findPlaceholderInfo(path)
: OCC::CfApiWrapper::findPlaceholderInfo(replacesPath);
if (!info) {
return { "Can't update non existing placeholder info" };
}
const auto info = replacesPath.isEmpty() ? OCC::CfApiWrapper::findPlaceholderInfo(path)
: OCC::CfApiWrapper::findPlaceholderInfo(replacesPath);
if (!info) {
return { "Can't update non existing placeholder info" };
}
const auto previousPinState = cfPinStateToPinState(info->PinState);
const auto fileIdentity = QString::fromUtf8(fileId).toStdWString();
const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t);
const auto previousPinState = cfPinStateToPinState(info->PinState);
CF_FS_METADATA metadata;
metadata.FileSize.QuadPart = size;
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.CreationTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.LastWriteTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.LastAccessTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.ChangeTime);
metadata.BasicInfo.FileAttributes = 0;
CF_FS_METADATA metadata;
metadata.FileSize.QuadPart = size;
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.CreationTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.LastWriteTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.LastAccessTime);
OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &metadata.BasicInfo.ChangeTime);
metadata.BasicInfo.FileAttributes = 0;
OCC::CfApiWrapper::setPinState(path, OCC::PinState::Unspecified, OCC::CfApiWrapper::SetPinRecurseMode::NoRecurse);
OCC::CfApiWrapper::setPinState(path, OCC::PinState::Unspecified, OCC::CfApiWrapper::SetPinRecurseMode::NoRecurse);
qCInfo(lcCfApiWrapper) << "updatePlaceholderState" << path << modtime;
const qint64 result = CfUpdatePlaceholder(OCC::CfApiWrapper::handleForPath(path).get(), updateType == CfApiUpdateMetadataType::AllMetadata ? &metadata : nullptr,
fileIdentity.data(), sizeToDWORD(fileIdentitySize),
nullptr, 0, CF_UPDATE_FLAG_MARK_IN_SYNC, nullptr, nullptr);
qCInfo(lcCfApiWrapper) << "updatePlaceholderState" << path << modtime;
const qint64 result =
CfUpdatePlaceholder(OCC::CfApiWrapper::handleForPath(path).get(), updateType == CfApiUpdateMetadataType::AllMetadata ? &metadata : nullptr,
fileId.data(), static_cast<DWORD>(fileId.size()), nullptr, 0, CF_UPDATE_FLAG_MARK_IN_SYNC, nullptr, nullptr);
if (result != S_OK) {
const auto errorMessage = createErrorMessageForPlaceholderUpdateAndCreate(path, "Couldn't update placeholder info");
qCWarning(lcCfApiWrapper) << errorMessage << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage()) << replacesPath;
return errorMessage;
}
if (result != S_OK) {
const auto errorMessage = createErrorMessageForPlaceholderUpdateAndCreate(path, "Couldn't update placeholder info");
qCWarning(lcCfApiWrapper) << errorMessage << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage()) << replacesPath;
return errorMessage;
}
// Pin state tends to be lost on updates, so restore it every time
if (!setPinState(path, previousPinState, OCC::CfApiWrapper::NoRecurse)) {
return { "Couldn't restore pin state" };
}
// Pin state tends to be lost on updates, so restore it every time
if (!setPinState(path, previousPinState, OCC::CfApiWrapper::NoRecurse)) {
return { "Couldn't restore pin state" };
}
return OCC::Vfs::ConvertToPlaceholderResult::Ok;
}
return OCC::Vfs::ConvertToPlaceholderResult::Ok;
}
void CALLBACK cfApiCancelFetchData(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS * /*callbackParameters*/)
@ -433,11 +435,6 @@ CF_CALLBACK_REGISTRATION cfApiCallbacks[] = {
CF_CALLBACK_REGISTRATION_END
};
void deletePlaceholderInfo(CF_PLACEHOLDER_BASIC_INFO *info)
{
auto byte = reinterpret_cast<char *>(info);
delete[] byte;
}
std::wstring pathForHandle(const OCC::CfApiWrapper::FileHandle &handle)
{
@ -496,24 +493,18 @@ OCC::CfApiWrapper::FileHandle::FileHandle(void *data, Deleter deleter)
{
}
OCC::CfApiWrapper::PlaceHolderInfo::PlaceHolderInfo()
: _data(nullptr, [](CF_PLACEHOLDER_BASIC_INFO *) {})
{
}
OCC::CfApiWrapper::PlaceHolderInfo::PlaceHolderInfo(CF_PLACEHOLDER_BASIC_INFO *data, Deleter deleter)
: _data(data, deleter)
OCC::CfApiWrapper::PlaceHolderInfo::PlaceHolderInfo(std::vector<char> &&buffer)
: _data(buffer)
{
}
OCC::Optional<OCC::PinState> OCC::CfApiWrapper::PlaceHolderInfo::pinState() const
{
Q_ASSERT(_data);
if (!_data) {
if (!this) {
return {};
}
return cfPinStateToPinState(_data->PinState);
return cfPinStateToPinState(get()->PinState);
}
QString convertSidToStringSid(void *sid)
@ -665,7 +656,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::registerSyncRoot(const QString &pa
const auto name = std::wstring(providerName.toStdWString().data());
const auto version = std::wstring(providerVersion.toStdWString().data());
CF_SYNC_REGISTRATION info;
CF_SYNC_REGISTRATION info = {};
info.StructSize = static_cast<ULONG>(sizeof(info) + (name.length() + version.length()) * sizeof(wchar_t));
info.ProviderName = name.data();
info.ProviderVersion = version.data();
@ -829,16 +820,22 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
OCC::CfApiWrapper::PlaceHolderInfo OCC::CfApiWrapper::findPlaceholderInfo(const QString &path)
{
constexpr auto fileIdMaxLength = 128;
const auto infoSize = sizeof(CF_PLACEHOLDER_BASIC_INFO) + fileIdMaxLength;
auto info = PlaceHolderInfo(reinterpret_cast<CF_PLACEHOLDER_BASIC_INFO *>(new char[infoSize]), deletePlaceholderInfo);
const qint64 result = CfGetPlaceholderInfo(handleForPath(path).get(), CF_PLACEHOLDER_INFO_BASIC, info.get(), sizeToDWORD(infoSize), nullptr);
if (result == S_OK) {
return info;
} else {
return {};
if (auto handle = handleForPath(path)) {
std::vector<char> buffer(512);
DWORD actualSize = {};
const qint64 result = CfGetPlaceholderInfo(handle.get(), CF_PLACEHOLDER_INFO_BASIC, buffer.data(), static_cast<DWORD>(buffer.size()), &actualSize);
if (result == S_OK) {
buffer.resize(actualSize);
return PlaceHolderInfo(std::move(buffer));
} else if (result == HRESULT_FROM_WIN32(ERROR_NOT_A_CLOUD_FILE)) {
// native file, not yet converted
return {};
} else {
qCWarning(lcCfApiWrapper) << "Failed to retrieve placeholder info:" << Utility::formatWinError(result);
Q_ASSERT(false);
}
}
return {};
}
OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::setPinState(const QString &path, OCC::PinState state, SetPinRecurseMode mode)
@ -865,12 +862,9 @@ OCC::Result<void, QString> OCC::CfApiWrapper::createPlaceholderInfo(const QStrin
const auto localBasePath = QDir::toNativeSeparators(fileInfo.path()).toStdWString();
const auto relativePath = fileInfo.fileName().toStdWString();
const auto fileIdentity = QString::fromUtf8(fileId).toStdWString();
CF_PLACEHOLDER_CREATE_INFO cloudEntry;
cloudEntry.FileIdentity = fileIdentity.data();
const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t);
cloudEntry.FileIdentityLength = sizeToDWORD(fileIdentitySize);
CF_PLACEHOLDER_CREATE_INFO cloudEntry = {};
cloudEntry.FileIdentity = fileId.data();
cloudEntry.FileIdentityLength = static_cast<DWORD>(fileId.length());
cloudEntry.RelativeFileName = relativePath.data();
cloudEntry.Flags = CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC;
@ -915,9 +909,6 @@ OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::de
return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(path).arg(modtime)};
}
const auto fileIdentity = QString::fromUtf8(fileId).toStdWString();
const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t);
const auto info = findPlaceholderInfo(path);
if (info) {
setPinState(path, OCC::PinState::OnlineOnly, OCC::CfApiWrapper::NoRecurse);
@ -926,27 +917,16 @@ OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::de
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);
const qint64 result = CfUpdatePlaceholder(handleForPath(path).get(), nullptr, fileId.data(), static_cast<DWORD>(fileId.size()), &dehydrationRange, 1,
CF_UPDATE_FLAG_MARK_IN_SYNC | CF_UPDATE_FLAG_DEHYDRATE, nullptr, nullptr);
if (result != S_OK) {
const auto errorMessage = createErrorMessageForPlaceholderUpdateAndCreate(path, "Couldn't update placeholder info");
qCWarning(lcCfApiWrapper) << errorMessage << path << ":" << QString::fromWCharArray(_com_error(result).ErrorMessage());
return errorMessage;
}
} else {
const qint64 result = CfConvertToPlaceholder(handleForPath(path).get(),
fileIdentity.data(),
sizeToDWORD(fileIdentitySize),
CF_CONVERT_FLAG_MARK_IN_SYNC | CF_CONVERT_FLAG_DEHYDRATE,
nullptr,
nullptr);
const qint64 result = CfConvertToPlaceholder(handleForPath(path).get(), fileId.data(), static_cast<DWORD>(fileId.size()),
CF_CONVERT_FLAG_MARK_IN_SYNC | CF_CONVERT_FLAG_DEHYDRATE, nullptr, nullptr);
if (result != S_OK) {
const auto errorMessage = createErrorMessageForPlaceholderUpdateAndCreate(path, "Couldn't convert to placeholder");
@ -963,9 +943,8 @@ OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> OCC::CfApiWrapper::co
Q_UNUSED(modtime);
Q_UNUSED(size);
const auto fileIdentity = QString::fromUtf8(fileId).toStdWString();
const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t);
const qint64 result = CfConvertToPlaceholder(handleForPath(path).get(), fileIdentity.data(), sizeToDWORD(fileIdentitySize), CF_CONVERT_FLAG_MARK_IN_SYNC, nullptr, nullptr);
const qint64 result =
CfConvertToPlaceholder(handleForPath(path).get(), fileId.data(), static_cast<DWORD>(fileId.size()), CF_CONVERT_FLAG_MARK_IN_SYNC, nullptr, nullptr);
Q_ASSERT(result == S_OK);
if (result != S_OK) {
const auto errorMessage = createErrorMessageForPlaceholderUpdateAndCreate(path, "Couldn't convert to placeholder");

View File

@ -4,13 +4,19 @@
*/
#pragma once
#include <memory>
// include order is important, this must be included before cfapi
#include <windows.h>
#include <winternl.h>
#include <cfapi.h>
#include "cfapiexport.h"
#include "common/pinstate.h"
#include "common/result.h"
#include "common/vfs.h"
#include <memory>
struct CF_PLACEHOLDER_BASIC_INFO;
namespace OCC {
@ -48,19 +54,18 @@ private:
class NEXTCLOUD_CFAPI_EXPORT PlaceHolderInfo
{
public:
using Deleter = void (*)(CF_PLACEHOLDER_BASIC_INFO *);
PlaceHolderInfo(std::vector<char> &&buffer = {});
PlaceHolderInfo();
PlaceHolderInfo(CF_PLACEHOLDER_BASIC_INFO *data, Deleter deleter);
inline auto *get() const noexcept { return reinterpret_cast<CF_PLACEHOLDER_BASIC_INFO *>(const_cast<char *>(_data.data())); }
inline auto *operator->() const noexcept { return get(); }
inline explicit operator bool() const noexcept { return !_data.empty(); }
inline CF_PLACEHOLDER_BASIC_INFO *get() const noexcept { return _data.get(); }
inline CF_PLACEHOLDER_BASIC_INFO *operator->() const noexcept { return _data.get(); }
inline explicit operator bool() const noexcept { return static_cast<bool>(_data); }
inline auto size() const { return _data.size(); }
Optional<PinState> pinState() const;
private:
std::unique_ptr<CF_PLACEHOLDER_BASIC_INFO, Deleter> _data;
std::vector<char> _data;
};
NEXTCLOUD_CFAPI_EXPORT Result<void, QString> registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion, const QString &folderAlias, const QString &navigationPaneClsid, const QString &displayName, const QString &accountDisplayName);