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