From: Kevin Ottens Date: Wed, 23 Dec 2020 16:38:05 +0000 (+0100) Subject: Split the CfAPI lower level code in a wrapper X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~21^2~447^2~5 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=7668c521c1f018baa4a68ad65db631fd4b7198bd;p=nextcloud-desktop.git Split the CfAPI lower level code in a wrapper Signed-off-by: Kevin Ottens --- diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt index 1f1dda281..aa482accc 100644 --- a/src/libsync/CMakeLists.txt +++ b/src/libsync/CMakeLists.txt @@ -62,7 +62,10 @@ set(libsync_SRCS ) if (WIN32) - set (libsync_SRCS ${libsync_SRCS} vfs/cfapi/vfs_cfapi.cpp) + set(libsync_SRCS ${libsync_SRCS} + vfs/cfapi/cfapiwrapper.cpp + vfs/cfapi/vfs_cfapi.cpp + ) add_definitions(-D_WIN32_WINNT=_WIN32_WINNT_WIN10) list(APPEND OS_SPECIFIC_LINK_LIBRARIES cldapi) endif() diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp new file mode 100644 index 000000000..c2c0ecca2 --- /dev/null +++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (C) by Kevin Ottens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "cfapiwrapper.h" + +#include "common/utility.h" + +#include +#include +#include + +#include +#include + +Q_LOGGING_CATEGORY(lcCfApiWrapper, "nextcloud.sync.vfs.cfapi.wrapper", QtInfoMsg) + +namespace { +void CALLBACK cfApiFetchDataCallback(_In_ CONST CF_CALLBACK_INFO* callbackInfo, _In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters) +{ + qCCritical(lcCfApiWrapper()) << "Got in!"; + Q_ASSERT(false); +} + +CF_CALLBACK_REGISTRATION cfApiCallbacks[] = { + { CF_CALLBACK_TYPE_FETCH_DATA, cfApiFetchDataCallback }, + CF_CALLBACK_REGISTRATION_END +}; + +DWORD sizeToDWORD(size_t size) +{ + return OCC::Utility::convertSizeToDWORD(size); +} + +void deletePlaceholderInfo(CF_PLACEHOLDER_BASIC_INFO *info) +{ + auto byte = reinterpret_cast(info); + delete[] byte; +} + +std::wstring pathForHandle(const OCC::CfApiWrapper::FileHandle &handle) +{ + wchar_t buffer[MAX_PATH]; + const qint64 result = GetFinalPathNameByHandle(handle.get(), buffer, MAX_PATH, VOLUME_NAME_DOS); + Q_ASSERT(result < MAX_PATH); + return std::wstring(buffer); +} + +OCC::PinState cfPinStateToPinState(CF_PIN_STATE state) +{ + switch (state) { + case CF_PIN_STATE_UNSPECIFIED: + return OCC::PinState::Unspecified; + case CF_PIN_STATE_PINNED: + return OCC::PinState::AlwaysLocal; + case CF_PIN_STATE_UNPINNED: + return OCC::PinState::OnlineOnly; + case CF_PIN_STATE_INHERIT: + return OCC::PinState::Inherited; + default: + Q_UNREACHABLE(); + return OCC::PinState::Inherited; + } +} + +CF_PIN_STATE pinStateToCfPinState(OCC::PinState state) +{ + switch (state) { + case OCC::PinState::Inherited: + return CF_PIN_STATE_INHERIT; + case OCC::PinState::AlwaysLocal: + return CF_PIN_STATE_PINNED; + case OCC::PinState::OnlineOnly: + return CF_PIN_STATE_UNPINNED; + case OCC::PinState::Unspecified: + return CF_PIN_STATE_UNSPECIFIED; + default: + Q_UNREACHABLE(); + return CF_PIN_STATE_UNSPECIFIED; + } +} + +CF_SET_PIN_FLAGS pinRecurseModeToCfSetPinFlags(OCC::CfApiWrapper::SetPinRecurseMode mode) +{ + switch (mode) { + case OCC::CfApiWrapper::NoRecurse: + return CF_SET_PIN_FLAG_NONE; + case OCC::CfApiWrapper::Recurse: + return CF_SET_PIN_FLAG_RECURSE; + case OCC::CfApiWrapper::ChildrenOnly: + return CF_SET_PIN_FLAG_RECURSE_ONLY; + default: + Q_UNREACHABLE(); + return CF_SET_PIN_FLAG_NONE; + } +} + +} + +OCC::CfApiWrapper::ConnectionKey::ConnectionKey() + : _data(new CF_CONNECTION_KEY, [](void *p) { delete reinterpret_cast(p); }) +{ +} + +OCC::CfApiWrapper::FileHandle::FileHandle() + : _data(nullptr, [](void *) {}) +{ +} + +OCC::CfApiWrapper::FileHandle::FileHandle(void *data, Deleter deleter) + : _data(data, 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::Optional OCC::CfApiWrapper::PlaceHolderInfo::pinState() const +{ + Q_ASSERT(_data); + if (!_data) { + return {}; + } + + return cfPinStateToPinState(_data->PinState); +} + +OCC::Result OCC::CfApiWrapper::registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion) +{ + const auto p = path.toStdWString(); + const auto name = providerName.toStdWString(); + const auto version = providerVersion.toStdWString(); + + CF_SYNC_REGISTRATION info; + info.ProviderName = name.data(); + info.ProviderVersion = version.data(); + info.SyncRootIdentity = nullptr; + info.SyncRootIdentityLength = 0; + info.FileIdentity = nullptr; + info.FileIdentityLength = 0; + + CF_SYNC_POLICIES policies; + policies.Hydration.Primary = CF_HYDRATION_POLICY_FULL; + policies.Hydration.Modifier = CF_HYDRATION_POLICY_MODIFIER_NONE; + policies.Population.Primary = CF_POPULATION_POLICY_ALWAYS_FULL; + policies.Population.Modifier = CF_POPULATION_POLICY_MODIFIER_NONE; + policies.InSync = CF_INSYNC_POLICY_PRESERVE_INSYNC_FOR_SYNC_ENGINE; + policies.HardLink = CF_HARDLINK_POLICY_NONE; + + const qint64 result = CfRegisterSyncRoot(p.data(), &info, &policies, CF_REGISTER_FLAG_UPDATE); + Q_ASSERT(result == S_OK); + if (result != S_OK) { + return QString::fromWCharArray(_com_error(result).ErrorMessage()); + } else { + return {}; + } +} + +OCC::Result OCC::CfApiWrapper::unegisterSyncRoot(const QString &path) +{ + const auto p = path.toStdWString(); + const qint64 result = CfUnregisterSyncRoot(p.data()); + Q_ASSERT(result == S_OK); + if (result != S_OK) { + return QString::fromWCharArray(_com_error(result).ErrorMessage()); + } else { + return {}; + } +} + +OCC::Result OCC::CfApiWrapper::connectSyncRoot(const QString &path, OCC::VfsCfApi *context) +{ + auto key = ConnectionKey(); + const auto p = path.toStdWString(); + const qint64 result = CfConnectSyncRoot(p.data(), + cfApiCallbacks, + context, + CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO | CF_CONNECT_FLAG_REQUIRE_FULL_FILE_PATH, + static_cast(key.get())); + Q_ASSERT(result == S_OK); + if (result != S_OK) { + return QString::fromWCharArray(_com_error(result).ErrorMessage()); + } else { + return key; + } +} + +OCC::Result OCC::CfApiWrapper::disconnectSyncRoot(ConnectionKey &&key) +{ + const qint64 result = CfDisconnectSyncRoot(*static_cast(key.get())); + Q_ASSERT(result == S_OK); + if (result != S_OK) { + return QString::fromWCharArray(_com_error(result).ErrorMessage()); + } else { + return {}; + } +} + +bool OCC::CfApiWrapper::isSparseFile(const QString &path) +{ + const auto p = path.toStdWString(); + const auto attributes = GetFileAttributes(p.data()); + return (attributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0; +} + +OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &path) +{ + if (QFileInfo(path).isDir()) { + HANDLE handle = nullptr; + const qint64 openResult = CfOpenFileWithOplock(path.toStdWString().data(), CF_OPEN_FILE_FLAG_NONE, &handle); + if (openResult == S_OK) { + return {handle, CfCloseHandle}; + } + } else { + const auto handle = CreateFile(path.toStdWString().data(), 0, 0, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (handle != INVALID_HANDLE_VALUE) { + return {handle, [](HANDLE h) { CloseHandle(h); }}; + } + } + + return {}; +} + +OCC::CfApiWrapper::PlaceHolderInfo OCC::CfApiWrapper::findPlaceholderInfo(const FileHandle &handle) +{ + Q_ASSERT(handle); + + 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(handle.get(), CF_PLACEHOLDER_INFO_BASIC, info.get(), sizeToDWORD(infoSize), nullptr); + + if (result == S_OK) { + return info; + } else { + return {}; + } +} + +OCC::Result OCC::CfApiWrapper::setPinState(const FileHandle &handle, PinState state, SetPinRecurseMode mode) +{ + const auto cfState = pinStateToCfPinState(state); + const auto flags = pinRecurseModeToCfSetPinFlags(mode); + + const qint64 result = CfSetPinState(handle.get(), cfState, flags, nullptr); + if (result == S_OK) { + return {}; + } else { + qCWarning(lcCfApiWrapper) << "Couldn't set pin state" << state << "for" << pathForHandle(handle) << "with recurse mode" << mode << ":" << _com_error(result).ErrorMessage(); + return "Couldn't set pin state"; + } +} + +OCC::Result OCC::CfApiWrapper::createPlaceholderInfo(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId) +{ + const auto fileInfo = QFileInfo(path); + 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); + + cloudEntry.RelativeFileName = relativePath.data(); + cloudEntry.Flags = CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC; + cloudEntry.FsMetadata.FileSize.QuadPart = size; + cloudEntry.FsMetadata.BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; + OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.CreationTime); + OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.LastWriteTime); + OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.LastAccessTime); + OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.ChangeTime); + + if (fileInfo.isDir()) { + cloudEntry.Flags |= CF_PLACEHOLDER_CREATE_FLAG_DISABLE_ON_DEMAND_POPULATION; + cloudEntry.FsMetadata.BasicInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + cloudEntry.FsMetadata.FileSize.QuadPart = 0; + } + + const qint64 result = CfCreatePlaceholders(localBasePath.data(), &cloudEntry, 1, CF_CREATE_FLAG_NONE, nullptr); + if (result != S_OK) { + qCWarning(lcCfApiWrapper) << "Couldn't create placeholder info for" << path << ":" << _com_error(result).ErrorMessage(); + return "Couldn't create placeholder info"; + } + + const auto parentHandle = handleForPath(QDir::toNativeSeparators(QFileInfo(path).absolutePath())); + const auto parentInfo = findPlaceholderInfo(parentHandle); + const auto state = parentInfo && parentInfo->PinState == CF_PIN_STATE_UNPINNED ? CF_PIN_STATE_UNPINNED : CF_PIN_STATE_INHERIT; + + const auto handle = handleForPath(path); + if (!setPinState(handle, cfPinStateToPinState(state), NoRecurse)) { + return "Couldn't set the default inherit pin state"; + } + + return {}; +} + +OCC::Result OCC::CfApiWrapper::updatePlaceholderInfo(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath) +{ + Q_ASSERT(handle); + + const auto info = replacesPath.isEmpty() ? findPlaceholderInfo(handle) + : findPlaceholderInfo(handleForPath(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); + + 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); + + const qint64 result = CfUpdatePlaceholder(handle.get(), &metadata, + fileIdentity.data(), sizeToDWORD(fileIdentitySize), + nullptr, 0, CF_UPDATE_FLAG_NONE, nullptr, nullptr); + + if (result != S_OK) { + qCWarning(lcCfApiWrapper) << "Couldn't update placeholder info for" << pathForHandle(handle) << ":" << _com_error(result).ErrorMessage(); + return "Couldn't update placeholder info"; + } + + // Pin state tends to be lost on updates, so restore it every time + if (!setPinState(handle, previousPinState, NoRecurse)) { + return "Couldn't restore pin state"; + } + + return {}; +} + +OCC::Result OCC::CfApiWrapper::convertToPlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath) +{ + Q_ASSERT(handle); + + const auto fileIdentity = QString::fromUtf8(fileId).toStdWString(); + const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t); + const qint64 result = CfConvertToPlaceholder(handle.get(), fileIdentity.data(), sizeToDWORD(fileIdentitySize), CF_CONVERT_FLAG_NONE, nullptr, nullptr); + Q_ASSERT(result == S_OK); + if (result != S_OK) { + qCCritical(lcCfApiWrapper) << "Couldn't convert to placeholder" << pathForHandle(handle) << ":" << _com_error(result).ErrorMessage(); + return "Couldn't convert to placeholder"; + } + + const auto originalHandle = handleForPath(replacesPath); + const auto originalInfo = originalHandle ? findPlaceholderInfo(originalHandle) : PlaceHolderInfo(nullptr, deletePlaceholderInfo); + if (!originalInfo) { + const auto stateResult = setPinState(handle, PinState::Inherited, NoRecurse); + Q_ASSERT(stateResult); + return stateResult; + } else { + const auto state = cfPinStateToPinState(originalInfo->PinState); + const auto stateResult = setPinState(handle, state, NoRecurse); + Q_ASSERT(stateResult); + return stateResult; + } +} diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.h b/src/libsync/vfs/cfapi/cfapiwrapper.h new file mode 100644 index 000000000..888242bf1 --- /dev/null +++ b/src/libsync/vfs/cfapi/cfapiwrapper.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) by Kevin Ottens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#pragma once + +#include + +#include "owncloudlib.h" +#include "common/pinstate.h" +#include "common/result.h" + +struct CF_PLACEHOLDER_BASIC_INFO; + +namespace OCC { + +class VfsCfApi; + +namespace CfApiWrapper +{ + +class OWNCLOUDSYNC_EXPORT ConnectionKey +{ +public: + ConnectionKey(); + inline void *get() const { return _data.get(); } + +private: + std::unique_ptr _data; +}; + +class OWNCLOUDSYNC_EXPORT FileHandle +{ +public: + using Deleter = void (*)(void *); + + FileHandle(); + FileHandle(void *data, Deleter deleter); + + inline void *get() const { return _data.get(); } + inline explicit operator bool() const noexcept { return static_cast(_data); } + +private: + std::unique_ptr _data; +}; + +class OWNCLOUDSYNC_EXPORT PlaceHolderInfo +{ +public: + using Deleter = void (*)(CF_PLACEHOLDER_BASIC_INFO *); + + PlaceHolderInfo(); + PlaceHolderInfo(CF_PLACEHOLDER_BASIC_INFO *data, Deleter deleter); + + 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); } + + Optional pinState() const; + +private: + std::unique_ptr _data; +}; + +OWNCLOUDSYNC_EXPORT Result registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion); +OWNCLOUDSYNC_EXPORT Result unegisterSyncRoot(const QString &path); + +OWNCLOUDSYNC_EXPORT Result connectSyncRoot(const QString &path, VfsCfApi *context); +OWNCLOUDSYNC_EXPORT Result disconnectSyncRoot(ConnectionKey &&key); + +OWNCLOUDSYNC_EXPORT bool isSparseFile(const QString &path); + +OWNCLOUDSYNC_EXPORT FileHandle handleForPath(const QString &path); + +OWNCLOUDSYNC_EXPORT PlaceHolderInfo findPlaceholderInfo(const FileHandle &handle); + +enum SetPinRecurseMode { + NoRecurse = 0, + Recurse, + ChildrenOnly +}; + +OWNCLOUDSYNC_EXPORT Result setPinState(const FileHandle &handle, PinState state, SetPinRecurseMode mode); +OWNCLOUDSYNC_EXPORT Result createPlaceholderInfo(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId); +OWNCLOUDSYNC_EXPORT Result updatePlaceholderInfo(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath = QString()); +OWNCLOUDSYNC_EXPORT Result convertToPlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath); + +} + +} // namespace OCC diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.cpp b/src/libsync/vfs/cfapi/vfs_cfapi.cpp index c43efcbf4..4268f51b6 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.cpp +++ b/src/libsync/vfs/cfapi/vfs_cfapi.cpp @@ -17,6 +17,7 @@ #include #include +#include "cfapiwrapper.h" #include "syncfileitem.h" #include "filesystem.h" #include "common/syncjournaldb.h" @@ -26,192 +27,8 @@ Q_LOGGING_CATEGORY(lcCfApi, "nextcloud.sync.vfs.cfapi", QtInfoMsg) -namespace { -void CALLBACK cfApiFetchDataCallback(_In_ CONST CF_CALLBACK_INFO* callbackInfo, _In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters) -{ - qCCritical(lcCfApi) << "Got in!"; - Q_ASSERT(false); -} - -CF_CALLBACK_REGISTRATION cfApiCallbacks[] = { - { CF_CALLBACK_TYPE_FETCH_DATA, cfApiFetchDataCallback }, - CF_CALLBACK_REGISTRATION_END -}; - -std::unique_ptr handleForPath(const QString &path) -{ - if (QFileInfo(path).isDir()) { - HANDLE handle = nullptr; - const qint64 openResult = CfOpenFileWithOplock(path.toStdWString().data(), CF_OPEN_FILE_FLAG_NONE, &handle); - if (openResult == S_OK) { - return {handle, CfCloseHandle}; - } - } else { - const auto handle = CreateFile(path.toStdWString().data(), 0, 0, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (handle != INVALID_HANDLE_VALUE) { - return {handle, [](HANDLE h) { CloseHandle(h); }}; - } - } - - return {nullptr, [](HANDLE){}}; -} - -DWORD sizeToDWORD(size_t size) -{ - return OCC::Utility::convertSizeToDWORD(size); -} - -void deletePlaceholderInfo(CF_PLACEHOLDER_BASIC_INFO *info) -{ - auto byte = reinterpret_cast(info); - delete[] byte; -} - -std::unique_ptr findPlaceholderInfo(const QString &path) -{ - auto handle = handleForPath(path); - if (!handle) { - return {nullptr, deletePlaceholderInfo}; - } - - constexpr auto fileIdMaxLength = 128; - const auto infoSize = sizeof(CF_PLACEHOLDER_BASIC_INFO) + fileIdMaxLength; - std::unique_ptr info(reinterpret_cast(new char[infoSize]), deletePlaceholderInfo); - const qint64 result = CfGetPlaceholderInfo(handle.get(), CF_PLACEHOLDER_INFO_BASIC, info.get(), sizeToDWORD(infoSize), nullptr); - - if (result == S_OK) { - return info; - } else { - return {nullptr, deletePlaceholderInfo}; - } -} - -bool setPinState(const QString &path, CF_PIN_STATE state, CF_SET_PIN_FLAGS flags) -{ - if (!findPlaceholderInfo(path)) { - return false; - } - - const auto handle = handleForPath(path); - if (!handle) { - return false; - } - - const qint64 result = CfSetPinState(handle.get(), state, flags, nullptr); - if (result != S_OK) { - qCWarning(lcCfApi) << "Couldn't set pin state for" << path << ":" << _com_error(result).ErrorMessage(); - } - return result == S_OK; -} - -OCC::Result createPlaceholderInfo(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId) -{ - const auto fileInfo = QFileInfo(path); - 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); - - cloudEntry.RelativeFileName = relativePath.data(); - cloudEntry.Flags = CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC; - cloudEntry.FsMetadata.FileSize.QuadPart = size; - cloudEntry.FsMetadata.BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; - OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.CreationTime); - OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.LastWriteTime); - OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.LastAccessTime); - OCC::Utility::UnixTimeToLargeIntegerFiletime(modtime, &cloudEntry.FsMetadata.BasicInfo.ChangeTime); - - if (fileInfo.isDir()) { - cloudEntry.Flags |= CF_PLACEHOLDER_CREATE_FLAG_DISABLE_ON_DEMAND_POPULATION; - cloudEntry.FsMetadata.BasicInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; - cloudEntry.FsMetadata.FileSize.QuadPart = 0; - } - - const qint64 result = CfCreatePlaceholders(localBasePath.data(), &cloudEntry, 1, CF_CREATE_FLAG_NONE, nullptr); - if (result != S_OK) { - qCWarning(lcCfApi) << "Couldn't create placeholder info for" << path << ":" << _com_error(result).ErrorMessage(); - return "Couldn't create placeholder info"; - } - - const auto parentInfo = findPlaceholderInfo(QDir::toNativeSeparators(QFileInfo(path).absolutePath())); - const auto state = parentInfo && parentInfo->PinState == CF_PIN_STATE_UNPINNED ? CF_PIN_STATE_UNPINNED : CF_PIN_STATE_INHERIT; - - if (!setPinState(path, state, CF_SET_PIN_FLAG_NONE)) { - return "Couldn't set the default inherit pin state"; - } - - return {}; -} - -OCC::Result updatePlaceholderInfo(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath = QString()) -{ - auto info = findPlaceholderInfo(replacesPath.isEmpty() ? path : replacesPath); - if (!info) { - return "Can't update non existing placeholder info"; - } - - const auto previousPinState = info->PinState; - - auto handle = handleForPath(path); - if (!handle) { - return "Can't update placeholder info for non existing file"; - } - - const auto fileIdentity = QString::fromUtf8(fileId).toStdWString(); - const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t); - - 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); - - const qint64 result = CfUpdatePlaceholder(handle.get(), &metadata, - fileIdentity.data(), sizeToDWORD(fileIdentitySize), - nullptr, 0, CF_UPDATE_FLAG_NONE, nullptr, nullptr); - - if (result != S_OK) { - qCWarning(lcCfApi) << "Couldn't update placeholder info for" << path << ":" << _com_error(result).ErrorMessage(); - return "Couldn't update placeholder info"; - } - - // Pin state tends to be lost on updates, so restore it every time - if (!setPinState(path, previousPinState, CF_SET_PIN_FLAG_NONE)) { - return "Couldn't restore pin state"; - } - - return {}; -} - -void convertToPlaceholder(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath) -{ - auto handle = handleForPath(path); - Q_ASSERT(handle); - - const auto fileIdentity = QString::fromUtf8(fileId).toStdWString(); - const auto fileIdentitySize = (fileIdentity.length() + 1) * sizeof(wchar_t); - const qint64 result = CfConvertToPlaceholder(handle.get(), fileIdentity.data(), sizeToDWORD(fileIdentitySize), CF_CONVERT_FLAG_NONE, nullptr, nullptr); - Q_ASSERT(result == S_OK); - if (result != S_OK) { - qCCritical(lcCfApi) << "Couldn't convert to placeholder" << path << ":" << _com_error(result).ErrorMessage(); - return; - } - - const auto originalInfo = findPlaceholderInfo(replacesPath); - if (!originalInfo) { - const auto stateResult = setPinState(path, CF_PIN_STATE_INHERIT, CF_SET_PIN_FLAG_NONE); - Q_ASSERT(stateResult); - } else { - const auto stateResult = setPinState(path, originalInfo->PinState, CF_SET_PIN_FLAG_NONE); - Q_ASSERT(stateResult); - } +namespace cfapi { +using namespace OCC::CfApiWrapper; } namespace OCC { @@ -219,7 +36,7 @@ namespace OCC { class VfsCfApiPrivate { public: - CF_CONNECTION_KEY callbackConnectionKey = {}; + cfapi::ConnectionKey connectionKey; }; VfsCfApi::VfsCfApi(QObject *parent) @@ -244,53 +61,36 @@ void VfsCfApi::startImpl(const VfsSetupParams ¶ms) { const auto localPath = QDir::toNativeSeparators(params.filesystemPath); - const auto providerName = params.providerName.toStdWString(); - const auto providerVersion = params.providerVersion.toStdWString(); - - CF_SYNC_REGISTRATION info; - info.ProviderName = providerName.data(); - info.ProviderVersion = providerVersion.data(); - info.SyncRootIdentity = nullptr; - info.SyncRootIdentityLength = 0; - info.FileIdentity = nullptr; - info.FileIdentityLength = 0; - - CF_SYNC_POLICIES policies; - policies.Hydration.Primary = CF_HYDRATION_POLICY_FULL; - policies.Hydration.Modifier = CF_HYDRATION_POLICY_MODIFIER_NONE; - policies.Population.Primary = CF_POPULATION_POLICY_ALWAYS_FULL; - policies.Population.Modifier = CF_POPULATION_POLICY_MODIFIER_NONE; - policies.InSync = CF_INSYNC_POLICY_PRESERVE_INSYNC_FOR_SYNC_ENGINE; - policies.HardLink = CF_HARDLINK_POLICY_NONE; - - const qint64 registerResult = CfRegisterSyncRoot(localPath.toStdWString().data(), &info, &policies, CF_REGISTER_FLAG_UPDATE); - Q_ASSERT(registerResult == S_OK); - if (registerResult != S_OK) { - qCCritical(lcCfApi) << "Initialization failed, couldn't register sync root:" << _com_error(registerResult).ErrorMessage(); + const auto registerResult = cfapi::registerSyncRoot(localPath, params.providerName, params.providerVersion); + if (!registerResult) { + qCCritical(lcCfApi) << "Initialization failed, couldn't register sync root:" << registerResult.error(); return; } - - const qint64 connectResult = CfConnectSyncRoot(localPath.toStdWString().data(), - cfApiCallbacks, - nullptr, - CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO | CF_CONNECT_FLAG_REQUIRE_FULL_FILE_PATH, - &d->callbackConnectionKey); - Q_ASSERT(connectResult == S_OK); - if (connectResult != S_OK) { - qCCritical(lcCfApi) << "Initialization failed, couldn't connect sync root:" << _com_error(connectResult).ErrorMessage(); + auto connectResult = cfapi::connectSyncRoot(localPath, this); + if (!connectResult) { + qCCritical(lcCfApi) << "Initialization failed, couldn't connect sync root:" << connectResult.error(); + return; } + + d->connectionKey = *std::move(connectResult); } void VfsCfApi::stop() { - CfDisconnectSyncRoot(d->callbackConnectionKey); + const auto result = cfapi::disconnectSyncRoot(std::move(d->connectionKey)); + if (!result) { + qCCritical(lcCfApi) << "Disconnect failed for" << QDir::toNativeSeparators(params().filesystemPath) << ":" << result.error(); + } } void VfsCfApi::unregisterFolder() { - const auto localPath = QDir::toNativeSeparators(params().filesystemPath).toStdWString(); - CfUnregisterSyncRoot(localPath.data()); + const auto localPath = QDir::toNativeSeparators(params().filesystemPath); + const auto result = cfapi::unegisterSyncRoot(localPath); + if (!result) { + qCCritical(lcCfApi) << "Unregistration failed for" << localPath << ":" << result.error(); + } } bool VfsCfApi::socketApiPinStateActionsShown() const @@ -306,14 +106,20 @@ bool VfsCfApi::isHydrating() const Result VfsCfApi::updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) { const auto localPath = QDir::toNativeSeparators(filePath); - return updatePlaceholderInfo(localPath, modtime, size, fileId); + const auto handle = cfapi::handleForPath(localPath); + if (handle) { + return cfapi::updatePlaceholderInfo(handle, modtime, size, fileId); + } else { + qCWarning(lcCfApi) << "Couldn't update metadata for non existing file" << localPath; + return "Couldn't update metadata"; + } } Result VfsCfApi::createPlaceholder(const SyncFileItem &item) { Q_ASSERT(params().filesystemPath.endsWith('/')); const auto localPath = QDir::toNativeSeparators(params().filesystemPath + item._file); - const auto result = createPlaceholderInfo(localPath, item._modtime, item._size, item._fileId); + const auto result = cfapi::createPlaceholderInfo(localPath, item._modtime, item._size, item._fileId); return result; } @@ -345,10 +151,12 @@ void VfsCfApi::convertToPlaceholder(const QString &filename, const SyncFileItem { const auto localPath = QDir::toNativeSeparators(filename); const auto replacesPath = QDir::toNativeSeparators(replacesFile); - if (findPlaceholderInfo(localPath)) { - updatePlaceholderInfo(localPath, item._modtime, item._size, item._fileId, replacesPath); + + const auto handle = cfapi::handleForPath(localPath); + if (cfapi::findPlaceholderInfo(handle)) { + cfapi::updatePlaceholderInfo(handle, item._modtime, item._size, item._fileId, replacesPath); } else { - ::convertToPlaceholder(localPath, item._modtime, item._size, item._fileId, replacesPath); + cfapi::convertToPlaceholder(handle, item._modtime, item._size, item._fileId, replacesPath); } } @@ -359,9 +167,8 @@ bool VfsCfApi::needsMetadataUpdate(const SyncFileItem &item) bool VfsCfApi::isDehydratedPlaceholder(const QString &filePath) { - const auto path = QDir::toNativeSeparators(filePath).toStdWString(); - const auto attributes = GetFileAttributes(path.data()); - return (attributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0; + const auto path = QDir::toNativeSeparators(filePath); + return cfapi::isSparseFile(path); } bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData) @@ -398,50 +205,35 @@ bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData) bool VfsCfApi::setPinState(const QString &folderPath, PinState state) { const auto localPath = QDir::toNativeSeparators(params().filesystemPath + folderPath); - - auto cfState = CF_PIN_STATE_UNSPECIFIED; - switch (state) { - case PinState::AlwaysLocal: - cfState = CF_PIN_STATE_PINNED; - break; - case PinState::Inherited: - cfState = CF_PIN_STATE_INHERIT; - break; - case PinState::OnlineOnly: - cfState = CF_PIN_STATE_UNPINNED; - break; - case PinState::Unspecified: - cfState = CF_PIN_STATE_UNSPECIFIED; - break; - default: - Q_UNREACHABLE(); + const auto handle = cfapi::handleForPath(localPath); + if (handle) { + if (cfapi::setPinState(handle, state, cfapi::Recurse)) { + return true; + } else { + return false; + } + } else { + qCWarning(lcCfApi) << "Couldn't update pin state for non existing file" << localPath; return false; } - - return ::setPinState(localPath, cfState, CF_SET_PIN_FLAG_RECURSE); } Optional VfsCfApi::pinState(const QString &folderPath) { const auto localPath = QDir::toNativeSeparators(params().filesystemPath + folderPath); - const auto info = findPlaceholderInfo(localPath); - if (!info) { + const auto handle = cfapi::handleForPath(localPath); + if (!handle) { + qCWarning(lcCfApi) << "Couldn't find pin state for non existing file" << localPath; return {}; } - switch (info->PinState) { - case CF_PIN_STATE_UNSPECIFIED: - return PinState::Unspecified; - case CF_PIN_STATE_PINNED: - return PinState::AlwaysLocal; - case CF_PIN_STATE_UNPINNED: - return PinState::OnlineOnly; - case CF_PIN_STATE_INHERIT: - return PinState::Inherited; - default: - Q_UNREACHABLE(); + const auto info = cfapi::findPlaceholderInfo(handle); + if (!info) { + qCWarning(lcCfApi) << "Couldn't find pin state for regular non-placeholder file" << localPath; return {}; } + + return info.pinState(); } Vfs::AvailabilityResult VfsCfApi::availability(const QString &folderPath)