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 
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 = 3;
38 constexpr uint32_t DENTRY_PER_GROUP = 60;
39 constexpr uint32_t DENTRY_BITMAP_LENGTH = 8;
40 constexpr uint32_t DENTRY_GROUP_RESERVED = 7;
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 DIR_SIZE = 4096;
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     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 
66 struct 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 };
73 static_assert(sizeof(HmdfsDentryGroup) == DENTRYGROUP_SIZE);
74 
75 struct 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 
PathHash(const std::string & path,bool caseSense)86 static 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 
GetDentryfileName(const std::string & path,bool caseSense)102 static 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 
GetDentryfileByPath(uint32_t userId,const std::string & path,bool caseSense=false)115 static 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 
GetParentDir(const std::string & path)124 std::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 
GetFileName(const std::string & path)138 std::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 
GetParentMetaFile(uint32_t userId,const std::string & path)152 static 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 
MetaFile(uint32_t userId,const std::string & path)162 MetaFile::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 
~MetaFile()195 MetaFile::~MetaFile()
196 {
197 }
198 
GetDentrySlots(size_t nameLen)199 static 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 
GetDentryGroupPos(size_t bidx)204 static inline off_t GetDentryGroupPos(size_t bidx)
205 {
206     return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER;
207 }
208 
GetDentryGroupCnt(uint64_t size)209 static inline uint64_t GetDentryGroupCnt(uint64_t size)
210 {
211     return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0;
212 }
213 
GetOverallBucket(uint32_t level)214 static 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 
GetDcacheFileSize(uint32_t level)224 static size_t GetDcacheFileSize(uint32_t level)
225 {
226     size_t buckets = GetOverallBucket(level);
227     return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER;
228 }
229 
GetBucketaddr(uint32_t level,uint32_t buckoffset)230 static 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 
GetBucketByLevel(uint32_t level)244 static 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 
RoomForFilename(const uint8_t bitmap[],size_t slots,uint32_t maxSlots)255 static 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 
UpdateDentry(HmdfsDentryGroup & d,const MetaBase & base,uint32_t nameHash,uint32_t bitPos)278 static void 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     if (memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length())) {
288         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length());
289     }
290     de->mtime = base.mtime;
291     de->fileType = base.fileType;
292     de->size = base.size;
293     de->mode = base.mode;
294     if (memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length())) {
295         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
296     }
297 
298     for (uint32_t i = 0; i < slots; i++) {
299         BitOps::SetBit(bitPos + i, d.bitmap);
300         if (i) {
301             (de + i)->namelen = 0;
302         }
303     }
304 }
305 
HandleFileByFd(unsigned long & endBlock,uint32_t & level)306 int32_t MetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t &level)
307 {
308     struct stat fileStat;
309     int err = fstat(fd_, &fileStat);
310     if (err < 0) {
311         return EINVAL;
312     }
313     if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) &&
314         ftruncate(fd_, GetDcacheFileSize(level))) {
315         return ENOENT;
316     }
317     return E_OK;
318 }
319 
DoCreate(const MetaBase & base)320 int32_t MetaFile::DoCreate(const MetaBase &base)
321 {
322     if (fd_ < 0) {
323         LOGE("bad metafile fd");
324         return EINVAL;
325     }
326 
327     off_t pos = 0;
328     uint32_t level = 0;
329     uint32_t bitPos = 0;
330     uint32_t namehash = 0;
331     unsigned long bidx;
332     HmdfsDentryGroup dentryBlk = {0};
333 
334     std::unique_lock<std::mutex> lock(mtx_);
335     namehash = CloudDisk::CloudFileUtils::DentryHash(base.name);
336 
337     bool found = false;
338     while (!found) {
339         if (level == MAX_BUCKET_LEVEL) {
340             return ENOSPC;
341         }
342         bidx = BUCKET_BLOCKS * GetBucketaddr(level, namehash % GetBucketByLevel(level));
343         unsigned long endBlock = bidx + BUCKET_BLOCKS;
344 
345         int32_t ret = MetaFile::HandleFileByFd(endBlock, level);
346         if (ret != E_OK) {
347             return ret;
348         }
349 
350         for (; bidx < endBlock; bidx++) {
351             pos = GetDentryGroupPos(bidx);
352             int size = FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk);
353             if (size != DENTRYGROUP_SIZE) {
354                 return ENOENT;
355             }
356             bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP);
357             if (bitPos < DENTRY_PER_GROUP) {
358                 found = true;
359                 break;
360             }
361         }
362         ++level;
363     }
364 
365     pos = GetDentryGroupPos(bidx);
366     UpdateDentry(dentryBlk, base, namehash, bitPos);
367     int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE);
368     if (size != DENTRYGROUP_SIZE) {
369         LOGI("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE);
370         return EINVAL;
371     }
372 
373     return E_OK;
374 }
375 
376 struct DcacheLookupCtx {
377     int fd{-1};
378     std::string name{};
379     uint32_t hash{0};
380     uint32_t bidx{0};
381     std::unique_ptr<HmdfsDentryGroup> page{nullptr};
382 };
383 
InitDcacheLookupCtx(DcacheLookupCtx * ctx,const MetaBase & base,int fd)384 static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd)
385 {
386     ctx->fd = fd;
387     ctx->name = base.name;
388     ctx->bidx = 0;
389     ctx->page = nullptr;
390     ctx->hash = CloudDisk::CloudFileUtils::DentryHash(ctx->name);
391 }
392 
FindDentryPage(uint64_t index,DcacheLookupCtx * ctx)393 static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx)
394 {
395     auto dentryBlk = std::make_unique<HmdfsDentryGroup>();
396 
397     off_t pos = GetDentryGroupPos(index);
398     ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get());
399     if (size != DENTRYGROUP_SIZE) {
400         return nullptr;
401     }
402     return dentryBlk;
403 }
404 
FindInBlock(HmdfsDentryGroup & dentryBlk,uint32_t namehash,const std::string & name)405 static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name)
406 {
407     int maxLen = 0;
408     uint32_t bitPos = 0;
409     HmdfsDentry *de = nullptr;
410 
411     while (bitPos < DENTRY_PER_GROUP) {
412         if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) {
413             bitPos++;
414             maxLen++;
415             continue;
416         }
417         de = &dentryBlk.nsl[bitPos];
418         if (!de->namelen) {
419             bitPos++;
420             continue;
421         }
422 
423         if (de->hash == namehash && de->namelen == name.length() &&
424             !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) {
425             return de;
426         }
427         maxLen = 0;
428         bitPos += GetDentrySlots(de->namelen);
429     }
430 
431     return nullptr;
432 }
433 
InLevel(uint32_t level,DcacheLookupCtx * ctx)434 static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx)
435                             __attribute__((no_sanitize("unsigned-integer-overflow")))
436 {
437     HmdfsDentry *de = nullptr;
438 
439     uint32_t nbucket = GetBucketByLevel(level);
440     if (!nbucket) {
441         return de;
442     }
443 
444     uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
445     uint32_t endBlock = bidx + BUCKET_BLOCKS;
446 
447     for (; bidx < endBlock; bidx++) {
448         auto dentryBlk = FindDentryPage(bidx, ctx);
449         if (dentryBlk == nullptr) {
450             break;
451         }
452 
453         de = FindInBlock(*dentryBlk, ctx->hash, ctx->name);
454         if (de != nullptr) {
455             ctx->page = std::move(dentryBlk);
456             break;
457         }
458     }
459     ctx->bidx = bidx;
460     return de;
461 }
462 
GetMaxLevel(int32_t fd)463 static uint32_t GetMaxLevel(int32_t fd)
464 {
465     struct stat st;
466     if (fstat(fd, &st) == -1) {
467         return MAX_BUCKET_LEVEL;
468     }
469     uint32_t blkNum = static_cast<uint32_t>(st.st_size) / DENTRYGROUP_SIZE + 1;
470     uint32_t maxLevel = 0;
471     blkNum >>= 1;
472     while (blkNum > 1) {
473         blkNum >>= 1;
474         maxLevel++;
475     }
476     return maxLevel;
477 }
478 
FindDentry(DcacheLookupCtx * ctx)479 static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx)
480 {
481     uint32_t maxLevel = GetMaxLevel(ctx->fd);
482     for (uint32_t level = 0; level < maxLevel; level++) {
483         HmdfsDentry *de = InLevel(level, ctx);
484         if (de != nullptr) {
485             return de;
486         }
487     }
488     return nullptr;
489 }
490 
DoRemove(const MetaBase & base)491 int32_t MetaFile::DoRemove(const MetaBase &base)
492 {
493     if (fd_ < 0) {
494         LOGE("bad metafile fd");
495         return EINVAL;
496     }
497 
498     std::unique_lock<std::mutex> lock(mtx_);
499     DcacheLookupCtx ctx;
500     InitDcacheLookupCtx(&ctx, base, fd_);
501     HmdfsDentry *de = FindDentry(&ctx);
502     if (de == nullptr) {
503         LOGE("find dentry failed");
504         return ENOENT;
505     }
506 
507     uint32_t bitPos = (de - ctx.page->nsl);
508     uint32_t slots = GetDentrySlots(de->namelen);
509     for (uint32_t i = 0; i < slots; i++) {
510         BitOps::ClearBit(bitPos + i, ctx.page->bitmap);
511     }
512 
513     off_t ipos = GetDentryGroupPos(ctx.bidx);
514     ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(HmdfsDentryGroup));
515     if (size != sizeof(HmdfsDentryGroup)) {
516         LOGE("WriteFile failed!, ret = %{public}zd", size);
517         return EIO;
518     }
519 
520     return E_OK;
521 }
522 
DoLookup(MetaBase & base)523 int32_t MetaFile::DoLookup(MetaBase &base)
524 {
525     if (fd_ < 0) {
526         LOGE("bad metafile fd");
527         return EINVAL;
528     }
529 
530     std::unique_lock<std::mutex> lock(mtx_);
531     struct DcacheLookupCtx ctx;
532     InitDcacheLookupCtx(&ctx, base, fd_);
533     struct HmdfsDentry *de = FindDentry(&ctx);
534     if (de == nullptr) {
535         LOGD("find dentry failed");
536         return ENOENT;
537     }
538 
539     base.size = de->size;
540     base.mtime = de->mtime;
541     base.mode = de->mode;
542     base.fileType = de->fileType;
543     base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN - 1);
544 
545     return E_OK;
546 }
547 
DoUpdate(const MetaBase & base)548 int32_t MetaFile::DoUpdate(const MetaBase &base)
549 {
550     if (fd_ < 0) {
551         LOGE("bad metafile fd");
552         return EINVAL;
553     }
554 
555     std::unique_lock<std::mutex> lock(mtx_);
556     struct DcacheLookupCtx ctx;
557     InitDcacheLookupCtx(&ctx, base, fd_);
558     struct HmdfsDentry *de = FindDentry(&ctx);
559     if (de == nullptr) {
560         LOGI("find dentry failed");
561         return ENOENT;
562     }
563 
564     de->mtime = base.mtime;
565     de->size = base.size;
566     de->mode = base.mode;
567     de->fileType = base.fileType;
568     if (memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length())) {
569         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
570     }
571 
572     off_t ipos = GetDentryGroupPos(ctx.bidx);
573     ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct HmdfsDentryGroup));
574     if (size != sizeof(struct HmdfsDentryGroup)) {
575         LOGI("write failed, ret = %{public}zd", size);
576         return EIO;
577     }
578     return E_OK;
579 }
580 
DoRename(const MetaBase & oldBase,const std::string & newName)581 int32_t MetaFile::DoRename(const MetaBase &oldBase, const std::string &newName)
582 {
583     MetaBase base{oldBase.name};
584     int32_t ret = DoLookup(base);
585     if (ret) {
586         LOGE("ret = %{public}d, lookup %s failed", ret, base.name.c_str());
587         return ret;
588     }
589 
590     base.name = newName;
591     ret = DoCreate(base);
592     if (ret) {
593         LOGE("ret = %{public}d, create %s failed", ret, base.name.c_str());
594         return ret;
595     }
596 
597     base.name = oldBase.name;
598     ret = DoRemove(oldBase);
599     if (ret) {
600         LOGE("ret = %{public}d, remove %s failed", ret, oldBase.name.c_str());
601         base.name = newName;
602         (void)DoRemove(base);
603         return ret;
604     }
605 
606     return E_OK;
607 }
608 
DecodeDentrys(const HmdfsDentryGroup & dentryGroup,std::vector<MetaBase> & bases)609 static int32_t DecodeDentrys(const HmdfsDentryGroup &dentryGroup, std::vector<MetaBase> &bases)
610 {
611     for (uint32_t i = 0; i < DENTRY_PER_GROUP; i++) {
612         int len = dentryGroup.nsl[i].namelen;
613         if (!BitOps::TestBit(i, dentryGroup.bitmap) || len == 0 || len >= PATH_MAX) {
614             continue;
615         }
616 
617         std::string name(reinterpret_cast<const char *>(dentryGroup.fileName[i]), len);
618 
619         MetaBase base(name);
620         base.mode = dentryGroup.nsl[i].mode;
621         base.mtime = dentryGroup.nsl[i].mtime;
622         base.size = dentryGroup.nsl[i].size;
623         base.fileType = dentryGroup.nsl[i].fileType;
624         base.cloudId = std::string(reinterpret_cast<const char *>(dentryGroup.nsl[i].recordId), CLOUD_RECORD_ID_LEN);
625         bases.emplace_back(base);
626     }
627     return 0;
628 }
629 
LoadChildren(std::vector<MetaBase> & bases)630 int32_t MetaFile::LoadChildren(std::vector<MetaBase> &bases)
631 {
632     if (fd_ < 0) {
633         LOGE("bad metafile fd");
634         return EINVAL;
635     }
636 
637     std::lock_guard<std::mutex> lock(mtx_);
638     struct stat fileStat;
639     int ret = fstat(fd_, &fileStat);
640     if (ret != E_OK) {
641         return EINVAL;
642     }
643 
644     uint64_t fileSize = static_cast<uint64_t>(fileStat.st_size);
645     uint64_t groupCnt = GetDentryGroupCnt(fileSize);
646     HmdfsDentryGroup dentryGroup;
647 
648     for (uint64_t i = 1; i < groupCnt + 1; i++) {
649         uint64_t off = i * sizeof(HmdfsDentryGroup);
650         FileUtils::ReadFile(fd_, off, sizeof(HmdfsDentryGroup), &dentryGroup);
651         DecodeDentrys(dentryGroup, bases);
652     }
653     return E_OK;
654 }
655 
GetInstance()656 MetaFileMgr& MetaFileMgr::GetInstance()
657 {
658     static MetaFileMgr instance_;
659     return instance_;
660 }
661 
GetMetaFile(uint32_t userId,const std::string & path)662 std::shared_ptr<MetaFile> MetaFileMgr::GetMetaFile(uint32_t userId, const std::string &path)
663 {
664     std::shared_ptr<MetaFile> mFile = nullptr;
665     std::lock_guard<std::recursive_mutex> lock(mtx_);
666 
667     MetaFileKey key(userId, path);
668     auto it = metaFiles_.find(key);
669     if (it != metaFiles_.end()) {
670         metaFileList_.splice(metaFileList_.begin(), metaFileList_, it->second);
671         mFile = it->second->second;
672     } else {
673         if (metaFiles_.size() == MAX_META_FILE_NUM) {
674             auto deleteKey = metaFileList_.back().first;
675             metaFiles_.erase(deleteKey);
676             metaFileList_.pop_back();
677         }
678         mFile = std::make_shared<MetaFile>(userId, path);
679         metaFileList_.emplace_front(key, mFile);
680         metaFiles_[key] = metaFileList_.begin();
681     }
682     return mFile;
683 }
684 
ClearAll()685 void MetaFileMgr::ClearAll()
686 {
687     std::lock_guard<std::recursive_mutex> lock(mtx_);
688     metaFiles_.clear();
689     metaFileList_.clear();
690 }
691 
RecordIdToCloudId(const std::string hexStr)692 std::string MetaFileMgr::RecordIdToCloudId(const std::string hexStr)
693 {
694     std::string result;
695     constexpr std::size_t offset = 2;
696     constexpr int changeBase = 16;
697     for (std::size_t i = 0; i < hexStr.length(); i += offset) {
698         std::string hexByte = hexStr.substr(i, offset);
699         char *endPtr;
700         unsigned long hexValue = std::strtoul(hexByte.c_str(), &endPtr, changeBase);
701 
702         if (endPtr != hexByte.c_str() + hexByte.length()) {
703             LOGE("Invalid hexadecimal string: %{public}s", hexStr.c_str());
704             return "";
705         }
706         result += static_cast<char>(hexValue);
707     }
708     if (result.size() > CLOUD_RECORD_ID_LEN) {
709         LOGE("Invalid result length %{public}zu", result.size());
710         return "";
711     }
712 
713     return result;
714 }
715 
CloudIdToRecordId(const std::string cloudId)716 std::string MetaFileMgr::CloudIdToRecordId(const std::string cloudId)
717 {
718     std::stringstream result;
719     constexpr int width = 2;
720     for (std::size_t i = 0; i < cloudId.length(); i++) {
721         uint8_t u8Byte = cloudId[i];
722         result << std::setw(width) << std::setfill('0') << std::hex << static_cast<int>(u8Byte);
723     }
724     return result.str();
725 }
726 
727 } // namespace FileManagement
728 } // namespace OHOS
729