1/*
2 * Copyright (c) 2024 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 "file_utils.h"
27#include "securec.h"
28#include "string_ex.h"
29#include "sys/xattr.h"
30#include "utils_directory.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 = 4;
38constexpr uint32_t DENTRY_PER_GROUP = 52;
39constexpr uint32_t DENTRY_BITMAP_LENGTH = 7;
40constexpr uint32_t DENTRY_GROUP_RESERVED = 32;
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 FILE_TYPE_OFFSET = 2;
48constexpr uint32_t NO_UPLOAD_OFFSET = 4;
49
50#pragma pack(push, 1)
51struct HmdfsDentry {
52    uint32_t hash{0};
53    uint16_t mode{0};
54    uint16_t namelen{0};
55    uint64_t size{0};
56    uint64_t mtime{0};
57    uint64_t atime{0};
58    uint8_t recordId[CLOUD_RECORD_ID_LEN]{0};
59    uint8_t flags{0};
60    /* reserved bytes for long term extend, total 60 bytes */
61    uint8_t reserved[DENTRY_RESERVED_LENGTH];
62};
63
64struct HmdfsDentryGroup {
65    uint8_t dentryVersion;
66    uint8_t bitmap[DENTRY_BITMAP_LENGTH];
67    struct HmdfsDentry nsl[DENTRY_PER_GROUP];
68    uint8_t fileName[DENTRY_PER_GROUP][DENTRY_NAME_LEN];
69    uint8_t reserved[DENTRY_GROUP_RESERVED];
70};
71static_assert(sizeof(HmdfsDentryGroup) == DENTRYGROUP_SIZE);
72
73struct HmdfsDcacheHeader {
74    uint64_t dcacheCrtime{0};
75    uint64_t dcacheCrtimeNsec{0};
76
77    uint64_t dentryCtime{0};
78    uint64_t dentryCtimeNsec{0};
79
80    uint64_t dentryCount{0};
81};
82#pragma pack(pop)
83
84void MetaHelper::SetFileType(struct HmdfsDentry *de, uint8_t fileType)
85{
86    de->flags &= 0x13;
87    de->flags |= (fileType << FILE_TYPE_OFFSET);
88}
89
90void MetaHelper::SetPosition(struct HmdfsDentry *de, uint8_t position)
91{
92    de->flags &= 0xFC;
93    de->flags |= position;
94}
95
96void MetaHelper::SetNoUpload(struct HmdfsDentry *de, uint8_t noUpload)
97{
98    de->flags &= 0xEF;
99    de->flags |= (noUpload << NO_UPLOAD_OFFSET);
100}
101
102uint8_t MetaHelper::GetNoUpload(const struct HmdfsDentry *de)
103{
104    return (de->flags & 0x10) >> NO_UPLOAD_OFFSET;
105}
106
107uint8_t MetaHelper::GetFileType(const struct HmdfsDentry *de)
108{
109    return (de->flags & 0xC) >> FILE_TYPE_OFFSET;
110}
111
112uint8_t MetaHelper::GetPosition(const struct HmdfsDentry *de)
113{
114    return de->flags & 0x3;
115}
116
117static std::string GetCloudDiskDentryFileByPath(uint32_t userId, const std::string &bundleName,
118    const std::string &cloudId)
119{
120    std::string cacheDir =
121        "/data/service/el2/" + std::to_string(userId) +
122        "/hmdfs/cloud/data/" + bundleName + "/" +
123        std::to_string(CloudDisk::CloudFileUtils::GetBucketId(cloudId)) + "/";
124    std::string dentryFileName = MetaFileMgr::GetInstance().CloudIdToRecordId(cloudId);
125    Storage::DistributedFile::Utils::ForceCreateDirectory(cacheDir, STAT_MODE_DIR);
126    return cacheDir + dentryFileName;
127}
128
129CloudDiskMetaFile::CloudDiskMetaFile(uint32_t userId, const std::string &bundleName, const std::string &cloudId)
130{
131    userId_ = userId;
132    bundleName_ = bundleName;
133    cloudId_ = cloudId;
134    cacheFile_ = GetCloudDiskDentryFileByPath(userId_, bundleName_, cloudId_);
135    fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)};
136    LOGD("CloudDiskMetaFile cloudId=%{public}s, path=%{public}s", cloudId_.c_str(), GetAnonyString(cacheFile_).c_str());
137    LOGD("CloudDiskMetaFile fd=%{public}d, errno :%{public}d", fd_.Get(), errno);
138
139    HmdfsDcacheHeader header{};
140    (void)FileUtils::ReadFile(fd_, 0, sizeof(header), &header);
141}
142
143std::string CloudDiskMetaFile::GetDentryFilePath()
144{
145    return cacheFile_;
146}
147
148int32_t CloudDiskMetaFile::DoLookupAndUpdate(const std::string &name, CloudDiskMetaFileCallBack callback)
149{
150    MetaBase m(name);
151    /* lookup and create in parent */
152    int32_t ret = DoLookup(m);
153    if (ret != E_OK) {
154        callback(m);
155        ret = DoCreate(m);
156        if (ret != E_OK) {
157            LOGE("create dentry file failed, ret %{public}d", ret);
158            return ret;
159        }
160        return ret;
161    } else {
162        callback(m);
163        ret = DoUpdate(m);
164        if (ret != E_OK) {
165            LOGE("update dentry file failed, ret %{public}d", ret);
166            return ret;
167        }
168    }
169    return E_OK;
170}
171
172int32_t CloudDiskMetaFile::DoChildUpdate(const std::string &name, CloudDiskMetaFileCallBack callback)
173{
174    MetaBase m(name);
175    /* lookup and update */
176    int32_t ret = DoLookup(m);
177    if (ret != E_OK) {
178        LOGE("lookup dentry file failed, ret %{public}d", ret);
179        return ret;
180    } else {
181        callback(m);
182        ret = DoUpdate(m);
183        if (ret != E_OK) {
184            LOGE("update dentry file failed, ret %{public}d", ret);
185            return ret;
186        }
187    }
188    return E_OK;
189}
190
191int32_t CloudDiskMetaFile::DoLookupAndRemove(MetaBase &metaBase)
192{
193    /* lookup and remove in parent */
194    int32_t ret = DoLookup(metaBase);
195    if (ret == E_OK) {
196        ret = DoRemove(metaBase);
197        if (ret != E_OK) {
198            LOGE("remove dentry file failed, ret %{public}d", ret);
199            return ret;
200        }
201        return E_OK;
202    }
203    return E_OK;
204}
205
206CloudDiskMetaFile::~CloudDiskMetaFile()
207{
208}
209
210static inline uint32_t GetDentrySlots(size_t nameLen)
211{
212    return static_cast<uint32_t>((nameLen + BITS_PER_BYTE - 1) >> HMDFS_SLOT_LEN_BITS);
213}
214
215static inline off_t GetDentryGroupPos(size_t bidx)
216{
217    return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER;
218}
219
220static inline uint64_t GetDentryGroupCnt(uint64_t size)
221{
222    return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0;
223}
224
225static uint32_t GetOverallBucket(uint32_t level)
226{
227    if (level >= MAX_BUCKET_LEVEL) {
228        LOGD("level = %{public}d overflow", level);
229        return 0;
230    }
231    uint64_t buckets = (1ULL << (level + 1)) - 1;
232    return static_cast<uint32_t>(buckets);
233}
234
235static size_t GetDcacheFileSize(uint32_t level)
236{
237    size_t buckets = GetOverallBucket(level);
238    return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER;
239}
240
241static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset)
242{
243    if (level >= MAX_BUCKET_LEVEL) {
244        return 0;
245    }
246
247    uint64_t curLevelMaxBucks = (1ULL << level);
248    if (buckoffset >= curLevelMaxBucks) {
249        return 0;
250    }
251
252    return static_cast<uint32_t>(curLevelMaxBucks) + buckoffset - 1;
253}
254
255static uint32_t GetBucketByLevel(uint32_t level)
256{
257    if (level >= MAX_BUCKET_LEVEL) {
258        LOGD("level = %{public}d overflow", level);
259        return 0;
260    }
261
262    uint64_t buckets = (1ULL << level);
263    return static_cast<uint32_t>(buckets);
264}
265
266static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots)
267{
268    uint32_t bitStart = 0;
269    bool loopFlag = true;
270    while (loopFlag) {
271        uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart);
272        if (zeroStart >= maxSlots) {
273            return maxSlots;
274        }
275
276        uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart);
277        if (zeroEnd - zeroStart >= slots) {
278            return zeroStart;
279        }
280
281        bitStart = zeroEnd + 1;
282        if (zeroEnd + 1 >= maxSlots) {
283            return maxSlots;
284        }
285    }
286    return 0;
287}
288
289static bool UpdateDentry(HmdfsDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos)
290{
291    HmdfsDentry *de;
292    const std::string name = base.name;
293    uint32_t slots = GetDentrySlots(name.length());
294
295    de = &d.nsl[bitPos];
296    de->hash = nameHash;
297    de->namelen = name.length();
298    auto ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length());
299    if (ret != 0) {
300        LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length());
301        return false;
302    }
303    de->atime = base.atime;
304    de->mtime = base.mtime;
305    de->size = base.size;
306    de->mode = base.mode;
307    MetaHelper::SetPosition(de, base.position);
308    MetaHelper::SetFileType(de, base.fileType);
309    MetaHelper::SetNoUpload(de, base.noUpload);
310    (void) memset_s(de->recordId, CLOUD_RECORD_ID_LEN, 0, CLOUD_RECORD_ID_LEN);
311    ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length());
312    if (ret != 0) {
313        LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
314        return false;
315    }
316
317    for (uint32_t i = 0; i < slots; i++) {
318        BitOps::SetBit(bitPos + i, d.bitmap);
319        if (i) {
320            (de + i)->namelen = 0;
321        }
322    }
323    return true;
324}
325
326int32_t CloudDiskMetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t &level)
327{
328    struct stat fileStat;
329    int err = fstat(fd_, &fileStat);
330    if (err < 0) {
331        return EINVAL;
332    }
333    if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) &&
334        ftruncate(fd_, GetDcacheFileSize(level))) {
335        return ENOENT;
336    }
337    return E_OK;
338}
339
340static unsigned long GetBidxFromLevel(uint32_t level, uint32_t namehash)
341{
342    uint32_t bucket = GetBucketByLevel(level);
343    if (bucket == 0) {
344        return 0;
345    }
346    return BUCKET_BLOCKS * GetBucketaddr(level, namehash % bucket);
347}
348
349int32_t CloudDiskMetaFile::DoCreate(const MetaBase &base)
350{
351    if (fd_ < 0) {
352        LOGE("bad metafile fd");
353        return EINVAL;
354    }
355    off_t pos = 0;
356    uint32_t level = 0;
357    uint32_t bitPos = 0;
358    unsigned long bidx = 0;
359    HmdfsDentryGroup dentryBlk = {0};
360    std::unique_lock<std::mutex> lock(mtx_);
361    FileRangeLock fileLock(fd_, 0, 0);
362    uint32_t namehash = CloudDisk::CloudFileUtils::DentryHash(base.name);
363    bool found = false;
364    while (!found) {
365        if (level == MAX_BUCKET_LEVEL) {
366            return ENOSPC;
367        }
368        bidx = GetBidxFromLevel(level, namehash);
369        unsigned long endBlock = bidx + BUCKET_BLOCKS;
370        int32_t ret = HandleFileByFd(endBlock, level);
371        if (ret != E_OK) {
372            return ret;
373        }
374        for (; bidx < endBlock; bidx++) {
375            pos = GetDentryGroupPos(bidx);
376            if (FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk) != DENTRYGROUP_SIZE) {
377                return ENOENT;
378            }
379            bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP);
380            if (bitPos < DENTRY_PER_GROUP) {
381                found = true;
382                break;
383            }
384        }
385        ++level;
386    }
387    pos = GetDentryGroupPos(bidx);
388    if (!UpdateDentry(dentryBlk, base, namehash, bitPos)) {
389        LOGI("UpdateDentry fail, stop write.");
390        return EINVAL;
391    }
392    int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE);
393    if (size != DENTRYGROUP_SIZE) {
394        LOGD("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE);
395        return EINVAL;
396    }
397    return E_OK;
398}
399
400struct DcacheLookupCtx {
401    int fd{-1};
402    std::string name{};
403    uint32_t hash{0};
404    uint32_t bidx{0};
405    std::unique_ptr<HmdfsDentryGroup> page{nullptr};
406};
407
408static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd)
409{
410    ctx->fd = fd;
411    ctx->name = base.name;
412    ctx->bidx = 0;
413    ctx->page = nullptr;
414    ctx->hash = CloudDisk::CloudFileUtils::DentryHash(ctx->name);
415}
416
417static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx)
418{
419    auto dentryBlk = std::make_unique<HmdfsDentryGroup>();
420
421    off_t pos = GetDentryGroupPos(index);
422    ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get());
423    if (size != DENTRYGROUP_SIZE) {
424        return nullptr;
425    }
426    return dentryBlk;
427}
428
429static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name)
430{
431    int maxLen = 0;
432    uint32_t bitPos = 0;
433    HmdfsDentry *de = nullptr;
434
435    while (bitPos < DENTRY_PER_GROUP) {
436        if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) {
437            bitPos++;
438            maxLen++;
439            continue;
440        }
441        de = &dentryBlk.nsl[bitPos];
442        if (!de->namelen) {
443            bitPos++;
444            continue;
445        }
446
447        if (de->hash == namehash && de->namelen == name.length() &&
448            !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) {
449            return de;
450        }
451        maxLen = 0;
452        bitPos += GetDentrySlots(de->namelen);
453    }
454
455    return nullptr;
456}
457
458static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx)
459    __attribute__((no_sanitize("unsigned-integer-overflow")))
460{
461    HmdfsDentry *de = nullptr;
462
463    uint32_t nbucket = GetBucketByLevel(level);
464    if (nbucket == 0) {
465        return de;
466    }
467
468    uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
469    uint32_t endBlock = bidx + BUCKET_BLOCKS;
470
471    for (; bidx < endBlock; bidx++) {
472        auto dentryBlk = FindDentryPage(bidx, ctx);
473        if (dentryBlk == nullptr) {
474            break;
475        }
476
477        de = FindInBlock(*dentryBlk, ctx->hash, ctx->name);
478        if (de != nullptr) {
479            ctx->page = std::move(dentryBlk);
480            break;
481        }
482    }
483    ctx->bidx = bidx;
484    return de;
485}
486
487static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx)
488{
489    for (uint32_t level = 0; level < MAX_BUCKET_LEVEL; level++) {
490        HmdfsDentry *de = InLevel(level, ctx);
491        if (de != nullptr) {
492            return de;
493        }
494    }
495    return nullptr;
496}
497
498int32_t CloudDiskMetaFile::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 CloudDiskMetaFile::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.atime = de->atime;
550    base.mtime = de->mtime;
551    base.mode = de->mode;
552    base.position = MetaHelper::GetPosition(de);
553    base.fileType = MetaHelper::GetFileType(de);
554    base.noUpload = MetaHelper::GetNoUpload(de);
555    base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN);
556    return E_OK;
557}
558
559int32_t CloudDiskMetaFile::DoUpdate(const MetaBase &base)
560{
561    if (fd_ < 0) {
562        LOGE("bad metafile fd");
563        return EINVAL;
564    }
565
566    std::unique_lock<std::mutex> lock(mtx_);
567    FileRangeLock fileLock(fd_, 0, 0);
568    struct DcacheLookupCtx ctx;
569    InitDcacheLookupCtx(&ctx, base, fd_);
570    struct HmdfsDentry *de = FindDentry(&ctx);
571    if (de == nullptr) {
572        LOGD("find dentry failed");
573        return ENOENT;
574    }
575
576    de->atime = base.atime;
577    de->mtime = base.mtime;
578    de->size = base.size;
579    de->mode = base.mode;
580    MetaHelper::SetPosition(de, base.position);
581    MetaHelper::SetFileType(de, base.fileType);
582    MetaHelper::SetNoUpload(de, base.noUpload);
583    auto ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length());
584    if (ret != 0) {
585        LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
586    }
587
588    off_t ipos = GetDentryGroupPos(ctx.bidx);
589    ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct HmdfsDentryGroup));
590    if (size != sizeof(struct HmdfsDentryGroup)) {
591        LOGE("write failed, ret = %{public}zd", size);
592        return EIO;
593    }
594    return E_OK;
595}
596
597int32_t CloudDiskMetaFile::DoRename(MetaBase &metaBase, const std::string &newName,
598    std::shared_ptr<CloudDiskMetaFile> newMetaFile)
599{
600    std::string oldName = metaBase.name;
601    metaBase.name = newName;
602    int32_t ret = newMetaFile->DoCreate(metaBase);
603    if (ret != E_OK) {
604        LOGE("create dentry failed, ret = %{public}d", ret);
605        return ret;
606    }
607    metaBase.name = oldName;
608    ret = DoRemove(metaBase);
609    if (ret != E_OK) {
610        LOGE("remove dentry failed, ret = %{public}d", ret);
611        metaBase.name = newName;
612        (void)newMetaFile->DoRemove(metaBase);
613        return ret;
614    }
615    return E_OK;
616}
617
618static int32_t DecodeDentrys(const HmdfsDentryGroup &dentryGroup, std::vector<MetaBase> &bases)
619{
620    for (uint32_t i = 0; i < DENTRY_PER_GROUP; i++) {
621        int len = dentryGroup.nsl[i].namelen;
622        if (!BitOps::TestBit(i, dentryGroup.bitmap) || len == 0 || len >= PATH_MAX) {
623            continue;
624        }
625
626        std::string name(reinterpret_cast<const char *>(dentryGroup.fileName[i]), len);
627
628        MetaBase base(name);
629        base.mode = dentryGroup.nsl[i].mode;
630        base.mtime = dentryGroup.nsl[i].mtime;
631        base.size = dentryGroup.nsl[i].size;
632        base.position = MetaHelper::GetPosition(&dentryGroup.nsl[i]);
633        base.fileType = MetaHelper::GetFileType(&dentryGroup.nsl[i]);
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 CloudDiskMetaFile::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
667void MetaFileMgr::Clear(uint32_t userId, const std::string &bundleName,
668    const std::string &cloudId)
669{
670    std::lock_guard<std::mutex> lock(cloudDiskMutex_);
671    MetaFileKey key(userId, cloudId + bundleName);
672    cloudDiskMetaFile_.erase(key);
673    cloudDiskMetaFileList_.remove_if([key](CloudDiskMetaFileListEle &item) { return item.first == key; });
674}
675
676void MetaFileMgr::CloudDiskClearAll()
677{
678    std::lock_guard<std::mutex> lock(cloudDiskMutex_);
679    cloudDiskMetaFile_.clear();
680    cloudDiskMetaFileList_.clear();
681}
682
683std::shared_ptr<CloudDiskMetaFile> MetaFileMgr::GetCloudDiskMetaFile(uint32_t userId, const std::string &bundleName,
684    const std::string &cloudId)
685{
686    std::shared_ptr<CloudDiskMetaFile> mFile = nullptr;
687    std::lock_guard<std::mutex> lock(cloudDiskMutex_);
688    MetaFileKey key(userId, cloudId + bundleName);
689    auto it = cloudDiskMetaFile_.find(key);
690    if (it != cloudDiskMetaFile_.end()) {
691        cloudDiskMetaFileList_.splice(cloudDiskMetaFileList_.begin(), cloudDiskMetaFileList_, it->second);
692        mFile = it->second->second;
693    } else {
694        if (cloudDiskMetaFile_.size() == MAX_CLOUDDISK_META_FILE_NUM) {
695            auto deleteKey = cloudDiskMetaFileList_.back().first;
696            cloudDiskMetaFile_.erase(deleteKey);
697            cloudDiskMetaFileList_.pop_back();
698        }
699        mFile = std::make_shared<CloudDiskMetaFile>(userId, bundleName, cloudId);
700        cloudDiskMetaFileList_.emplace_front(key, mFile);
701        cloudDiskMetaFile_[key] = cloudDiskMetaFileList_.begin();
702    }
703    return mFile;
704}
705
706int32_t MetaFileMgr::CreateRecycleDentry(uint32_t userId, const std::string &bundleName)
707{
708    MetaBase metaBase(RECYCLE_NAME);
709    auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, ROOT_CLOUD_ID);
710    int32_t ret = metaFile->DoLookup(metaBase);
711    if (ret != 0) {
712        metaBase.cloudId = RECYCLE_CLOUD_ID;
713        metaBase.mode = S_IFDIR | STAT_MODE_DIR;
714        metaBase.position = static_cast<uint8_t>(LOCAL);
715        ret = metaFile->DoCreate(metaBase);
716        if (ret != 0) {
717            return ret;
718        }
719    }
720    return 0;
721}
722
723int32_t MetaFileMgr::MoveIntoRecycleDentryfile(uint32_t userId, const std::string &bundleName, const std::string &name,
724    const std::string &parentCloudId, int64_t rowId)
725{
726    MetaBase metaBase(name);
727    auto srcMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, parentCloudId);
728    auto dstMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, RECYCLE_CLOUD_ID);
729    std::string uniqueName = name + "_" + std::to_string(rowId);
730    int32_t ret = srcMetaFile->DoLookup(metaBase);
731    if (ret != E_OK) {
732        LOGE("lookup src metafile failed, ret = %{public}d", ret);
733        return ret;
734    }
735    metaBase.name = uniqueName;
736    ret = dstMetaFile->DoCreate(metaBase);
737    if (ret != E_OK) {
738        LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
739        return ret;
740    }
741    metaBase.name = name;
742    ret = srcMetaFile->DoLookupAndRemove(metaBase);
743    if (ret != E_OK) {
744        LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
745        metaBase.name = uniqueName;
746        (void)dstMetaFile->DoLookupAndRemove(metaBase);
747        return ret;
748    }
749    return E_OK;
750}
751
752int32_t MetaFileMgr::RemoveFromRecycleDentryfile(uint32_t userId, const std::string &bundleName,
753    const std::string &name, const std::string &parentCloudId, int64_t rowId)
754{
755    auto srcMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, RECYCLE_CLOUD_ID);
756    auto dstMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, parentCloudId);
757    std::string uniqueName = name + "_" + std::to_string(rowId);
758    MetaBase metaBase(uniqueName);
759    int32_t ret = srcMetaFile->DoLookup(metaBase);
760    if (ret != E_OK) {
761        LOGE("lookup and update dentry failed, ret = %{public}d", ret);
762        return ret;
763    }
764    metaBase.name = name;
765    ret = dstMetaFile->DoCreate(metaBase);
766    if (ret != E_OK) {
767        LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
768        return ret;
769    }
770    metaBase.name = uniqueName;
771    ret = srcMetaFile->DoLookupAndRemove(metaBase);
772    if (ret != E_OK) {
773        LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
774        metaBase.name = name;
775        (void)dstMetaFile->DoLookupAndRemove(metaBase);
776        return ret;
777    }
778    return E_OK;
779}
780} // namespace FileManagement
781} // namespace OHOS
782