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