1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "meta_file.h" 17 18#include <ctime> 19#include <fcntl.h> 20#include <iomanip> 21#include <sstream> 22#include <sys/stat.h> 23 24#include "cloud_file_utils.h" 25#include "dfs_error.h" 26#include "directory_ex.h" 27#include "file_utils.h" 28#include "securec.h" 29#include "string_ex.h" 30#include "sys/xattr.h" 31#include "utils_log.h" 32 33namespace OHOS { 34namespace FileManagement { 35constexpr uint32_t DENTRYGROUP_SIZE = 4096; 36constexpr uint32_t DENTRY_NAME_LEN = 8; 37constexpr uint32_t DENTRY_RESERVED_LENGTH = 3; 38constexpr uint32_t DENTRY_PER_GROUP = 60; 39constexpr uint32_t DENTRY_BITMAP_LENGTH = 8; 40constexpr uint32_t DENTRY_GROUP_RESERVED = 7; 41constexpr uint32_t CLOUD_RECORD_ID_LEN = 33; 42constexpr uint32_t DENTRYGROUP_HEADER = 4096; 43constexpr uint32_t MAX_BUCKET_LEVEL = 63; 44constexpr uint32_t BUCKET_BLOCKS = 2; 45constexpr uint32_t BITS_PER_BYTE = 8; 46constexpr uint32_t HMDFS_SLOT_LEN_BITS = 3; 47constexpr uint32_t DIR_SIZE = 4096; 48 49#pragma pack(push, 1) 50struct HmdfsDentry { 51 uint32_t hash{0}; 52 uint16_t mode{0}; 53 uint16_t namelen{0}; 54 uint64_t size{0}; 55 uint64_t mtime{0}; 56 uint8_t recordId[CLOUD_RECORD_ID_LEN]{0}; 57 /* reserved bytes for long term extend, total 60 bytes */ 58 union { 59 struct { 60 uint8_t fileType : 2; 61 }; 62 uint8_t reserved[DENTRY_RESERVED_LENGTH]; 63 }; 64}; 65 66struct HmdfsDentryGroup { 67 uint8_t dentryVersion; 68 uint8_t bitmap[DENTRY_BITMAP_LENGTH]; 69 HmdfsDentry nsl[DENTRY_PER_GROUP]; 70 uint8_t fileName[DENTRY_PER_GROUP][DENTRY_NAME_LEN]; 71 uint8_t reserved[DENTRY_GROUP_RESERVED]; 72}; 73static_assert(sizeof(HmdfsDentryGroup) == DENTRYGROUP_SIZE); 74 75struct HmdfsDcacheHeader { 76 uint64_t dcacheCrtime{0}; 77 uint64_t dcacheCrtimeNsec{0}; 78 79 uint64_t dentryCtime{0}; 80 uint64_t dentryCtimeNsec{0}; 81 82 uint64_t dentryCount{0}; 83}; 84#pragma pack(pop) 85 86static uint64_t PathHash(const std::string &path, bool caseSense) 87{ 88 uint64_t res = 0; 89 const char *kp = path.c_str(); 90 91 while (*kp) { 92 char c = *kp; 93 if (!caseSense) { 94 c = tolower(c); 95 } 96 res = (res << 5) - res + static_cast<uint64_t>(c); /* hash shift width 5 */ 97 kp++; 98 } 99 return res; 100} 101 102static std::string GetDentryfileName(const std::string &path, bool caseSense) 103{ 104 // path should be like "/", "/dir/", "/dir/dir/" ... 105 constexpr uint32_t fileNameLen = 32; 106 char buf[fileNameLen + 1] = {0}; 107 uint64_t fileHash = PathHash(path, caseSense); 108 int ret = snprintf_s(buf, fileNameLen + 1, fileNameLen, "cloud_%016llx", fileHash); 109 if (ret < 0) { 110 LOGE("filename failer fileHash ret :%{public}d", ret); 111 } 112 return buf; 113} 114 115static std::string GetDentryfileByPath(uint32_t userId, const std::string &path, bool caseSense = false) 116{ 117 std::string cacheDir = 118 "/data/service/el2/" + std::to_string(userId) + "/hmdfs/cache/account_cache/dentry_cache/cloud/"; 119 std::string dentryFileName = GetDentryfileName(path, caseSense); 120 121 return cacheDir + dentryFileName; 122} 123 124std::string MetaFile::GetParentDir(const std::string &path) 125{ 126 if ((path == "/") || (path == "")) { 127 return ""; 128 } 129 130 auto pos = path.find_last_of('/'); 131 if ((pos == std::string::npos) || (pos == 0)) { 132 return "/"; 133 } 134 135 return path.substr(0, pos); 136} 137 138std::string MetaFile::GetFileName(const std::string &path) 139{ 140 if ((path == "/") || (path == "")) { 141 return ""; 142 } 143 144 auto pos = path.find_last_of('/'); 145 if (pos == std::string::npos) { 146 return ""; 147 } 148 149 return path.substr(pos + 1); 150} 151 152static std::shared_ptr<MetaFile> GetParentMetaFile(uint32_t userId, const std::string &path) 153{ 154 std::string parentPath = MetaFile::GetParentDir(path); 155 if (parentPath == "") { 156 return nullptr; 157 } 158 159 return MetaFileMgr::GetInstance().GetMetaFile(userId, parentPath); 160} 161 162MetaFile::MetaFile(uint32_t userId, const std::string &path) 163{ 164 path_ = path; 165 cacheFile_ = GetDentryfileByPath(userId, path); 166 fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)}; 167 if (fd_ < 0) { 168 LOGE("fd=%{public}d, errno :%{public}d", fd_.Get(), errno); 169 return; 170 } 171 172 int ret = fsetxattr(fd_, "user.hmdfs_cache", path.c_str(), path.size(), 0); 173 if (ret != 0) { 174 LOGE("setxattr failed, errno %{public}d, cacheFile_ %s", errno, GetAnonyString(cacheFile_).c_str()); 175 } 176 177 /* lookup and create in parent */ 178 parentMetaFile_ = GetParentMetaFile(userId, path); 179 std::string dirName = GetFileName(path); 180 if ((parentMetaFile_ == nullptr) || (dirName == "")) { 181 return; 182 } 183 MetaBase m(dirName, std::to_string(PathHash(path, false)) + std::to_string(std::time(nullptr))); 184 ret = parentMetaFile_->DoLookup(m); 185 if (ret != E_OK) { 186 m.mode = S_IFDIR; 187 m.size = DIR_SIZE; 188 ret = parentMetaFile_->DoCreate(m); 189 if (ret != E_OK) { 190 LOGE("create parent failed, ret %{public}d", ret); 191 } 192 } 193} 194 195MetaFile::~MetaFile() 196{ 197} 198 199static inline uint32_t GetDentrySlots(size_t nameLen) 200{ 201 return static_cast<uint32_t>((nameLen + BITS_PER_BYTE - 1) >> HMDFS_SLOT_LEN_BITS); 202} 203 204static inline off_t GetDentryGroupPos(size_t bidx) 205{ 206 return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER; 207} 208 209static inline uint64_t GetDentryGroupCnt(uint64_t size) 210{ 211 return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0; 212} 213 214static uint32_t GetOverallBucket(uint32_t level) 215{ 216 if (level >= MAX_BUCKET_LEVEL) { 217 LOGI("level = %{public}d overflow", level); 218 return 0; 219 } 220 uint64_t buckets = (1ULL << (level + 1)) - 1; 221 return static_cast<uint32_t>(buckets); 222} 223 224static size_t GetDcacheFileSize(uint32_t level) 225{ 226 size_t buckets = GetOverallBucket(level); 227 return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER; 228} 229 230static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset) 231{ 232 if (level >= MAX_BUCKET_LEVEL) { 233 return 0; 234 } 235 236 uint64_t curLevelMaxBucks = (1ULL << level); 237 if (buckoffset >= curLevelMaxBucks) { 238 return 0; 239 } 240 241 return static_cast<uint32_t>(curLevelMaxBucks) + buckoffset - 1; 242} 243 244static uint32_t GetBucketByLevel(uint32_t level) 245{ 246 if (level >= MAX_BUCKET_LEVEL) { 247 LOGI("level = %{public}d overflow", level); 248 return 0; 249 } 250 251 uint64_t buckets = (1ULL << level); 252 return static_cast<uint32_t>(buckets); 253} 254 255static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots) 256{ 257 uint32_t bitStart = 0; 258 259 while (1) { 260 uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart); 261 if (zeroStart >= maxSlots) { 262 return maxSlots; 263 } 264 265 uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart); 266 if (zeroEnd - zeroStart >= slots) { 267 return zeroStart; 268 } 269 270 bitStart = zeroEnd + 1; 271 if (zeroEnd + 1 >= maxSlots) { 272 return maxSlots; 273 } 274 } 275 return 0; 276} 277 278static bool UpdateDentry(HmdfsDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos) 279{ 280 HmdfsDentry *de; 281 const std::string name = base.name; 282 uint32_t slots = GetDentrySlots(name.length()); 283 284 de = &d.nsl[bitPos]; 285 de->hash = nameHash; 286 de->namelen = name.length(); 287 errno_t ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length()); 288 if (ret != EOK) { 289 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length()); 290 return false; 291 } 292 de->mtime = base.mtime; 293 de->fileType = base.fileType; 294 de->size = base.size; 295 de->mode = base.mode; 296 ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length()); 297 if (ret != EOK) { 298 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length()); 299 return false; 300 } 301 302 for (uint32_t i = 0; i < slots; i++) { 303 BitOps::SetBit(bitPos + i, d.bitmap); 304 if (i) { 305 (de + i)->namelen = 0; 306 } 307 } 308 return true; 309} 310 311int32_t MetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t &level) 312{ 313 struct stat fileStat; 314 int err = fstat(fd_, &fileStat); 315 if (err < 0) { 316 return EINVAL; 317 } 318 if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) && 319 ftruncate(fd_, GetDcacheFileSize(level))) { 320 return ENOENT; 321 } 322 return E_OK; 323} 324 325int32_t MetaFile::DoCreate(const MetaBase &base) 326{ 327 if (fd_ < 0) { 328 LOGE("bad metafile fd"); 329 return EINVAL; 330 } 331 332 off_t pos = 0; 333 uint32_t level = 0; 334 uint32_t bitPos = 0; 335 unsigned long bidx; 336 HmdfsDentryGroup dentryBlk = {0}; 337 338 std::unique_lock<std::mutex> lock(mtx_); 339 FileRangeLock fileLock(fd_, 0, 0); 340 uint32_t namehash = CloudDisk::CloudFileUtils::DentryHash(base.name); 341 342 bool found = false; 343 while (!found) { 344 if (level == MAX_BUCKET_LEVEL) { 345 return ENOSPC; 346 } 347 bidx = BUCKET_BLOCKS * GetBucketaddr(level, namehash % GetBucketByLevel(level)); 348 unsigned long endBlock = bidx + BUCKET_BLOCKS; 349 350 int32_t ret = MetaFile::HandleFileByFd(endBlock, level); 351 if (ret != E_OK) { 352 return ret; 353 } 354 355 for (; bidx < endBlock; bidx++) { 356 pos = GetDentryGroupPos(bidx); 357 if (FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk) != DENTRYGROUP_SIZE) { 358 return ENOENT; 359 } 360 bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP); 361 if (bitPos < DENTRY_PER_GROUP) { 362 found = true; 363 break; 364 } 365 } 366 ++level; 367 } 368 369 pos = GetDentryGroupPos(bidx); 370 if (!UpdateDentry(dentryBlk, base, namehash, bitPos)) { 371 LOGI("UpdateDentry fail, stop write."); 372 return EINVAL; 373 } 374 int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE); 375 if (size != DENTRYGROUP_SIZE) { 376 LOGI("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE); 377 return EINVAL; 378 } 379 380 return E_OK; 381} 382 383struct DcacheLookupCtx { 384 int fd{-1}; 385 std::string name{}; 386 uint32_t hash{0}; 387 uint32_t bidx{0}; 388 std::unique_ptr<HmdfsDentryGroup> page{nullptr}; 389}; 390 391static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd) 392{ 393 ctx->fd = fd; 394 ctx->name = base.name; 395 ctx->bidx = 0; 396 ctx->page = nullptr; 397 ctx->hash = CloudDisk::CloudFileUtils::DentryHash(ctx->name); 398} 399 400static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx) 401{ 402 auto dentryBlk = std::make_unique<HmdfsDentryGroup>(); 403 404 off_t pos = GetDentryGroupPos(index); 405 ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get()); 406 if (size != DENTRYGROUP_SIZE) { 407 return nullptr; 408 } 409 return dentryBlk; 410} 411 412static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name) 413{ 414 int maxLen = 0; 415 uint32_t bitPos = 0; 416 HmdfsDentry *de = nullptr; 417 418 while (bitPos < DENTRY_PER_GROUP) { 419 if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) { 420 bitPos++; 421 maxLen++; 422 continue; 423 } 424 de = &dentryBlk.nsl[bitPos]; 425 if (!de->namelen) { 426 bitPos++; 427 continue; 428 } 429 430 if (de->hash == namehash && de->namelen == name.length() && 431 !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) { 432 return de; 433 } 434 maxLen = 0; 435 bitPos += GetDentrySlots(de->namelen); 436 } 437 438 return nullptr; 439} 440 441static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx) 442 __attribute__((no_sanitize("unsigned-integer-overflow"))) 443{ 444 HmdfsDentry *de = nullptr; 445 446 uint32_t nbucket = GetBucketByLevel(level); 447 if (!nbucket) { 448 return de; 449 } 450 451 uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS; 452 uint32_t endBlock = bidx + BUCKET_BLOCKS; 453 454 for (; bidx < endBlock; bidx++) { 455 auto dentryBlk = FindDentryPage(bidx, ctx); 456 if (dentryBlk == nullptr) { 457 break; 458 } 459 460 de = FindInBlock(*dentryBlk, ctx->hash, ctx->name); 461 if (de != nullptr) { 462 ctx->page = std::move(dentryBlk); 463 break; 464 } 465 } 466 ctx->bidx = bidx; 467 return de; 468} 469 470static uint32_t GetMaxLevel(int32_t fd) 471{ 472 struct stat st; 473 if (fstat(fd, &st) == -1) { 474 return MAX_BUCKET_LEVEL; 475 } 476 uint32_t blkNum = static_cast<uint32_t>(st.st_size) / DENTRYGROUP_SIZE + 1; 477 uint32_t maxLevel = 0; 478 blkNum >>= 1; 479 while (blkNum > 1) { 480 blkNum >>= 1; 481 maxLevel++; 482 } 483 return maxLevel; 484} 485 486static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx) 487{ 488 uint32_t maxLevel = GetMaxLevel(ctx->fd); 489 for (uint32_t level = 0; level < maxLevel; level++) { 490 HmdfsDentry *de = InLevel(level, ctx); 491 if (de != nullptr) { 492 return de; 493 } 494 } 495 return nullptr; 496} 497 498int32_t MetaFile::DoRemove(const MetaBase &base) 499{ 500 if (fd_ < 0) { 501 LOGE("bad metafile fd"); 502 return EINVAL; 503 } 504 505 std::unique_lock<std::mutex> lock(mtx_); 506 FileRangeLock fileLock(fd_, 0, 0); 507 DcacheLookupCtx ctx; 508 InitDcacheLookupCtx(&ctx, base, fd_); 509 HmdfsDentry *de = FindDentry(&ctx); 510 if (de == nullptr) { 511 LOGE("find dentry failed"); 512 return ENOENT; 513 } 514 515 uint32_t bitPos = (de - ctx.page->nsl); 516 uint32_t slots = GetDentrySlots(de->namelen); 517 for (uint32_t i = 0; i < slots; i++) { 518 BitOps::ClearBit(bitPos + i, ctx.page->bitmap); 519 } 520 521 off_t ipos = GetDentryGroupPos(ctx.bidx); 522 ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(HmdfsDentryGroup)); 523 if (size != sizeof(HmdfsDentryGroup)) { 524 LOGE("WriteFile failed!, ret = %{public}zd", size); 525 return EIO; 526 } 527 528 return E_OK; 529} 530 531int32_t MetaFile::DoLookup(MetaBase &base) 532{ 533 if (fd_ < 0) { 534 LOGE("bad metafile fd"); 535 return EINVAL; 536 } 537 538 std::unique_lock<std::mutex> lock(mtx_); 539 FileRangeLock fileLock(fd_, 0, 0); 540 struct DcacheLookupCtx ctx; 541 InitDcacheLookupCtx(&ctx, base, fd_); 542 struct HmdfsDentry *de = FindDentry(&ctx); 543 if (de == nullptr) { 544 LOGD("find dentry failed"); 545 return ENOENT; 546 } 547 548 base.size = de->size; 549 base.mtime = de->mtime; 550 base.mode = de->mode; 551 base.fileType = de->fileType; 552 base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN - 1); 553 554 return E_OK; 555} 556 557int32_t MetaFile::DoUpdate(const MetaBase &base) 558{ 559 if (fd_ < 0) { 560 LOGE("bad metafile fd"); 561 return EINVAL; 562 } 563 564 std::unique_lock<std::mutex> lock(mtx_); 565 FileRangeLock fileLock(fd_, 0, 0); 566 struct DcacheLookupCtx ctx; 567 InitDcacheLookupCtx(&ctx, base, fd_); 568 struct HmdfsDentry *de = FindDentry(&ctx); 569 if (de == nullptr) { 570 LOGI("find dentry failed"); 571 return ENOENT; 572 } 573 574 de->mtime = base.mtime; 575 de->size = base.size; 576 de->mode = base.mode; 577 de->fileType = base.fileType; 578 if (memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length())) { 579 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length()); 580 } 581 582 off_t ipos = GetDentryGroupPos(ctx.bidx); 583 ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct HmdfsDentryGroup)); 584 if (size != sizeof(struct HmdfsDentryGroup)) { 585 LOGI("write failed, ret = %{public}zd", size); 586 return EIO; 587 } 588 return E_OK; 589} 590 591int32_t MetaFile::DoRename(const MetaBase &oldBase, const std::string &newName) 592{ 593 MetaBase base{oldBase.name}; 594 int32_t ret = DoLookup(base); 595 if (ret) { 596 LOGE("ret = %{public}d, lookup %s failed", ret, base.name.c_str()); 597 return ret; 598 } 599 600 base.name = newName; 601 ret = DoCreate(base); 602 if (ret) { 603 LOGE("ret = %{public}d, create %s failed", ret, base.name.c_str()); 604 return ret; 605 } 606 607 base.name = oldBase.name; 608 ret = DoRemove(oldBase); 609 if (ret) { 610 LOGE("ret = %{public}d, remove %s failed", ret, oldBase.name.c_str()); 611 base.name = newName; 612 (void)DoRemove(base); 613 return ret; 614 } 615 616 return E_OK; 617} 618 619static int32_t DecodeDentrys(const HmdfsDentryGroup &dentryGroup, std::vector<MetaBase> &bases) 620{ 621 for (uint32_t i = 0; i < DENTRY_PER_GROUP; i++) { 622 int len = dentryGroup.nsl[i].namelen; 623 if (!BitOps::TestBit(i, dentryGroup.bitmap) || len == 0 || len >= PATH_MAX) { 624 continue; 625 } 626 627 std::string name(reinterpret_cast<const char *>(dentryGroup.fileName[i]), len); 628 629 MetaBase base(name); 630 base.mode = dentryGroup.nsl[i].mode; 631 base.mtime = dentryGroup.nsl[i].mtime; 632 base.size = dentryGroup.nsl[i].size; 633 base.fileType = dentryGroup.nsl[i].fileType; 634 base.cloudId = std::string(reinterpret_cast<const char *>(dentryGroup.nsl[i].recordId), CLOUD_RECORD_ID_LEN); 635 bases.emplace_back(base); 636 } 637 return 0; 638} 639 640int32_t MetaFile::LoadChildren(std::vector<MetaBase> &bases) 641{ 642 if (fd_ < 0) { 643 LOGE("bad metafile fd"); 644 return EINVAL; 645 } 646 647 std::lock_guard<std::mutex> lock(mtx_); 648 FileRangeLock fileLock(fd_, 0, 0); 649 struct stat fileStat; 650 int ret = fstat(fd_, &fileStat); 651 if (ret != E_OK) { 652 return EINVAL; 653 } 654 655 uint64_t fileSize = static_cast<uint64_t>(fileStat.st_size); 656 uint64_t groupCnt = GetDentryGroupCnt(fileSize); 657 HmdfsDentryGroup dentryGroup; 658 659 for (uint64_t i = 1; i < groupCnt + 1; i++) { 660 uint64_t off = i * sizeof(HmdfsDentryGroup); 661 FileUtils::ReadFile(fd_, off, sizeof(HmdfsDentryGroup), &dentryGroup); 662 DecodeDentrys(dentryGroup, bases); 663 } 664 return E_OK; 665} 666 667MetaFileMgr& MetaFileMgr::GetInstance() 668{ 669 static MetaFileMgr instance_; 670 return instance_; 671} 672 673std::shared_ptr<MetaFile> MetaFileMgr::GetMetaFile(uint32_t userId, const std::string &path) 674{ 675 std::shared_ptr<MetaFile> mFile = nullptr; 676 std::lock_guard<std::recursive_mutex> lock(mtx_); 677 678 MetaFileKey key(userId, path); 679 auto it = metaFiles_.find(key); 680 if (it != metaFiles_.end()) { 681 metaFileList_.splice(metaFileList_.begin(), metaFileList_, it->second); 682 mFile = it->second->second; 683 } else { 684 if (metaFiles_.size() == MAX_META_FILE_NUM) { 685 auto deleteKey = metaFileList_.back().first; 686 metaFiles_.erase(deleteKey); 687 metaFileList_.pop_back(); 688 } 689 mFile = std::make_shared<MetaFile>(userId, path); 690 metaFileList_.emplace_front(key, mFile); 691 metaFiles_[key] = metaFileList_.begin(); 692 } 693 return mFile; 694} 695 696void MetaFileMgr::ClearAll() 697{ 698 std::lock_guard<std::recursive_mutex> lock(mtx_); 699 metaFiles_.clear(); 700 metaFileList_.clear(); 701} 702 703std::string MetaFileMgr::RecordIdToCloudId(const std::string hexStr) 704{ 705 std::string result; 706 constexpr std::size_t offset = 2; 707 constexpr int changeBase = 16; 708 for (std::size_t i = 0; i < hexStr.length(); i += offset) { 709 std::string hexByte = hexStr.substr(i, offset); 710 char *endPtr; 711 unsigned long hexValue = std::strtoul(hexByte.c_str(), &endPtr, changeBase); 712 713 if (endPtr != hexByte.c_str() + hexByte.length()) { 714 LOGE("Invalid hexadecimal string: %{public}s", hexStr.c_str()); 715 return ""; 716 } 717 result += static_cast<char>(hexValue); 718 } 719 if (result.size() > CLOUD_RECORD_ID_LEN) { 720 LOGE("Invalid result length %{public}zu", result.size()); 721 return ""; 722 } 723 724 return result; 725} 726 727std::string MetaFileMgr::CloudIdToRecordId(const std::string cloudId) 728{ 729 std::stringstream result; 730 constexpr int width = 2; 731 for (std::size_t i = 0; i < cloudId.length(); i++) { 732 uint8_t u8Byte = cloudId[i]; 733 result << std::setw(width) << std::setfill('0') << std::hex << static_cast<int>(u8Byte); 734 } 735 return result.str(); 736} 737 738} // namespace FileManagement 739} // namespace OHOS 740