diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp index 076ac21679..b76cb39b45 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.cpp +++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp @@ -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 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(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(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 &&buffer) + : _data(buffer) { } OCC::Optional 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 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(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(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 buffer(512); + DWORD actualSize = {}; + const qint64 result = CfGetPlaceholderInfo(handle.get(), CF_PLACEHOLDER_INFO_BASIC, buffer.data(), static_cast(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::CfApiWrapper::setPinState(const QString &path, OCC::PinState state, SetPinRecurseMode mode) @@ -865,12 +862,9 @@ OCC::Result 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(fileId.length()); cloudEntry.RelativeFileName = relativePath.data(); cloudEntry.Flags = CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC; @@ -915,9 +909,6 @@ 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 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::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(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(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::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(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"); diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.h b/src/libsync/vfs/cfapi/cfapiwrapper.h index 0e0d4458fc..9db1083da6 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.h +++ b/src/libsync/vfs/cfapi/cfapiwrapper.h @@ -4,13 +4,19 @@ */ #pragma once -#include +// include order is important, this must be included before cfapi +#include +#include + +#include #include "cfapiexport.h" #include "common/pinstate.h" #include "common/result.h" #include "common/vfs.h" +#include + 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 &&buffer = {}); - PlaceHolderInfo(); - PlaceHolderInfo(CF_PLACEHOLDER_BASIC_INFO *data, Deleter deleter); + inline auto *get() const noexcept { return reinterpret_cast(const_cast(_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(_data); } + inline auto size() const { return _data.size(); } Optional pinState() const; private: - std::unique_ptr _data; + std::vector _data; }; NEXTCLOUD_CFAPI_EXPORT Result registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion, const QString &folderAlias, const QString &navigationPaneClsid, const QString &displayName, const QString &accountDisplayName);