From 084d8fbf571e8789b38b2b6c5618dff97105c853 Mon Sep 17 00:00:00 2001 From: runrunya Date: Sat, 6 Sep 2025 10:28:39 +0800 Subject: [PATCH] log and dentryfile Signed-off-by: runrunya --- .../sync_folder/include/bit_ops.h | 63 ++ .../include/cloud_disk_service_logfile.h | 153 ++++ .../include/cloud_disk_service_metafile.h | 158 ++++ .../include/cloud_disk_service_syncfolder.h | 39 + .../sync_folder/include/convertor.h | 31 + .../sync_folder/include/uuid_helper.h | 34 + .../src/cloud_disk_service_logfile.cpp | 674 +++++++++++++++ .../src/cloud_disk_service_metafile.cpp | 798 ++++++++++++++++++ .../src/cloud_disk_service_syncfolder.cpp | 61 ++ .../sync_folder/src/convertor.cpp | 62 ++ .../sync_folder/src/uuid_helper.cpp | 53 ++ .../include/cloud_disk_service_error.h | 44 + 12 files changed, 2170 insertions(+) create mode 100644 services/clouddiskservice/sync_folder/include/bit_ops.h create mode 100644 services/clouddiskservice/sync_folder/include/cloud_disk_service_logfile.h create mode 100644 services/clouddiskservice/sync_folder/include/cloud_disk_service_metafile.h create mode 100644 services/clouddiskservice/sync_folder/include/cloud_disk_service_syncfolder.h create mode 100644 services/clouddiskservice/sync_folder/include/convertor.h create mode 100644 services/clouddiskservice/sync_folder/include/uuid_helper.h create mode 100644 services/clouddiskservice/sync_folder/src/cloud_disk_service_logfile.cpp create mode 100644 services/clouddiskservice/sync_folder/src/cloud_disk_service_metafile.cpp create mode 100644 services/clouddiskservice/sync_folder/src/cloud_disk_service_syncfolder.cpp create mode 100644 services/clouddiskservice/sync_folder/src/convertor.cpp create mode 100644 services/clouddiskservice/sync_folder/src/uuid_helper.cpp create mode 100644 utils/clouddiskservice/include/cloud_disk_service_error.h diff --git a/services/clouddiskservice/sync_folder/include/bit_ops.h b/services/clouddiskservice/sync_folder/include/bit_ops.h new file mode 100644 index 000000000..0dc6c4e5f --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/bit_ops.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BIT_OPS_H +#define BIT_OPS_H + +namespace OHOS::FileManagement::CloudDiskService { + +struct BitOps { + static const uint8_t BIT_PER_BYTE = 8; + static int TestBit(uint32_t nr, const uint8_t addr[]) + { + return 1 & (addr[nr / BIT_PER_BYTE] >> (nr & (BIT_PER_BYTE - 1))); + } + + static void ClearBit(uint32_t nr, uint8_t addr[]) + { + addr[nr / BIT_PER_BYTE] &= ~(1UL << ((nr) % BIT_PER_BYTE)); + } + + static void SetBit(uint32_t nr, uint8_t addr[]) + { + addr[nr / BIT_PER_BYTE] |= (1UL << ((nr) % BIT_PER_BYTE)); + } + + static uint32_t FindNextBit(const uint8_t addr[], uint32_t maxSlots, uint32_t start) + { + while (start < maxSlots) { + if (BitOps::TestBit(start, addr)) { + return start; + } + start++; + } + return maxSlots; + } + + static uint32_t FindNextZeroBit(const uint8_t addr[], uint32_t maxSlots, uint32_t start) + { + while (start < maxSlots) { + if (!BitOps::TestBit(start, addr)) { + return start; + } + start++; + } + return maxSlots; + } +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // BIT_OPS_H diff --git a/services/clouddiskservice/sync_folder/include/cloud_disk_service_logfile.h b/services/clouddiskservice/sync_folder/include/cloud_disk_service_logfile.h new file mode 100644 index 000000000..3b9bb2123 --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/cloud_disk_service_logfile.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_DISK_SERVICE_LOGFILE_H +#define CLOUD_DISK_SERVICE_LOGFILE_H + +#include +#include +#include +#include +#include +#include + +#include "cloud_disk_common.h" +#include "cloud_disk_service_metafile.h" +#include "disk_types.h" +#include "ffrt_inner.h" +#include "unique_fd.h" + +namespace OHOS::FileManagement::CloudDiskService { + +constexpr uint32_t LOGBLOCK_RESERVED_LENGTH = 2; +constexpr uint32_t LOGGROUP_RESERVED_LENGTH = 62; +constexpr uint32_t LOGBLOCK_PER_GROUP = 64; +constexpr uint32_t LOGGROUP_SHIFT = 0X6; +constexpr uint32_t LOGGROUP_SIZE = 4096; +constexpr uint32_t LOGGROUP_SIZE_SHIFT = 0XC; +constexpr uint32_t LOGGROUP_MAX = 1024; +constexpr uint32_t LOGGROUP_INDEX_MASK = 0XFFFF; +constexpr uint32_t LOGGROUP_HEADER = 4096; +constexpr uint32_t LOG_INDEX_MASK = 0X3F; +constexpr uint32_t LOG_COUNT_MAX = 65536; + +#pragma pack(push, 1) +struct LogBlock { + uint64_t line; + uint64_t timestamp; + uint64_t parentInode; + uint32_t hash; + uint8_t operationType; + uint8_t recordId[RECORD_ID_LEN]; + uint8_t parentRecordId[RECORD_ID_LEN]; + uint8_t reserved[LOGBLOCK_RESERVED_LENGTH]; +}; + +struct LogGroup { + uint8_t logVersion; + uint8_t logBlockCnt; + struct LogBlock nsl[LOGBLOCK_PER_GROUP]; + uint8_t reserved[LOGGROUP_RESERVED_LENGTH]; +}; +static_assert(sizeof(LogGroup) == LOGGROUP_SIZE); +#pragma pack(pop) + +class CloudDiskServiceLogFile { +public: + CloudDiskServiceLogFile() = delete; + ~CloudDiskServiceLogFile() = default; + explicit CloudDiskServiceLogFile(const int32_t userId, const uint32_t syncFolderIndex); + void SetSyncFolderPath(const std::string &path); + + int32_t ProduceLog(const struct EventInfo &eventInfo); + int32_t PraseLog(const uint64_t line, ChangeData &data, bool &isEof); + int32_t FillChildForDir(const std::string &path, const uint64_t timestamp); + void StartCallback(); + void StopCallback(); + + int32_t OnDataChange(); + +private: + int32_t ProductLogForOperate(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, std::string &childRecordId, + OperationType operationType); + int32_t ProduceCreateLog(const std::shared_ptr parentMetaFile, const std::string &path, + const std::string &name, std::string &childRecordId); + int32_t ProduceUnlinkLog(const std::shared_ptr parentMetaFile, const std::string &name, + std::string &childRecordId); + int32_t ProduceRenameOldLog(const std::shared_ptr parentMetaFile, const std::string &name, + std::string &childRecordId); + int32_t ProduceRenameNewLog(const std::shared_ptr parentMetaFile, const std::string &path, + const std::string &name, std::string &childRecordId); + int32_t ProduceCloseAndWriteLog(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, std::string &childRecordId); + + int32_t WriteLogFile(const struct LogBlock &logBlock); + int32_t ReadLogFile(const uint64_t line, struct LogBlock &logBlock); + int32_t GenerateLogBlock(const struct EventInfo &eventInfo, const uint64_t parentInode, + const std::string &childRecordId, const std::string &parentRecordId, uint64_t &line); + int32_t GenerateChangeData(const struct EventInfo &eventInfo, uint64_t line, + const std::string &childRecordId, const std::string &parentRecordId); + + int32_t userId_; + uint32_t syncFolderIndex_; + bool needCallback_; + std::string syncFolderPath_; + std::string logFilePath_; + std::string renameRecordId_; + UniqueFd fd_; + + std::atomic currentLine_; + + std::mutex vectorMtx_; + std::vector changeDatas_; +}; + +typedef std::pair LogFileKey; + +class LogFileMgr { +public: + static LogFileMgr& GetInstance(); + std::shared_ptr GetCloudDiskServiceLogFile(const int32_t userId, + const uint32_t syncFolderIndex); + + int32_t ProduceRequest(const struct EventInfo &eventInfo); + int32_t PraseRequest(const int32_t userId, const uint32_t syncFolderIndex, const uint64_t start, + const uint64_t count, struct ChangesResult &changesResult); + + int32_t RegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex, const std::string &path); + int32_t UnRegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex); + void RegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex); + void UnRegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex); + +private: + LogFileMgr() = default; + ~LogFileMgr() = default; + LogFileMgr(const LogFileMgr &m) = delete; + const LogFileMgr &operator=(const LogFileMgr &m) = delete; + + static void CallBack(void *data); + int32_t OnDataChange(); + + bool running_; + std::mutex mtx_{}; + std::map> LogFiles_; + int32_t registerChangeCount_{0}; + ffrt_timer_t handle_{-1}; +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // CLOUD_DISK_SERVICE_LOGFILE_H \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/include/cloud_disk_service_metafile.h b/services/clouddiskservice/sync_folder/include/cloud_disk_service_metafile.h new file mode 100644 index 000000000..261166146 --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/cloud_disk_service_metafile.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_DISK_SERVICE_METAFILE_H +#define CLOUD_DISK_SERVICE_METAFILE_H + +#include +#include +#include +#include +#include + +#include "unique_fd.h" + +namespace OHOS::FileManagement::CloudDiskService { + +constexpr uint8_t VALIDATE = 0x01; +constexpr uint8_t INVALIDATE = 0x02; +constexpr uint32_t DENTRYGROUP_SIZE = 4096; +constexpr uint32_t DENTRY_NAME_LEN = 16; +constexpr uint32_t DENTRY_RESERVED_LENGTH = 3; +constexpr uint32_t DENTRY_PER_GROUP = 60; +constexpr uint32_t DENTRY_BITMAP_LENGTH = 8; +constexpr uint32_t DENTRY_GROUP_RESERVED = 7; +constexpr uint32_t DENTRYGROUP_HEADER = 4096; +constexpr uint32_t MAX_BUCKET_LEVEL = 63; +constexpr uint32_t BUCKET_BLOCKS = 2; +constexpr uint32_t BYTE_PER_SLOT = 16; +constexpr uint32_t HMDFS_SLOT_LEN_BITS = 4; +constexpr uint32_t RECORD_ID_LEN = 16; +constexpr uint32_t DENTRYFILE_NAME_LEN = 16; +const std::string ROOT_PARENTDENTRYFILE = "."; + +#pragma pack(push, 1) +struct CloudDiskServiceDentry { + uint8_t revalidate; + uint8_t recordId[RECORD_ID_LEN]; + uint32_t hash; + uint16_t mode; + uint16_t namelen; + uint64_t size; + uint64_t mtime; + uint64_t atime; + /* reserved bytes for long term extend, total 52 bytes */ + uint8_t reserved[DENTRY_RESERVED_LENGTH]; +}; + +struct CloudDiskServiceDentryGroup { + uint8_t dentryVersion; + uint8_t bitmap[DENTRY_BITMAP_LENGTH]; + struct CloudDiskServiceDentry nsl[DENTRY_PER_GROUP]; + uint8_t fileName[DENTRY_PER_GROUP][DENTRY_NAME_LEN]; + uint8_t reserved[DENTRY_GROUP_RESERVED]; +}; +static_assert(sizeof(CloudDiskServiceDentryGroup) == DENTRYGROUP_SIZE); + +struct CloudDiskServiceDcacheHeader { + uint8_t parentDentryfile[DENTRYFILE_NAME_LEN]; + uint8_t selfRecordId[RECORD_ID_LEN]; + uint32_t selfHash; +}; +#pragma pack(pop) + +struct MetaBase { + MetaBase(const std::string &nameOrRecordId, const bool isName) + { + if (isName) { + name = nameOrRecordId; + } else { + recordId = nameOrRecordId; + } + } + MetaBase(const std::string &name, const std::string &recordId) : name(name), recordId(recordId) {} + MetaBase() = default; + + uint32_t mode{0}; + uint32_t hash{0}; + uint64_t atime{0}; + uint64_t mtime{0}; + uint64_t size{0}; + std::string name{}; + std::string recordId{}; +}; + +class CloudDiskServiceMetaFile { +public: + CloudDiskServiceMetaFile() = delete; + ~CloudDiskServiceMetaFile() = default; + using CloudDiskServiceMetaFileCallBack = std::function; + explicit CloudDiskServiceMetaFile(const int32_t userId, const uint32_t syncFolderIndex, const uint64_t inode); + + int32_t DoCreate(const MetaBase &base); + int32_t DoRemove(const MetaBase &base, std::string &recordId); + int32_t DoFlush(const MetaBase &base, std::string &recordId); + int32_t DoUpdate(const MetaBase &base, std::string &recordId); + int32_t DoRenameOld(const MetaBase &base, std::string &recordId); + int32_t DoRenameNew(const MetaBase &base, std::string &recordId); + int32_t DoRename(MetaBase &metaBase, const std::string &newName, + std::shared_ptr newMetaFile); + int32_t DoLookupByName(MetaBase &base); + int32_t DoLookupByRecordId(MetaBase &base, uint8_t revalidate); + + int32_t DecodeDentryHeader(); + int32_t GenericDentryHeader(); + + std::string parentDentryFile_{}; + std::string selfRecordId_{}; + std::string selfInode_{}; + uint32_t selfHash_{0}; + + std::string syncFolderIndex_{}; + int32_t userId_{0}; + +private: + int32_t GetCreateInfo(const MetaBase &base, uint32_t &bitPos, uint32_t &namehash, unsigned long &bidx, + struct CloudDiskServiceDentryGroup &dentryBlk); + int32_t HandleFileByFd(unsigned long &endBlock, uint32_t level); + std::mutex mtx_{}; + UniqueFd fd_{}; + std::string cacheFile_{}; +}; + +typedef std::pair MetaFileKey; +typedef std::pair> MetaFileListEle; + +class MetaFileMgr { +public: + static MetaFileMgr& GetInstance(); + std::shared_ptr GetCloudDiskServiceMetaFile(const int32_t userId, + const uint32_t syncFolderIndex, const uint64_t inode); + + int32_t GetRelativePath(const std::shared_ptr metaFile, std::string &path); +private: + MetaFileMgr() = default; + ~MetaFileMgr() = default; + MetaFileMgr(const MetaFileMgr &m) = delete; + const MetaFileMgr &operator=(const MetaFileMgr &m) = delete; + + std::mutex mtx_{}; + std::list metaFileList_; + std::map::iterator> metaFiles_; +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // CLOUD_DISK_SERVICE_METAFILE_H \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/include/cloud_disk_service_syncfolder.h b/services/clouddiskservice/sync_folder/include/cloud_disk_service_syncfolder.h new file mode 100644 index 000000000..cb350ec30 --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/cloud_disk_service_syncfolder.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_DISK_SERVICE_SYNCFOLDER_H +#define CLOUD_DISK_SERVICE_SYNCFOLDER_H + +#include +#include + +#include "disk_types.h" + +namespace OHOS::FileManagement::CloudDiskService { + +class CloudDiskServiceSyncFolder { +public: + static int32_t RegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex, const std::string &path); + static int32_t UnRegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex); + static void RegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex); + static void UnRegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex); + static int32_t GetSyncFolderChanges(const int32_t userId, const uint32_t syncFolderIndex, const uint64_t start, + const uint64_t count, struct ChangesResult &changesResult); + static int32_t SetSyncFolderChanges(const struct EventInfo &eventInfos); +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // CLOUD_DISK_SERVICE_SYNCFOLDER_H \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/include/convertor.h b/services/clouddiskservice/sync_folder/include/convertor.h new file mode 100644 index 000000000..ee691b340 --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/convertor.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONVERTOR_H +#define CONVERTOR_H + +#include + +namespace OHOS::FileManagement::CloudDiskService { + +class Convertor { +public: + static std::string ConvertToHex(uint64_t value); + static uint64_t ConvertFromHex(const std::string &hex); +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // CONVERTOR_H diff --git a/services/clouddiskservice/sync_folder/include/uuid_helper.h b/services/clouddiskservice/sync_folder/include/uuid_helper.h new file mode 100644 index 000000000..f5eb488b8 --- /dev/null +++ b/services/clouddiskservice/sync_folder/include/uuid_helper.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UUID_HELPER_H +#define UUID_HELPER_H + +#include + +#include "uuid.h" + +namespace OHOS::FileManagement::CloudDiskService { + +class UuidHelper { +public: + static std::string GenerateUuid(); + static std::string GenerateUuidWithoutDelim(); + static std::string GenerateUuidOnly(); +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // UUID_HELPER_H diff --git a/services/clouddiskservice/sync_folder/src/cloud_disk_service_logfile.cpp b/services/clouddiskservice/sync_folder/src/cloud_disk_service_logfile.cpp new file mode 100644 index 000000000..783855807 --- /dev/null +++ b/services/clouddiskservice/sync_folder/src/cloud_disk_service_logfile.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_disk_service_logfile.h" + +#include +#include +#include +#include +#include + +#include "cloud_disk_service_callback_manager.h" +#include "cloud_disk_service_error.h" +#include "cloud_disk_service_manager.h" +#include "cloud_disk_sync_folder.h" +#include "cloud_file_utils.h" +#include "convertor.h" +#include "file_utils.h" +#include "utils_directory.h" +#include "utils_log.h" +#include "uuid_helper.h" + +namespace OHOS::FileManagement::CloudDiskService { + +const std::string LOGFILENAME = "history.log"; +const unsigned int STAT_MODE_DIR = 0771; +const unsigned int MAX_CHANGEDATAS_SIZE = 20; +const uint64_t CHANGE_DATA_TIMEOUT_MS = 1000; + +static std::string GetLogFileByPath(const int32_t userId, const uint32_t syncFolderIndex) +{ + std::string rootDir = + "/data/service/el2/" + std::to_string(userId) + + "/hmdfs/cache/account_cache/dentry_cache/clouddisk_service_cache/" + + Convertor::ConvertToHex(syncFolderIndex) + "/"; + Storage::DistributedFile::Utils::ForceCreateDirectory(rootDir, STAT_MODE_DIR); + return rootDir + LOGFILENAME; +} + +static void GetBucketAndOffset(const uint64_t line, uint32_t &bucket, uint32_t &offset) +{ + uint32_t temp = line & LOGGROUP_INDEX_MASK; + bucket = temp >> LOGGROUP_SHIFT; + offset = temp & LOG_INDEX_MASK; +} + +static off_t GetBucketaddr(const uint32_t bucket) +{ + return (static_cast(bucket) << LOGGROUP_SIZE_SHIFT) + LOGGROUP_HEADER; +} + +static std::unique_ptr LoadCurrentPage(int fd, uint32_t bucket) +{ + auto logGroup = std::make_unique(); + off_t pos = GetBucketaddr(bucket); + auto ret = FileRangeLock::FilePosLock(fd, pos, LOGGROUP_SIZE, F_WRLCK); + if (ret) { + return nullptr; + } + ssize_t size = FileUtils::ReadFile(fd, pos, LOGGROUP_SIZE, logGroup.get()); + if (size != LOGGROUP_SIZE) { + (void)FileRangeLock::FilePosLock(fd, pos, LOGGROUP_SIZE, F_UNLCK); + return nullptr; + } + return logGroup; +} + +static void UnlockCurrentPage(int fd, uint32_t bucket) +{ + off_t pos = GetBucketaddr(bucket); + (void)FileRangeLock::FilePosLock(fd, pos, LOGGROUP_SIZE, F_UNLCK); +} + +static int32_t SyncCurrentPage(LogGroup &logGroup, int fd, uint32_t bucket) +{ + off_t pos = GetBucketaddr(bucket); + int size = FileUtils::WriteFile(fd, &logGroup, pos, LOGGROUP_SIZE); + if (size != LOGGROUP_SIZE) { + LOGD("WriteFile failed, size %{public}d != %{public}d", size, LOGGROUP_SIZE); + (void)FileRangeLock::FilePosLock(fd, pos, LOGGROUP_SIZE, F_UNLCK); + return EINVAL; + } + auto ret = FileRangeLock::FilePosLock(fd, pos, LOGGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +static int32_t FillLogGroup(struct LogGroup &logGroup, const struct LogBlock &logBlock, uint32_t offset) +{ + if (logGroup.nsl[offset].timestamp != 0) { + (void) memset_s(&logGroup, LOGGROUP_SIZE, 0, LOGGROUP_SIZE); + } + auto ret = memcpy_s(&logGroup.nsl[offset], sizeof(struct LogBlock), &logBlock, sizeof(struct LogBlock)); + if (ret != 0) { + LOGE("memcpy_s struct LogBlock failed, errno = %{public}d", errno); + return -1; + } + logGroup.logBlockCnt++; + return E_OK; +} + +static uint32_t GetCurrentLine(int fd) +{ + uint32_t startLine = 0; + uint32_t offset = 0; + for (uint32_t i = 0; i < LOGGROUP_MAX; i++) { + auto logGroup = LoadCurrentPage(fd, i); + if (logGroup == nullptr) { + LOGE("load page failed"); + return 0; + } + if (logGroup->nsl[0].timestamp != 0) { + UnlockCurrentPage(fd, i); + break; + } else if (logGroup->nsl[0].line != startLine + offset) { + UnlockCurrentPage(fd, i); + break; + } else { + startLine = logGroup->nsl[0].line; + offset = logGroup->logBlockCnt; + UnlockCurrentPage(fd, i); + } + } + return startLine + offset; +} + +int32_t CloudDiskServiceLogFile::WriteLogFile(const struct LogBlock &logBlock) +{ + uint32_t bucket; + uint32_t offset; + GetBucketAndOffset(logBlock.line, bucket, offset); + + auto logGroup = LoadCurrentPage(fd_, bucket); + if (logGroup == nullptr) { + LOGE("load page failed"); + return -1; + } + FillLogGroup(*logGroup, logBlock, offset); + SyncCurrentPage(*logGroup, fd_, bucket); + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ReadLogFile(const uint64_t line, struct LogBlock &logBlock) +{ + uint32_t bucket; + uint32_t offset; + GetBucketAndOffset(line, bucket, offset); + + auto logGroup = LoadCurrentPage(fd_, bucket); + if (logGroup == nullptr) { + LOGE("load page failed"); + return -1; + } + logBlock = logGroup->nsl[offset]; + UnlockCurrentPage(fd_, bucket); + return E_OK; +} + +int32_t CloudDiskServiceLogFile::OnDataChange() +{ + std::lock_guard lock(vectorMtx_); + + if (changeDatas_.empty()) { + return E_OK; + } + CloudDiskServiceCallbackManager::GetInstance().OnChangeData(syncFolderIndex_, changeDatas_); + changeDatas_.clear(); + + return E_OK; +} + +CloudDiskServiceLogFile::CloudDiskServiceLogFile(const int32_t userId, const uint32_t syncFolderIndex) +{ + userId_ = userId; + syncFolderIndex_ = syncFolderIndex; + needCallback_ = false; + CloudDiskSyncFolder::GetInstance().GetSyncFolderByIndex(syncFolderIndex_, syncFolderPath_); + logFilePath_ = GetLogFileByPath(userId_, syncFolderIndex_); + if (access(logFilePath_.c_str(), F_OK) == 0) { + fd_ = UniqueFd{open(logFilePath_.c_str(), O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)}; + currentLine_ = GetCurrentLine(fd_); + } else { + fd_ = UniqueFd{open(logFilePath_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)}; + ftruncate(fd_, LOGGROUP_SIZE * LOGGROUP_MAX + LOGGROUP_HEADER); + currentLine_ = 0; + } +} + +void CloudDiskServiceLogFile::SetSyncFolderPath(const std::string &path) +{ + syncFolderPath_ = path; +} + +int32_t CloudDiskServiceLogFile::GenerateLogBlock(const struct EventInfo &eventInfo, const uint64_t parentInode, + const std::string &childRecordId, const std::string &parentRecordId, + uint64_t &line) +{ + struct LogBlock logBlock; + logBlock.line = currentLine_++; + logBlock.timestamp = eventInfo.timestamp; + logBlock.parentInode = parentInode; + logBlock.hash = CloudDisk::CloudFileUtils::DentryHash(eventInfo.name); + logBlock.operationType = static_cast(eventInfo.operateType); + auto ret = memcpy_s(logBlock.recordId, RECORD_ID_LEN, childRecordId.c_str(), RECORD_ID_LEN); + if (ret != 0) { + LOGE("memcpy_s recordId failed, errno = %{public}d", errno); + return -1; + } + ret = memcpy_s(logBlock.parentRecordId, RECORD_ID_LEN, parentRecordId.c_str(), RECORD_ID_LEN); + if (ret != 0) { + LOGE("memcpy_s parentRecordId failed, errno = %{public}d", errno); + return -1; + } + WriteLogFile(logBlock); + line = logBlock.line; + LOGD("Generate log line:%{public}lu, operationType:%{public}d", line, logBlock.operationType); + return E_OK; +} + +int32_t CloudDiskServiceLogFile::GenerateChangeData(const struct EventInfo &eventInfo, uint64_t line, + const std::string &childRecordId, const std::string &parentRecordId) +{ + std::lock_guard lock(vectorMtx_); + std::string fullPath = eventInfo.path + "/" + eventInfo.name; + struct ChangeData changeData{}; + changeData.updateSequenceNumber = line; + changeData.fileId = childRecordId; + changeData.parentFileId = parentRecordId; + if (fullPath.compare(0, syncFolderPath_.size(), syncFolderPath_) == 0) { + changeData.relativePath = fullPath.substr(syncFolderPath_.size()); + } else { + changeData.relativePath = fullPath; + } + changeData.operationType = eventInfo.operateType; + + struct stat childStat; + if (stat(fullPath.c_str(), &childStat) != 0) { + changeData.size = 0; + changeData.mtime = 0; + } else { + changeData.size = childStat.st_size; + changeData.mtime = childStat.st_mtime; + } + + changeData.timeStamp = eventInfo.timestamp; + changeDatas_.push_back(changeData); + LOGD("Generate changedata line:%{public}lu, operationType:%{public}d, size:%{public}zu", line, + static_cast(changeData.operationType), changeDatas_.size()); + if (changeDatas_.size() >= MAX_CHANGEDATAS_SIZE) { + CloudDiskServiceCallbackManager::GetInstance().OnChangeData(syncFolderIndex_, changeDatas_); + changeDatas_.clear(); + } + return E_OK; +} + +int32_t CloudDiskServiceLogFile::FillChildForDir(const std::string &path, const uint64_t timestamp) +{ + struct stat statInfo; + if (stat(path.c_str(), &statInfo) != 0) { + LOGE("stat parent failed for %{public}d", errno); + return errno; + } + + if (!S_ISDIR(statInfo.st_mode)) { + return E_OK; + } + DIR *dir = opendir(path.c_str()); + if (!dir) { + LOGE("opendir failed errno = %{public}d", errno); + return errno; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + struct EventInfo eventInfo(userId_, syncFolderIndex_, OperationType::CREATE, path + "/" + entry->d_name); + LOGD("FillChildForDir for path: %{public}s", (path + "/" + entry->d_name).c_str()); + ProduceLog(eventInfo); + } + + closedir(dir); + return E_OK; +} + + +void CloudDiskServiceLogFile::StartCallback() +{ + LOGD("StartCallback, %{public}u", syncFolderIndex_); + needCallback_ = true; +} + +void CloudDiskServiceLogFile::StopCallback() +{ + LOGD("StopCallback, %{public}u", syncFolderIndex_); + needCallback_ = false; +} + +int32_t CloudDiskServiceLogFile::ProduceLog(const struct EventInfo &eventInfo) +{ + struct stat parentStat; + if (stat(eventInfo.path.c_str(), &parentStat) != 0) { + LOGE("stat parent failed for %{public}d", errno); + return errno; + } + + int32_t ret = 0; + if (eventInfo.operateType == OperationType::SYNC_FOLDER_INVALID) { + ret = CloudDiskServiceManager::GetInstance().UnregisterForSa(eventInfo.path + "/" + eventInfo.name); + if (ret != 0) { + return ret; + } else { + return LogFileMgr::GetInstance().UnRegisterSyncFolder(userId_, syncFolderIndex_); + } + } + + auto parentMetaFile = MetaFileMgr::GetInstance().GetCloudDiskServiceMetaFile(userId_, syncFolderIndex_, + parentStat.st_ino); + + std::string childRecordId; + ret = ProductLogForOperate(parentMetaFile, eventInfo.path, eventInfo.name, childRecordId, + eventInfo.operateType); + if (ret != 0) { + LOGE("create log failed"); + return ret; + } + + uint64_t line = 0; + GenerateLogBlock(eventInfo, parentStat.st_ino, childRecordId, parentMetaFile->selfRecordId_, line); + if (needCallback_) { + GenerateChangeData(eventInfo, line, childRecordId, parentMetaFile->selfRecordId_); + } + + if (eventInfo.operateType == OperationType::CREATE) { + ffrt::submit( + [eventInfo, this] { FillChildForDir(eventInfo.path + "/" + eventInfo.name, eventInfo.timestamp); }); + } + return E_OK; +} + +int32_t CloudDiskServiceLogFile::PraseLog(const uint64_t line, ChangeData &data, bool &isEof) + __attribute__((no_sanitize("unsigned-integer-overflow"))) +{ + LOGD("prase line:%{public}lu, currentline:%{public}lu", line, currentLine_.load()); + if (line == currentLine_.load()) { + isEof = true; + return E_OK; + } else if (currentLine_.load() - line >= LOG_COUNT_MAX) { + return E_INVALID_CHANGE_SEQUENCE; + } + + struct LogBlock logBlock; + auto ret = ReadLogFile(line, logBlock); + if (ret != 0) { + return ret; + } + + auto parentMetaFile = MetaFileMgr::GetInstance().GetCloudDiskServiceMetaFile(userId_, syncFolderIndex_, + logBlock.parentInode); + std::string relativePath; + ret = MetaFileMgr::GetInstance().GetRelativePath(parentMetaFile, relativePath); + if (ret != 0) { + LOGE("get relative path failed"); + return ret; + } + + uint8_t revalidate = VALIDATE; + if (logBlock.operationType == static_cast(OperationType::DELETE) || + logBlock.operationType == static_cast(OperationType::MOVE_FROM)) { + revalidate = INVALIDATE; + } + std::string recordId = std::string(reinterpret_cast(logBlock.recordId), RECORD_ID_LEN); + std::string parentRecordId = std::string(reinterpret_cast(logBlock.parentRecordId), RECORD_ID_LEN); + MetaBase mBase(recordId, false); + auto metaFile = parentMetaFile->DoLookupByRecordId(mBase, revalidate); + + data.updateSequenceNumber = logBlock.line; + data.fileId = recordId; + data.parentFileId = parentRecordId; + data.relativePath = relativePath + "/" + mBase.name; + data.operationType = static_cast(logBlock.operationType); + data.size = mBase.size; + data.mtime = mBase.mtime; + data.timeStamp = logBlock.timestamp; + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ProductLogForOperate(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, + std::string &childRecordId, OperationType operationType) +{ + switch (operationType) { + case OperationType::CREATE: + return ProduceCreateLog(parentMetaFile, path, name, childRecordId); + case OperationType::DELETE: + return ProduceUnlinkLog(parentMetaFile, name, childRecordId); + case OperationType::MOVE_FROM: + return ProduceRenameOldLog(parentMetaFile, name, childRecordId); + case OperationType::MOVE_TO: + return ProduceRenameNewLog(parentMetaFile, path, name, childRecordId); + case OperationType::CLOSE_WRITE: + return ProduceCloseAndWriteLog(parentMetaFile, path, name, childRecordId); + case OperationType::SYNC_FOLDER_INVALID: + case OperationType::OPERATION_MAX: + return EINVAL; + } +} + +int32_t CloudDiskServiceLogFile::ProduceCreateLog(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, + std::string &childRecordId) +{ + LOGD("Begin ProduceCreateLog"); + childRecordId = UuidHelper::GenerateUuidOnly(); + MetaBase mBase(name, childRecordId); + + struct stat childStat; + if (stat((path + "/" + name).c_str(), &childStat) != 0) { + LOGE("stat child failed for %{public}d", errno); + return errno; + } + mBase.mode = childStat.st_mode; + mBase.atime = childStat.st_atime; + mBase.mtime = childStat.st_mtime; + mBase.size = childStat.st_size; + auto ret = parentMetaFile->DoCreate(mBase); + if (ret != 0) { + LOGE("create failed"); + return -1; + } + + if (!S_ISDIR(childStat.st_mode)) { + return E_OK; + } + auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskServiceMetaFile(userId_, syncFolderIndex_, childStat.st_ino); + metaFile->parentDentryFile_ = parentMetaFile->selfInode_; + metaFile->selfRecordId_ = childRecordId; + metaFile->selfHash_ = CloudDisk::CloudFileUtils::DentryHash(name); + metaFile->GenericDentryHeader(); + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ProduceUnlinkLog(const std::shared_ptr parentMetaFile, + const std::string &name, std::string &childRecordId) +{ + LOGD("Begin ProduceUnlinkLog"); + MetaBase mBase(name, true); + auto ret = parentMetaFile->DoRemove(mBase, childRecordId); + if (ret != 0) { + LOGE("remove failed"); + return -1; + } + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ProduceRenameOldLog(const std::shared_ptr parentMetaFile, + const std::string &name, std::string &childRecordId) +{ + LOGD("Begin ProduceRenameOldLog"); + MetaBase mBase(name, true); + auto ret = parentMetaFile->DoRenameOld(mBase, childRecordId); + if (ret != 0) { + LOGE("renameold failed"); + return -1; + } + renameRecordId_ = childRecordId; + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ProduceRenameNewLog(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, + std::string &childRecordId) +{ + LOGD("Begin ProduceRenameNewLog"); + MetaBase mBase(name, true); + struct stat childStat; + if (stat((path + "/" + name).c_str(), &childStat) != 0) { + LOGE("stat child failed for %{public}d", errno); + return errno; + } + mBase.mode = childStat.st_mode; + mBase.atime = childStat.st_atime; + mBase.mtime = childStat.st_mtime; + mBase.size = childStat.st_size; + auto ret = parentMetaFile->DoRenameNew(mBase, renameRecordId_); + if (ret != 0) { + LOGE("renamenew failed"); + return -1; + } + childRecordId = renameRecordId_; + + if (!S_ISDIR(childStat.st_mode)) { + return E_OK; + } + auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskServiceMetaFile(userId_, syncFolderIndex_, childStat.st_ino); + metaFile->parentDentryFile_ = parentMetaFile->selfInode_; + metaFile->selfRecordId_ = childRecordId; + metaFile->selfHash_ = CloudDisk::CloudFileUtils::DentryHash(name); + metaFile->GenericDentryHeader(); + return E_OK; +} + +int32_t CloudDiskServiceLogFile::ProduceCloseAndWriteLog(const std::shared_ptr parentMetaFile, + const std::string &path, const std::string &name, + std::string &childRecordId) +{ + LOGD("Begin ProduceCloseAndWriteLog"); + MetaBase mBase(name, true); + struct stat childStat; + if (stat((path + "/" + name).c_str(), &childStat) != 0) { + LOGE("stat child failed for %{public}d", errno); + return errno; + } + mBase.mode = childStat.st_mode; + mBase.atime = childStat.st_atime; + mBase.mtime = childStat.st_mtime; + mBase.size = childStat.st_size; + auto ret = parentMetaFile->DoUpdate(mBase, childRecordId); + if (ret != 0) { + LOGE("update failed"); + return -1; + } + return E_OK; +} + +LogFileMgr& LogFileMgr::GetInstance() +{ + static LogFileMgr instance_; + return instance_; +} + +int32_t LogFileMgr::ProduceRequest(const struct EventInfo &eventInfo) +{ + auto logFile = GetCloudDiskServiceLogFile(eventInfo.userId, eventInfo.syncFolderIndex); + logFile->ProduceLog(eventInfo); + return E_OK; +} + +int32_t LogFileMgr::PraseRequest(const int32_t userId, const uint32_t syncFolderIndex, const uint64_t start, + const uint64_t count, struct ChangesResult &changesResult) +{ + int32_t ret; + uint64_t nextUsn = start + count; + auto logFile = GetCloudDiskServiceLogFile(userId, syncFolderIndex); + for (uint64_t line = start; line < start + count; line++) { + struct ChangeData changeData; + ret = logFile->PraseLog(line, changeData, changesResult.isEof); + if (ret == E_OK && !changesResult.isEof) { + changesResult.changesData.push_back(changeData); + continue; + } else if (changesResult.isEof) { + nextUsn = line; + break; + } else { + break; + } + } + if (ret == E_OK) { + changesResult.nextUsn = nextUsn; + } + return ret; +} + +int32_t LogFileMgr::RegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex, const std::string &path) +{ + auto logFile = GetCloudDiskServiceLogFile(userId, syncFolderIndex); + logFile->SetSyncFolderPath(path); + + struct stat st; + if (stat(path.c_str(), &st) != 0) { + LOGE("stat failed for %{public}d", errno); + return errno; + } + auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskServiceMetaFile(userId, syncFolderIndex, st.st_ino); + metaFile->parentDentryFile_ = ROOT_PARENTDENTRYFILE; + metaFile->selfRecordId_ = UuidHelper::GenerateUuidOnly(); + metaFile->selfHash_ = CloudDisk::CloudFileUtils::DentryHash(ROOT_PARENTDENTRYFILE); + + metaFile->GenericDentryHeader(); + + ffrt::submit([logFile, path] { logFile->FillChildForDir(path, UTCTimeMilliSeconds()); }); + return E_OK; +} + +int32_t LogFileMgr::UnRegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex) +{ + std::lock_guard lock(mtx_); + LogFileKey key(userId, syncFolderIndex); + LogFiles_.erase(key); + + std::string rootDir = + "/data/service/el2/" + std::to_string(userId) + + "/hmdfs/cache/account_cache/dentry_cache/clouddisk_service_cache/" + + Convertor::ConvertToHex(syncFolderIndex) + "/"; + if (!OHOS::Storage::DistributedFile::Utils::ForceRemoveDirectoryDeepFirst(rootDir)) { + LOGW("remove photo dentry dir failed, errno: %{public}d", errno); + } + return E_OK; +} + +void LogFileMgr::RegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex) +{ + { + std::lock_guard lock(mtx_); + if (registerChangeCount_ == 0) { + handle_ = ffrt_timer_start(ffrt_qos_default, CHANGE_DATA_TIMEOUT_MS, nullptr, CallBack, true); + } + registerChangeCount_++; + } + + auto logFile = GetCloudDiskServiceLogFile(userId, syncFolderIndex); + logFile->StartCallback(); +} + +void LogFileMgr::UnRegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex) +{ + auto logFile = GetCloudDiskServiceLogFile(userId, syncFolderIndex); + logFile->StopCallback(); + + { + std::lock_guard lock(mtx_); + registerChangeCount_--; + if (registerChangeCount_ == 0) { + ffrt_timer_stop(ffrt_qos_default, handle_); + } + } +} + +std::shared_ptr LogFileMgr::GetCloudDiskServiceLogFile(const int32_t userId, + const uint32_t syncFolderIndex) +{ + std::shared_ptr lFile = nullptr; + std::lock_guard lock(mtx_); + LogFileKey key(userId, syncFolderIndex); + auto it = LogFiles_.find(key); + if (it != LogFiles_.end()) { + lFile = it->second; + } else { + lFile = std::make_shared(userId, syncFolderIndex); + LogFiles_[key] = lFile; + } + return lFile; +} + +void LogFileMgr::CallBack(void *) +{ + LogFileMgr::GetInstance().OnDataChange(); +} + +int32_t LogFileMgr::OnDataChange() +{ + std::lock_guard lock(mtx_); + for (const auto &it : LogFiles_) { + it.second->OnDataChange(); + } + return E_OK; +} + +} // namespace OHOS::FileManagement::CloudDiskService \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/src/cloud_disk_service_metafile.cpp b/services/clouddiskservice/sync_folder/src/cloud_disk_service_metafile.cpp new file mode 100644 index 000000000..85c405816 --- /dev/null +++ b/services/clouddiskservice/sync_folder/src/cloud_disk_service_metafile.cpp @@ -0,0 +1,798 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_disk_service_metafile.h" + +#include +#include +#include +#include +#include + +#include "bit_ops.h" +#include "cloud_disk_service_error.h" +#include "cloud_file_utils.h" +#include "convertor.h" +#include "file_utils.h" +#include "utils_directory.h" +#include "utils_log.h" + +namespace OHOS::FileManagement::CloudDiskService { +using namespace OHOS::FileManagement; + +constexpr uint32_t MAX_META_FILE_NUM = 150; +const unsigned int STAT_MODE_DIR = 0771; + +struct DcacheLookupCtx { + int fd{-1}; + std::string name{}; + std::string recordId{}; + uint32_t hash{0}; + uint32_t bidx{0}; + uint32_t bitPos{0}; + std::unique_ptr page{nullptr}; +}; + +static inline uint32_t GetDentrySlots(size_t namelen) +{ + return static_cast((namelen + BYTE_PER_SLOT - 1) >> HMDFS_SLOT_LEN_BITS); +} + +static inline off_t GetDentryGroupPos(size_t bidx) +{ + return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER; +} + +static inline uint64_t GetDentryGroupCnt(uint64_t size) +{ + return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0; +} + +static uint32_t GetOverallBucket(uint32_t level) +{ + if (level >= MAX_BUCKET_LEVEL) { + LOGD("level = %{public}d overflow", level); + return E_OK; + } + uint64_t buckets = (1ULL << (level + 1)) - 1; + return static_cast(buckets); +} + +static size_t GetDcacheFileSize(uint32_t level) +{ + size_t buckets = GetOverallBucket(level); + return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER; +} + +static uint32_t GetBucketByLevel(uint32_t level) +{ + if (level >= MAX_BUCKET_LEVEL) { + LOGD("level = %{public}d overflow", level); + return E_OK; + } + + uint64_t buckets = (1ULL << level); + return static_cast(buckets); +} + +static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset) +{ + if (level >= MAX_BUCKET_LEVEL) { + return E_OK; + } + + uint64_t curLevelMaxBucks = (1ULL << level); + if (buckoffset >= curLevelMaxBucks) { + return E_OK; + } + + return static_cast(curLevelMaxBucks) + buckoffset - 1; +} + +static unsigned long GetBidxFromLevel(uint32_t level, uint32_t namehash) +{ + uint32_t bucket = GetBucketByLevel(level); + if (bucket == 0) { + return E_OK; + } + return BUCKET_BLOCKS * GetBucketaddr(level, namehash % bucket); +} + +static std::unique_ptr FindDentryPage(uint64_t index, DcacheLookupCtx *ctx) +{ + auto dentryBlk = std::make_unique(); + + off_t pos = GetDentryGroupPos(index); + auto ret = FileRangeLock::FilePosLock(ctx->fd, pos, DENTRYGROUP_SIZE, F_WRLCK); + if (ret) { + return nullptr; + } + ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get()); + if (size != DENTRYGROUP_SIZE) { + (void)FileRangeLock::FilePosLock(ctx->fd, pos, DENTRYGROUP_SIZE, F_UNLCK); + return nullptr; + } + return dentryBlk; +} + +static CloudDiskServiceDentry *FindInBlockById(CloudDiskServiceDentryGroup &dentryBlk, DcacheLookupCtx *ctx, + uint8_t revalidate) +{ + uint32_t bitPos = 0; + CloudDiskServiceDentry *de = nullptr; + + while (bitPos < DENTRY_PER_GROUP) { + if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) { + bitPos++; + continue; + } + de = &dentryBlk.nsl[bitPos]; + if (!de->namelen) { + bitPos++; + continue; + } + + if ((de->revalidate & revalidate) != 0 && + !memcmp(ctx->recordId.c_str(), de->recordId, ctx->recordId.length())) { + ctx->bitPos = bitPos; + return de; + } + bitPos += GetDentrySlots(de->namelen); + } + + return nullptr; +} + +static CloudDiskServiceDentry *FindInBlock(CloudDiskServiceDentryGroup &dentryBlk, DcacheLookupCtx *ctx, + uint8_t revalidate) +{ + uint32_t bitPos = 0; + CloudDiskServiceDentry *de = nullptr; + + while (bitPos < DENTRY_PER_GROUP) { + if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) { + bitPos++; + continue; + } + de = &dentryBlk.nsl[bitPos]; + if (!de->namelen) { + bitPos++; + continue; + } + + if ((de->revalidate & revalidate) != 0 && de->hash == ctx->hash && de->namelen == ctx->name.length() && + !memcmp(ctx->name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) { + ctx->bitPos = bitPos; + return de; + } + bitPos += GetDentrySlots(de->namelen); + } + + return nullptr; +} + +static CloudDiskServiceDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx, bool byId, uint8_t revalidate) + __attribute__((no_sanitize("unsigned-integer-overflow"))) +{ + CloudDiskServiceDentry *de = nullptr; + + uint32_t nbucket = GetBucketByLevel(level); + if (nbucket == 0) { + return de; + } + + uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS; + uint32_t endBlock = bidx + BUCKET_BLOCKS; + + for (; bidx < endBlock; bidx++) { + auto dentryBlk = FindDentryPage(bidx, ctx); + if (dentryBlk == nullptr) { + break; + } + + if (byId) { + de = FindInBlockById(*dentryBlk, ctx, revalidate); + } else { + de = FindInBlock(*dentryBlk, ctx, revalidate); + } + if (de != nullptr) { + ctx->page = std::move(dentryBlk); + break; + } + off_t pos = GetDentryGroupPos(bidx); + (void)FileRangeLock::FilePosLock(ctx->fd, pos, DENTRYGROUP_SIZE, F_UNLCK); + } + ctx->bidx = bidx; + return de; +} + +static CloudDiskServiceDentry *FindDentry(DcacheLookupCtx *ctx, bool byId = false, uint8_t revalidate = VALIDATE) +{ + for (uint32_t level = 0; level < MAX_BUCKET_LEVEL; level++) { + CloudDiskServiceDentry *de = InLevel(level, ctx, byId, revalidate); + if (de != nullptr) { + return de; + } + } + return nullptr; +} + +static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, uint32_t hash, int fd) +{ + ctx->fd = fd; + ctx->name = base.name; + ctx->bidx = 0; + ctx->page = nullptr; + ctx->hash = hash; +} + +static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots) +{ + uint32_t bitStart = 0; + bool loopFlag = true; + while (loopFlag) { + uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart); + if (zeroStart >= maxSlots) { + return maxSlots; + } + + uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart); + if (zeroEnd - zeroStart >= slots) { + return zeroStart; + } + + bitStart = zeroEnd + 1; + if (zeroEnd + 1 >= maxSlots) { + return maxSlots; + } + } + return E_OK; +} + +static bool CreateDentry(CloudDiskServiceDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos, + std::string recordId) +{ + CloudDiskServiceDentry *de; + const std::string name = base.name; + uint32_t slots = GetDentrySlots(name.length()); + + de = &d.nsl[bitPos]; + de->hash = nameHash; + de->namelen = name.length(); + auto ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length()); + if (ret != 0) { + LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length()); + return false; + } + de->revalidate = VALIDATE; + de->atime = base.atime; + de->mtime = base.mtime; + de->size = base.size; + de->mode = base.mode; + (void) memset_s(de->recordId, RECORD_ID_LEN, 0, RECORD_ID_LEN); + ret = memcpy_s(de->recordId, RECORD_ID_LEN, recordId.c_str(), recordId.length()); + if (ret != 0) { + LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", RECORD_ID_LEN, recordId.length()); + return false; + } + + for (uint32_t i = 0; i < slots; i++) { + BitOps::SetBit(bitPos + i, d.bitmap); + if (i) { + (de + i)->namelen = 0; + } + } + return true; +} + +static std::string GetDentryFileByPath(const int32_t userId, const std::string &syncFolderIndex, + const std::string &inode) +{ + std::string cacheDir = + "/data/service/el2/" + std::to_string(userId) + + "/hmdfs/cache/account_cache/dentry_cache/clouddisk_service_cache/" + syncFolderIndex + "/" + + std::to_string(CloudDisk::CloudFileUtils::GetBucketId(inode)) + "/"; + Storage::DistributedFile::Utils::ForceCreateDirectory(cacheDir, STAT_MODE_DIR); + return cacheDir + inode; +} + +CloudDiskServiceMetaFile::CloudDiskServiceMetaFile(const int32_t userId, const uint32_t syncFolderIndex, + const uint64_t inode) +{ + userId_ = userId; + syncFolderIndex_ = Convertor::ConvertToHex(syncFolderIndex); + selfInode_ = Convertor::ConvertToHex(inode); + cacheFile_ = GetDentryFileByPath(userId_, syncFolderIndex_, selfInode_); + if (access(cacheFile_.c_str(), F_OK) == 0) { + fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)}; + DecodeDentryHeader(); + } else { + fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)}; + } +} + +int32_t CloudDiskServiceMetaFile::DecodeDentryHeader() +{ + auto ret = FileRangeLock::FilePosLock(fd_, 0, DENTRYGROUP_HEADER, F_WRLCK); + if (ret) { + return ret; + } + struct CloudDiskServiceDcacheHeader header; + ssize_t size = FileUtils::ReadFile(fd_, 0, sizeof(CloudDiskServiceDcacheHeader), &header); + (void)FileRangeLock::FilePosLock(fd_, 0, DENTRYGROUP_HEADER, F_UNLCK); + if (size != sizeof(CloudDiskServiceDcacheHeader)) { + return size; + } + + parentDentryFile_ = std::string(reinterpret_cast(header.parentDentryfile), DENTRYFILE_NAME_LEN); + selfRecordId_ = std::string(reinterpret_cast(header.selfRecordId), RECORD_ID_LEN); + selfHash_ = header.selfHash; + + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::GenericDentryHeader() +{ + struct stat fileStat; + int err = fstat(fd_, &fileStat); + if (err < 0) { + return EINVAL; + } + if (fileStat.st_size < DENTRYGROUP_HEADER && ftruncate(fd_, DENTRYGROUP_HEADER)) { + return ENOENT; + } + + struct CloudDiskServiceDcacheHeader header; + auto ret = memcpy_s(header.parentDentryfile, DENTRYFILE_NAME_LEN, parentDentryFile_.c_str(), DENTRYFILE_NAME_LEN); + ret += memcpy_s(header.selfRecordId, RECORD_ID_LEN, selfRecordId_.c_str(), RECORD_ID_LEN); + if (ret != 0) { + LOGE("memcpy_s failed errno=%{public}d", errno); + return errno; + } + header.selfHash = selfHash_; + + ret = FileRangeLock::FilePosLock(fd_, 0, DENTRYGROUP_HEADER, F_WRLCK); + if (ret) { + return ret; + } + ssize_t size = FileUtils::WriteFile(fd_, &header, 0, sizeof(CloudDiskServiceDcacheHeader)); + (void)FileRangeLock::FilePosLock(fd_, 0, DENTRYGROUP_HEADER, F_UNLCK); + if (size != sizeof(CloudDiskServiceDcacheHeader)) { + return size; + } + + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoCreate(const MetaBase &base) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + // validate the length of name, maximum length is 52*8 byte + uint32_t slots = GetDentrySlots(base.name.length()); + if (slots > DENTRY_PER_GROUP) { + LOGE("name is too long"); + return ENAMETOOLONG; + } + + std::unique_lock lock(mtx_); + DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de != nullptr) { + LOGE("this name dentry is exist"); + (void)FileRangeLock::FilePosLock(fd_, GetDentryGroupPos(ctx.bidx), DENTRYGROUP_SIZE, F_UNLCK); + return EEXIST; + } + uint32_t bitPos = 0; + unsigned long bidx = 0; + CloudDiskServiceDentryGroup dentryBlk = {0}; + uint32_t namehash = 0; + auto ret = GetCreateInfo(base, bitPos, namehash, bidx, dentryBlk); + if (ret) { + return ret; + } + off_t pos = GetDentryGroupPos(bidx); + if (!CreateDentry(dentryBlk, base, namehash, bitPos, base.recordId)) { + LOGI("CreateDentry fail, stop write."); + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + return EINVAL; + } + int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE); + if (size != DENTRYGROUP_SIZE) { + LOGD("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE); + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + return EINVAL; + } + ret = FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoRemove(const MetaBase &base, std::string &recordId) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + struct DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + struct CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de == nullptr) { + LOGD("find dentry failed"); + return ENOENT; + } + + de->revalidate = INVALIDATE; + recordId = std::string(reinterpret_cast(de->recordId), RECORD_ID_LEN); + + off_t ipos = GetDentryGroupPos(ctx.bidx); + ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct CloudDiskServiceDentryGroup)); + if (size != sizeof(struct CloudDiskServiceDentryGroup)) { + LOGE("write failed, ret = %{public}zd", size); + (void)FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + return EIO; + } + auto ret = FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoFlush(const MetaBase &base, std::string &recordId) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + CloudDiskServiceDentry *de = FindDentry(&ctx, false, INVALIDATE); + if (de == nullptr) { + LOGE("find dentry failed"); + return ENOENT; + } + + uint32_t bitPos = (de - ctx.page->nsl); + uint32_t slots = GetDentrySlots(de->namelen); + for (uint32_t i = 0; i < slots; i++) { + BitOps::ClearBit(bitPos + i, ctx.page->bitmap); + } + + off_t ipos = GetDentryGroupPos(ctx.bidx); + ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(CloudDiskServiceDentryGroup)); + if (size != sizeof(CloudDiskServiceDentryGroup)) { + LOGE("WriteFile failed!, ret = %{public}zd", size); + (void)FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + return EIO; + } + auto ret = FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoUpdate(const MetaBase &base, std::string &recordId) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + struct DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + struct CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de == nullptr) { + LOGD("find dentry failed"); + return ENOENT; + } + + de->atime = base.atime; + de->mtime = base.mtime; + de->size = base.size; + de->mode = base.mode; + recordId = std::string(reinterpret_cast(de->recordId), RECORD_ID_LEN); + + off_t ipos = GetDentryGroupPos(ctx.bidx); + ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct CloudDiskServiceDentryGroup)); + if (size != sizeof(struct CloudDiskServiceDentryGroup)) { + LOGE("write failed, ret = %{public}zd", size); + (void)FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + return EIO; + } + auto ret = FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoRenameOld(const MetaBase &base, std::string &recordId) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + struct DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + struct CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de == nullptr) { + LOGD("find dentry failed"); + return ENOENT; + } + + de->revalidate = INVALIDATE; + recordId = std::string(reinterpret_cast(de->recordId), RECORD_ID_LEN); + + off_t ipos = GetDentryGroupPos(ctx.bidx); + ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct CloudDiskServiceDentryGroup)); + if (size != sizeof(struct CloudDiskServiceDentryGroup)) { + LOGE("write failed, ret = %{public}zd", size); + (void)FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + return EIO; + } + auto ret = FileRangeLock::FilePosLock(fd_, ipos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoRenameNew(const MetaBase &base, std::string &recordId) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + // validate the length of name, maximum length is 52*8 byte + uint32_t slots = GetDentrySlots(base.name.length()); + if (slots > DENTRY_PER_GROUP) { + LOGE("name is too long"); + return ENAMETOOLONG; + } + + std::unique_lock lock(mtx_); + DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de != nullptr) { + LOGE("this name dentry is exist"); + (void)FileRangeLock::FilePosLock(fd_, GetDentryGroupPos(ctx.bidx), DENTRYGROUP_SIZE, F_UNLCK); + return EEXIST; + } + uint32_t bitPos = 0; + unsigned long bidx = 0; + CloudDiskServiceDentryGroup dentryBlk = {0}; + uint32_t namehash = 0; + auto ret = GetCreateInfo(base, bitPos, namehash, bidx, dentryBlk); + if (ret) { + return ret; + } + off_t pos = GetDentryGroupPos(bidx); + if (!CreateDentry(dentryBlk, base, namehash, bitPos, recordId)) { + LOGI("CreateDentry fail, stop write."); + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + return EINVAL; + } + int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE); + if (size != DENTRYGROUP_SIZE) { + LOGD("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE); + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + return EINVAL; + } + ret = FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + if (ret) { + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoRename(MetaBase &metaBase, const std::string &newName, + std::shared_ptr newMetaFile) +{ + std::string oldName = metaBase.name; + metaBase.name = newName; + auto ret = newMetaFile->DoCreate(metaBase); + if (ret != 0) { + LOGE("create dentry failed, ret = %{public}d", ret); + return ret; + } + metaBase.name = oldName; + std::string childRecordId; + ret = DoRemove(metaBase, childRecordId); + if (ret != 0) { + LOGE("remove dentry failed, ret = %{public}d", ret); + metaBase.name = newName; + (void)newMetaFile->DoFlush(metaBase, childRecordId); + return ret; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoLookupByName(MetaBase &base) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + struct DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, CloudDisk::CloudFileUtils::DentryHash(base.name), fd_); + struct CloudDiskServiceDentry *de = FindDentry(&ctx); + if (de == nullptr) { + LOGD("find dentry failed"); + return ENOENT; + } + (void)FileRangeLock::FilePosLock(fd_, GetDentryGroupPos(ctx.bidx), DENTRYGROUP_SIZE, F_UNLCK); + + base.mode = de->mode; + base.hash = de->hash; + base.size = de->size; + base.atime = de->atime; + base.mtime = de->mtime; + base.recordId = std::string(reinterpret_cast(de->recordId), RECORD_ID_LEN); + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::DoLookupByRecordId(MetaBase &base, uint8_t revalidate) +{ + if (fd_ < 0) { + LOGE("bad metafile fd"); + return EINVAL; + } + + std::unique_lock lock(mtx_); + struct DcacheLookupCtx ctx; + InitDcacheLookupCtx(&ctx, base, base.hash, fd_); + ctx.recordId = base.recordId; + struct CloudDiskServiceDentry *de = ((revalidate & VALIDATE) == 0) ? nullptr : FindDentry(&ctx, true, VALIDATE); + if (de == nullptr) { + de = ((revalidate & INVALIDATE) == 0) ? nullptr : FindDentry(&ctx, true, INVALIDATE); + if (de == nullptr) { + LOGE("find dentry by id failed"); + return ENOENT; + } + } + (void)FileRangeLock::FilePosLock(fd_, GetDentryGroupPos(ctx.bidx), DENTRYGROUP_SIZE, F_UNLCK); + + base.mode = de->mode; + base.hash = de->hash; + base.size = de->size; + base.atime = de->atime; + base.mtime = de->mtime; + base.name = std::string(reinterpret_cast(ctx.page->fileName[ctx.bitPos]), de->namelen); + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::GetCreateInfo(const MetaBase &base, uint32_t &bitPos, uint32_t &namehash, + unsigned long &bidx, struct CloudDiskServiceDentryGroup &dentryBlk) +{ + uint32_t level = 0; + namehash = CloudDisk::CloudFileUtils::DentryHash(base.name); + bool found = false; + while (!found) { + if (level == MAX_BUCKET_LEVEL) { + return ENOSPC; + } + bidx = GetBidxFromLevel(level, namehash); + unsigned long endBlock = bidx + BUCKET_BLOCKS; + int32_t ret = HandleFileByFd(endBlock, level); + if (ret != 0) { + return ret; + } + for (; bidx < endBlock; bidx++) { + off_t pos = GetDentryGroupPos(bidx); + ret = FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_WRLCK); + if (ret) { + return ret; + } + if (FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk) != DENTRYGROUP_SIZE) { + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + return ENOENT; + } + bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP); + if (bitPos < DENTRY_PER_GROUP) { + found = true; + break; + } + (void)FileRangeLock::FilePosLock(fd_, pos, DENTRYGROUP_SIZE, F_UNLCK); + } + ++level; + } + return E_OK; +} + +int32_t CloudDiskServiceMetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t level) +{ + struct stat fileStat; + int err = fstat(fd_, &fileStat); + if (err < 0) { + return EINVAL; + } + if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) && + ftruncate(fd_, GetDcacheFileSize(level))) { + return ENOENT; + } + return E_OK; +} + +MetaFileMgr& MetaFileMgr::GetInstance() +{ + static MetaFileMgr instance_; + return instance_; +} + +std::shared_ptr MetaFileMgr::GetCloudDiskServiceMetaFile(int32_t userId, + const uint32_t syncFolderIndex, const uint64_t inode) +{ + std::shared_ptr mFile = nullptr; + std::lock_guard lock(mtx_); + MetaFileKey key(userId, Convertor::ConvertToHex(syncFolderIndex) + Convertor::ConvertToHex(inode)); + auto it = metaFiles_.find(key); + if (it != metaFiles_.end()) { + metaFileList_.splice(metaFileList_.begin(), metaFileList_, it->second); + mFile = it->second->second; + } else { + if (metaFiles_.size() == MAX_META_FILE_NUM) { + auto deleteKey = metaFileList_.back().first; + metaFiles_.erase(deleteKey); + metaFileList_.pop_back(); + } + mFile = std::make_shared(userId, syncFolderIndex, inode); + metaFileList_.emplace_front(key, mFile); + metaFiles_[key] = metaFileList_.begin(); + } + return mFile; +} + +int32_t MetaFileMgr::GetRelativePath(const std::shared_ptr metaFile, std::string &path) +{ + auto pMetaFile = metaFile; + int32_t userId = metaFile->userId_; + uint32_t syncFolderIndex = Convertor::ConvertFromHex(metaFile->syncFolderIndex_); + while (pMetaFile->parentDentryFile_ != ROOT_PARENTDENTRYFILE) { + uint64_t inode = Convertor::ConvertFromHex(pMetaFile->parentDentryFile_); + auto parentMetaFile = GetCloudDiskServiceMetaFile(userId, syncFolderIndex, inode); + + MetaBase mBase(pMetaFile->selfRecordId_, false); + int32_t ret = parentMetaFile->DoLookupByRecordId(mBase, VALIDATE | INVALIDATE); + if (ret != 0) { + LOGE("lookup by id failed"); + return -1; + } + + path = "/" + mBase.name + path; + pMetaFile = parentMetaFile; + } + return E_OK; +} + +} // namespace OHOS::FileManagement::CloudDiskService diff --git a/services/clouddiskservice/sync_folder/src/cloud_disk_service_syncfolder.cpp b/services/clouddiskservice/sync_folder/src/cloud_disk_service_syncfolder.cpp new file mode 100644 index 000000000..9fa8c1700 --- /dev/null +++ b/services/clouddiskservice/sync_folder/src/cloud_disk_service_syncfolder.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud_disk_service_syncfolder.h" + +#include +#include +#include +#include +#include + +#include "cloud_disk_service_logfile.h" + +namespace OHOS::FileManagement::CloudDiskService { + +int32_t CloudDiskServiceSyncFolder::RegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex, + const std::string &path) +{ + return LogFileMgr::GetInstance().RegisterSyncFolder(userId, syncFolderIndex, path); +} + +int32_t CloudDiskServiceSyncFolder::UnRegisterSyncFolder(const int32_t userId, const uint64_t syncFolderIndex) +{ + return LogFileMgr::GetInstance().UnRegisterSyncFolder(userId, syncFolderIndex); +} + +void CloudDiskServiceSyncFolder::RegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex) +{ + LogFileMgr::GetInstance().RegisterSyncFolderChanges(userId, syncFolderIndex); +} + +void CloudDiskServiceSyncFolder::UnRegisterSyncFolderChanges(const int32_t userId, const uint64_t syncFolderIndex) +{ + LogFileMgr::GetInstance().UnRegisterSyncFolderChanges(userId, syncFolderIndex); +} + +int32_t CloudDiskServiceSyncFolder::GetSyncFolderChanges(const int32_t userId, const uint32_t syncFolderIndex, + const uint64_t start, const uint64_t count, + struct ChangesResult &changesResult) +{ + return LogFileMgr::GetInstance().PraseRequest(userId, syncFolderIndex, start, count, changesResult); +} + +int32_t CloudDiskServiceSyncFolder::SetSyncFolderChanges(const struct EventInfo &eventInfo) +{ + return LogFileMgr::GetInstance().ProduceRequest(eventInfo); +} + +} // namespace OHOS::FileManagement::CloudDiskService \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/src/convertor.cpp b/services/clouddiskservice/sync_folder/src/convertor.cpp new file mode 100644 index 000000000..d345a50e2 --- /dev/null +++ b/services/clouddiskservice/sync_folder/src/convertor.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "convertor.h" + +#include "utils_log.h" + +namespace OHOS::FileManagement::CloudDiskService { + +std::string Convertor::ConvertToHex(uint64_t value) +{ + int length = 16; + uint64_t base = 10; + uint64_t offset = 4; + char buffer[length + 1]; + char *p = buffer + length; + *p = '\0'; + + for (int i = 0; i < length; i++) { + --p; + int digit = value & 0xF; + *p = digit < base ? '0' + digit : 'a' + digit - base; + value >>= offset; + } + + return std::string(buffer); +} + +uint64_t Convertor::ConvertFromHex(const std::string &hex) +{ + uint64_t value = 0; + uint64_t base = 10; + uint64_t offset = 4; + for (char c : hex) { + value <<= offset; + if (c >= '0' && c <= '9') { + value |= c - '0'; + } else if (c >= 'a' && c <= 'f') { + value |= c - 'a' + base; + } else if (c >= 'A' && c <= 'F') { + value |= c - 'A' + base; + } else { + LOGE("Invalid hex string: %{public}s", hex.c_str()); + return 0; + } + } + return value; +} + +} // namespace OHOS::FileManagement::CloudDiskService \ No newline at end of file diff --git a/services/clouddiskservice/sync_folder/src/uuid_helper.cpp b/services/clouddiskservice/sync_folder/src/uuid_helper.cpp new file mode 100644 index 000000000..2e98f406f --- /dev/null +++ b/services/clouddiskservice/sync_folder/src/uuid_helper.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "uuid_helper.h" + +namespace OHOS::FileManagement::CloudDiskService { + +std::string UuidHelper::GenerateUuid() +{ + std::string guid(""); + uuid_t uuid; + uuid_generate(uuid); + char str[50] = {}; + uuid_unparse(uuid, str); + guid.assign(str); + return guid; +} + +std::string UuidHelper::GenerateUuidWithoutDelim() +{ + std::string str = GenerateUuid(); + std::string uuid; + for (auto &ch : str) { + if (ch != '-') { + uuid += ch; + } + } + return uuid; +} + +std::string UuidHelper::GenerateUuidOnly() +{ + std::string guid(""); + uuid_t uuid; + uuid_generate(uuid); + int length = 16; + guid.assign(reinterpret_cast(uuid), length); + return guid; +} + +} // namespace OHOS::FileManagement::CloudDiskService \ No newline at end of file diff --git a/utils/clouddiskservice/include/cloud_disk_service_error.h b/utils/clouddiskservice/include/cloud_disk_service_error.h new file mode 100644 index 000000000..a92f352b6 --- /dev/null +++ b/utils/clouddiskservice/include/cloud_disk_service_error.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLOUD_DISK_SERVICE_ERROR_H +#define CLOUD_DISK_SERVICE_ERROR_H + +namespace OHOS::FileManagement::CloudDiskService { + +enum CloudDiskServiceErrCode : uint32_t { + E_OK = 0, + E_INVALID_ARG = 34400001, + E_SYNC_FOLDER_PATH_UNAUTHORIZED, + E_IPC_FAILED, + E_SYNC_FOLDER_LIMIT_EXCEEDED, + E_CONFLICT_THIS_APP, + E_CONFLICT_OTHER_APP, + E_REGISTER_SYNC_FOLDER_FAILED, + E_SYNC_FOLDER_NOT_REGISTERED, + E_UNREGISTER_SYNC_FOLDER_FAILED, + E_SYNC_FOLDER_PATH_NOT_EXIST, + E_LISTENER_NOT_REGISTERED, + E_LISTENER_ALREADY_REGISTERED, + E_INVALID_CHANGE_SEQUENCE, + E_TRY_AGAIN, + E_INTERNAL_ERROR, + E_NOT_SUPPORTED, + E_PERMISSION_DENIED = 201, +}; + +} // namespace OHOS::FileManagement::CloudDiskService + +#endif // CLOUD_DISK_SERVICE_ERROR_H -- Gitee