• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 constexpr uint32_t EVEN_NUM_FLAG = 2;
49 const char HDC_ID_START = '0';
50 const std::string HDC_ID_END = "ff";
51 
52 #pragma pack(push, 1)
53 struct HmdfsDentry {
54     uint32_t hash{0};
55     uint16_t mode{0};
56     uint16_t namelen{0};
57     uint64_t size{0};
58     uint64_t mtime{0};
59     uint8_t recordId[CLOUD_RECORD_ID_LEN]{0};
60     /* reserved bytes for long term extend, total 60 bytes */
61     union {
62         struct {
63             uint8_t fileType : 2;
64         };
65         uint8_t reserved[DENTRY_RESERVED_LENGTH];
66     };
67 };
68 
69 struct HmdfsDentryGroup {
70     uint8_t dentryVersion;
71     uint8_t bitmap[DENTRY_BITMAP_LENGTH];
72     HmdfsDentry nsl[DENTRY_PER_GROUP];
73     uint8_t fileName[DENTRY_PER_GROUP][DENTRY_NAME_LEN];
74     uint8_t reserved[DENTRY_GROUP_RESERVED];
75 };
76 static_assert(sizeof(HmdfsDentryGroup) == DENTRYGROUP_SIZE);
77 
78 struct HmdfsDcacheHeader {
79     uint64_t dcacheCrtime{0};
80     uint64_t dcacheCrtimeNsec{0};
81 
82     uint64_t dentryCtime{0};
83     uint64_t dentryCtimeNsec{0};
84 
85     uint64_t dentryCount{0};
86 };
87 #pragma pack(pop)
88 
PathHash(const std::string & path,bool caseSense)89 static uint64_t PathHash(const std::string &path, bool caseSense)
90 {
91     uint64_t res = 0;
92     const char *kp = path.c_str();
93 
94     while (*kp) {
95         char c = *kp;
96         if (!caseSense) {
97             c = tolower(c);
98         }
99         res = (res << 5) - res + static_cast<uint64_t>(c); /* hash shift width 5 */
100         kp++;
101     }
102     return res;
103 }
104 
GetDentryfileName(const std::string & path,bool caseSense)105 static std::string GetDentryfileName(const std::string &path, bool caseSense)
106 {
107     // path should be like "/", "/dir/", "/dir/dir/" ...
108     constexpr uint32_t fileNameLen = 32;
109     char buf[fileNameLen + 1] = {0};
110     uint64_t fileHash = PathHash(path, caseSense);
111     int ret = snprintf_s(buf, fileNameLen + 1, fileNameLen, "cloud_%016llx", fileHash);
112     if (ret < 0) {
113         LOGE("filename failer fileHash ret :%{public}d", ret);
114     }
115     return buf;
116 }
117 
GetDentryfileByPath(uint32_t userId,const std::string & path,bool caseSense=false)118 static std::string GetDentryfileByPath(uint32_t userId, const std::string &path, bool caseSense = false)
119 {
120     std::string cacheDir =
121         "/data/service/el2/" + std::to_string(userId) + "/hmdfs/cache/account_cache/dentry_cache/cloud/";
122     std::string dentryFileName = GetDentryfileName(path, caseSense);
123 
124     return cacheDir + dentryFileName;
125 }
126 
GetParentDir(const std::string & path)127 std::string MetaFile::GetParentDir(const std::string &path)
128 {
129     if ((path == "/") || (path == "")) {
130         return "";
131     }
132 
133     auto pos = path.find_last_of('/');
134     if ((pos == std::string::npos) || (pos == 0)) {
135         return "/";
136     }
137 
138     return path.substr(0, pos);
139 }
140 
GetFileName(const std::string & path)141 std::string MetaFile::GetFileName(const std::string &path)
142 {
143     if ((path == "/") || (path == "")) {
144         return "";
145     }
146 
147     auto pos = path.find_last_of('/');
148     if (pos == std::string::npos) {
149         return "";
150     }
151 
152     return path.substr(pos + 1);
153 }
154 
GetParentMetaFile(uint32_t userId,const std::string & path)155 static std::shared_ptr<MetaFile> GetParentMetaFile(uint32_t userId, const std::string &path)
156 {
157     std::string parentPath = MetaFile::GetParentDir(path);
158     if (parentPath == "") {
159         return nullptr;
160     }
161 
162     return MetaFileMgr::GetInstance().GetMetaFile(userId, parentPath);
163 }
164 
MetaFile(uint32_t userId,const std::string & path)165 MetaFile::MetaFile(uint32_t userId, const std::string &path)
166 {
167     path_ = path;
168     cacheFile_ = GetDentryfileByPath(userId, path);
169     fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)};
170     if (fd_ < 0) {
171         LOGE("fd=%{public}d, errno :%{public}d", fd_.Get(), errno);
172         return;
173     }
174 
175     int ret = fsetxattr(fd_, "user.hmdfs_cache", path.c_str(), path.size(), 0);
176     if (ret != 0) {
177         LOGE("setxattr failed, errno %{public}d, cacheFile_ %s", errno, GetAnonyString(cacheFile_).c_str());
178     }
179 
180     /* lookup and create in parent */
181     parentMetaFile_ = GetParentMetaFile(userId, path);
182     std::string dirName = GetFileName(path);
183     if ((parentMetaFile_ == nullptr) || (dirName == "")) {
184         return;
185     }
186     MetaBase m(dirName, std::to_string(PathHash(path, false)) + std::to_string(std::time(nullptr)));
187     ret = parentMetaFile_->DoLookup(m);
188     if (ret != E_OK) {
189         m.mode = S_IFDIR;
190         m.size = DIR_SIZE;
191         ret = parentMetaFile_->DoCreate(m);
192         if (ret != E_OK) {
193             LOGE("create parent failed, ret %{public}d", ret);
194         }
195     }
196 }
197 
~MetaFile()198 MetaFile::~MetaFile()
199 {
200 }
201 
GetDentrySlots(size_t nameLen)202 static inline uint32_t GetDentrySlots(size_t nameLen)
203 {
204     return static_cast<uint32_t>((nameLen + BITS_PER_BYTE - 1) >> HMDFS_SLOT_LEN_BITS);
205 }
206 
GetDentryGroupPos(size_t bidx)207 static inline off_t GetDentryGroupPos(size_t bidx)
208 {
209     return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER;
210 }
211 
GetDentryGroupCnt(uint64_t size)212 static inline uint64_t GetDentryGroupCnt(uint64_t size)
213 {
214     return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0;
215 }
216 
GetOverallBucket(uint32_t level)217 static uint32_t GetOverallBucket(uint32_t level)
218 {
219     if (level >= MAX_BUCKET_LEVEL) {
220         LOGI("level = %{public}d overflow", level);
221         return 0;
222     }
223     uint64_t buckets = (1ULL << (level + 1)) - 1;
224     return static_cast<uint32_t>(buckets);
225 }
226 
GetDcacheFileSize(uint32_t level)227 static size_t GetDcacheFileSize(uint32_t level)
228 {
229     size_t buckets = GetOverallBucket(level);
230     return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER;
231 }
232 
GetBucketaddr(uint32_t level,uint32_t buckoffset)233 static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset)
234 {
235     if (level >= MAX_BUCKET_LEVEL) {
236         return 0;
237     }
238 
239     uint64_t curLevelMaxBucks = (1ULL << level);
240     if (buckoffset >= curLevelMaxBucks) {
241         return 0;
242     }
243 
244     return static_cast<uint32_t>(curLevelMaxBucks) + buckoffset - 1;
245 }
246 
GetBucketByLevel(uint32_t level)247 static uint32_t GetBucketByLevel(uint32_t level)
248 {
249     if (level >= MAX_BUCKET_LEVEL) {
250         LOGI("level = %{public}d overflow", level);
251         return 0;
252     }
253 
254     uint64_t buckets = (1ULL << level);
255     return static_cast<uint32_t>(buckets);
256 }
257 
RoomForFilename(const uint8_t bitmap[],size_t slots,uint32_t maxSlots)258 static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots)
259 {
260     uint32_t bitStart = 0;
261 
262     while (1) {
263         uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart);
264         if (zeroStart >= maxSlots) {
265             return maxSlots;
266         }
267 
268         uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart);
269         if (zeroEnd - zeroStart >= slots) {
270             return zeroStart;
271         }
272 
273         bitStart = zeroEnd + 1;
274         if (zeroEnd + 1 >= maxSlots) {
275             return maxSlots;
276         }
277     }
278     return 0;
279 }
280 
UpdateDentry(HmdfsDentryGroup & d,const MetaBase & base,uint32_t nameHash,uint32_t bitPos)281 static bool UpdateDentry(HmdfsDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos)
282 {
283     HmdfsDentry *de;
284     const std::string name = base.name;
285     uint32_t slots = GetDentrySlots(name.length());
286 
287     de = &d.nsl[bitPos];
288     de->hash = nameHash;
289     de->namelen = name.length();
290     errno_t ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length());
291     if (ret != EOK) {
292         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length());
293         return false;
294     }
295     de->mtime = base.mtime;
296     de->fileType = base.fileType;
297     de->size = base.size;
298     de->mode = base.mode;
299     (void)memset_s(de->recordId, CLOUD_RECORD_ID_LEN, 0, CLOUD_RECORD_ID_LEN);
300     ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length());
301     if (ret != EOK) {
302         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
303         return false;
304     }
305 
306     for (uint32_t i = 0; i < slots; i++) {
307         BitOps::SetBit(bitPos + i, d.bitmap);
308         if (i) {
309             (de + i)->namelen = 0;
310         }
311     }
312     return true;
313 }
314 
HandleFileByFd(unsigned long & endBlock,uint32_t & level)315 int32_t MetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t &level)
316 {
317     struct stat fileStat;
318     int err = fstat(fd_, &fileStat);
319     if (err < 0) {
320         return EINVAL;
321     }
322     if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) &&
323         ftruncate(fd_, GetDcacheFileSize(level))) {
324         return ENOENT;
325     }
326     return E_OK;
327 }
328 
DoCreate(const MetaBase & base)329 int32_t MetaFile::DoCreate(const MetaBase &base)
330 {
331     if (fd_ < 0) {
332         LOGE("bad metafile fd");
333         return EINVAL;
334     }
335 
336     off_t pos = 0;
337     uint32_t level = 0;
338     uint32_t bitPos = 0;
339     unsigned long bidx;
340     HmdfsDentryGroup dentryBlk = {0};
341 
342     std::unique_lock<std::mutex> lock(mtx_);
343     uint32_t namehash = CloudDisk::CloudFileUtils::DentryHash(base.name);
344 
345     bool found = false;
346     while (!found) {
347         if (level == MAX_BUCKET_LEVEL) {
348             return ENOSPC;
349         }
350         bidx = BUCKET_BLOCKS * GetBucketaddr(level, namehash % GetBucketByLevel(level));
351         unsigned long endBlock = bidx + BUCKET_BLOCKS;
352 
353         int32_t ret = MetaFile::HandleFileByFd(endBlock, level);
354         if (ret != E_OK) {
355             return ret;
356         }
357 
358         for (; bidx < endBlock; bidx++) {
359             pos = GetDentryGroupPos(bidx);
360             if (FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk) != DENTRYGROUP_SIZE) {
361                 return ENOENT;
362             }
363             bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP);
364             if (bitPos < DENTRY_PER_GROUP) {
365                 found = true;
366                 break;
367             }
368         }
369         ++level;
370     }
371 
372     pos = GetDentryGroupPos(bidx);
373     if (!UpdateDentry(dentryBlk, base, namehash, bitPos)) {
374         LOGI("UpdateDentry fail, stop write.");
375         return EINVAL;
376     }
377     int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE);
378     if (size != DENTRYGROUP_SIZE) {
379         LOGI("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE);
380         return EINVAL;
381     }
382 
383     return E_OK;
384 }
385 
386 struct DcacheLookupCtx {
387     int fd{-1};
388     std::string name{};
389     uint32_t hash{0};
390     uint32_t bidx{0};
391     std::unique_ptr<HmdfsDentryGroup> page{nullptr};
392 };
393 
InitDcacheLookupCtx(DcacheLookupCtx * ctx,const MetaBase & base,int fd)394 static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd)
395 {
396     ctx->fd = fd;
397     ctx->name = base.name;
398     ctx->bidx = 0;
399     ctx->page = nullptr;
400     ctx->hash = CloudDisk::CloudFileUtils::DentryHash(ctx->name);
401 }
402 
FindDentryPage(uint64_t index,DcacheLookupCtx * ctx)403 static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx)
404 {
405     auto dentryBlk = std::make_unique<HmdfsDentryGroup>();
406 
407     off_t pos = GetDentryGroupPos(index);
408     ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get());
409     if (size != DENTRYGROUP_SIZE) {
410         return nullptr;
411     }
412     return dentryBlk;
413 }
414 
FindInBlock(HmdfsDentryGroup & dentryBlk,uint32_t namehash,const std::string & name)415 static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name)
416 {
417     int maxLen = 0;
418     uint32_t bitPos = 0;
419     HmdfsDentry *de = nullptr;
420 
421     while (bitPos < DENTRY_PER_GROUP) {
422         if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) {
423             bitPos++;
424             maxLen++;
425             continue;
426         }
427         de = &dentryBlk.nsl[bitPos];
428         if (!de->namelen) {
429             bitPos++;
430             continue;
431         }
432 
433         if (de->hash == namehash && de->namelen == name.length() &&
434             !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) {
435             return de;
436         }
437         maxLen = 0;
438         bitPos += GetDentrySlots(de->namelen);
439     }
440 
441     return nullptr;
442 }
443 
InLevel(uint32_t level,DcacheLookupCtx * ctx)444 static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx)
445                             __attribute__((no_sanitize("unsigned-integer-overflow")))
446 {
447     HmdfsDentry *de = nullptr;
448 
449     uint32_t nbucket = GetBucketByLevel(level);
450     if (!nbucket) {
451         return de;
452     }
453 
454     uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
455     uint32_t endBlock = bidx + BUCKET_BLOCKS;
456 
457     for (; bidx < endBlock; bidx++) {
458         auto dentryBlk = FindDentryPage(bidx, ctx);
459         if (dentryBlk == nullptr) {
460             break;
461         }
462 
463         de = FindInBlock(*dentryBlk, ctx->hash, ctx->name);
464         if (de != nullptr) {
465             ctx->page = std::move(dentryBlk);
466             break;
467         }
468     }
469     ctx->bidx = bidx;
470     return de;
471 }
472 
GetMaxLevel(int32_t fd)473 static uint32_t GetMaxLevel(int32_t fd)
474 {
475     struct stat st;
476     if (fstat(fd, &st) == -1) {
477         return MAX_BUCKET_LEVEL;
478     }
479     uint32_t blkNum = static_cast<uint32_t>(st.st_size) / DENTRYGROUP_SIZE + 1;
480     uint32_t maxLevel = 0;
481     blkNum >>= 1;
482     while (blkNum > 1) {
483         blkNum >>= 1;
484         maxLevel++;
485     }
486     return maxLevel;
487 }
488 
FindDentry(DcacheLookupCtx * ctx)489 static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx)
490 {
491     uint32_t maxLevel = GetMaxLevel(ctx->fd);
492     for (uint32_t level = 0; level < maxLevel; level++) {
493         HmdfsDentry *de = InLevel(level, ctx);
494         if (de != nullptr) {
495             return de;
496         }
497     }
498     return nullptr;
499 }
500 
DoRemove(const MetaBase & base)501 int32_t MetaFile::DoRemove(const MetaBase &base)
502 {
503     if (fd_ < 0) {
504         LOGE("bad metafile fd");
505         return EINVAL;
506     }
507 
508     std::unique_lock<std::mutex> lock(mtx_);
509     DcacheLookupCtx ctx;
510     InitDcacheLookupCtx(&ctx, base, fd_);
511     HmdfsDentry *de = FindDentry(&ctx);
512     if (de == nullptr) {
513         LOGE("find dentry failed");
514         return ENOENT;
515     }
516 
517     uint32_t bitPos = (de - ctx.page->nsl);
518     uint32_t slots = GetDentrySlots(de->namelen);
519     for (uint32_t i = 0; i < slots; i++) {
520         BitOps::ClearBit(bitPos + i, ctx.page->bitmap);
521     }
522 
523     off_t ipos = GetDentryGroupPos(ctx.bidx);
524     ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(HmdfsDentryGroup));
525     if (size != sizeof(HmdfsDentryGroup)) {
526         LOGE("WriteFile failed!, ret = %{public}zd", size);
527         return EIO;
528     }
529 
530     return E_OK;
531 }
532 
DoLookup(MetaBase & base)533 int32_t MetaFile::DoLookup(MetaBase &base)
534 {
535     if (fd_ < 0) {
536         LOGE("bad metafile fd");
537         return EINVAL;
538     }
539 
540     std::unique_lock<std::mutex> lock(mtx_);
541     struct DcacheLookupCtx ctx;
542     InitDcacheLookupCtx(&ctx, base, fd_);
543     struct HmdfsDentry *de = FindDentry(&ctx);
544     if (de == nullptr) {
545         LOGD("find dentry failed");
546         return ENOENT;
547     }
548 
549     base.size = de->size;
550     base.mtime = de->mtime;
551     base.mode = de->mode;
552     base.fileType = de->fileType;
553     base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN - 1);
554 
555     return E_OK;
556 }
557 
DoUpdate(const MetaBase & base)558 int32_t MetaFile::DoUpdate(const MetaBase &base)
559 {
560     if (fd_ < 0) {
561         LOGE("bad metafile fd");
562         return EINVAL;
563     }
564 
565     std::unique_lock<std::mutex> lock(mtx_);
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 
DoRename(const MetaBase & oldBase,const std::string & newName)591 int32_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, GetAnonyString(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, GetAnonyString(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, GetAnonyString(oldBase.name).c_str());
611         base.name = newName;
612         (void)DoRemove(base);
613         return ret;
614     }
615 
616     return E_OK;
617 }
618 
DecodeDentrys(const HmdfsDentryGroup & dentryGroup,std::vector<MetaBase> & bases)619 static 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 
LoadChildren(std::vector<MetaBase> & bases)640 int32_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     struct stat fileStat;
649     int ret = fstat(fd_, &fileStat);
650     if (ret != E_OK) {
651         return EINVAL;
652     }
653 
654     uint64_t fileSize = static_cast<uint64_t>(fileStat.st_size);
655     uint64_t groupCnt = GetDentryGroupCnt(fileSize);
656     HmdfsDentryGroup dentryGroup;
657 
658     for (uint64_t i = 1; i < groupCnt + 1; i++) {
659         uint64_t off = i * sizeof(HmdfsDentryGroup);
660         FileUtils::ReadFile(fd_, off, sizeof(HmdfsDentryGroup), &dentryGroup);
661         DecodeDentrys(dentryGroup, bases);
662     }
663     return E_OK;
664 }
665 
GetInstance()666 MetaFileMgr& MetaFileMgr::GetInstance()
667 {
668     static MetaFileMgr instance_;
669     return instance_;
670 }
671 
GetMetaFile(uint32_t userId,const std::string & path)672 std::shared_ptr<MetaFile> MetaFileMgr::GetMetaFile(uint32_t userId, const std::string &path)
673 {
674     std::shared_ptr<MetaFile> mFile = nullptr;
675     std::lock_guard<std::recursive_mutex> lock(mtx_);
676 
677     MetaFileKey key(userId, path);
678     auto it = metaFiles_.find(key);
679     if (it != metaFiles_.end()) {
680         metaFileList_.splice(metaFileList_.begin(), metaFileList_, it->second);
681         mFile = it->second->second;
682     } else {
683         mFile = std::make_shared<MetaFile>(userId, path);
684         while (metaFiles_.size() >= MAX_META_FILE_NUM) {
685             auto deleteKey = metaFileList_.back().first;
686             metaFiles_.erase(deleteKey);
687             metaFileList_.pop_back();
688         }
689         metaFileList_.emplace_front(key, mFile);
690         metaFiles_[key] = metaFileList_.begin();
691     }
692     return mFile;
693 }
694 
CheckMetaFileSize()695 int32_t MetaFileMgr::CheckMetaFileSize()
696 {
697     std::lock_guard<std::recursive_mutex> lock(mtx_);
698     if (metaFiles_.size() != metaFileList_.size()) {
699         return -1;
700     }
701     return metaFileList_.size();
702 }
703 
ClearAll()704 void MetaFileMgr::ClearAll()
705 {
706     std::lock_guard<std::recursive_mutex> lock(mtx_);
707     metaFiles_.clear();
708     metaFileList_.clear();
709 }
710 
RecordIdToCloudId(const std::string hexStr,bool isHdc)711 std::string MetaFileMgr::RecordIdToCloudId(const std::string hexStr, bool isHdc)
712 {
713     std::string srcStr = hexStr;
714     if (isHdc) {
715         if (srcStr.size() % EVEN_NUM_FLAG != 0) {
716             srcStr = HDC_ID_START + srcStr + HDC_ID_END;
717         } else {
718             srcStr = srcStr + HDC_ID_END;
719         }
720     }
721     std::string result;
722     constexpr std::size_t offset = 2;
723     constexpr int changeBase = 16;
724     for (std::size_t i = 0; i < srcStr.length(); i += offset) {
725         std::string hexByte = srcStr.substr(i, offset);
726         char *endPtr;
727         unsigned long hexValue = std::strtoul(hexByte.c_str(), &endPtr, changeBase);
728 
729         if (endPtr != hexByte.c_str() + hexByte.length()) {
730             LOGE("Invalid hexadecimal string: %{public}s", srcStr.c_str());
731             return "";
732         }
733         result += static_cast<char>(hexValue);
734     }
735     if (result.size() > CLOUD_RECORD_ID_LEN) {
736         LOGE("Invalid result length %{public}zu", result.size());
737         return "";
738     }
739 
740     return result;
741 }
742 
743 /**
744  * @brief convert cloudId to recordId
745  *
746  * @param cloudId
747  * @param isHdc is homedatacenter's cloudId
748  * @return string recordId
749  * @example recordId: 012345678ff -> 12345678 when isHdc=true
750  */
CloudIdToRecordId(const std::string cloudId,bool isHdc)751 std::string MetaFileMgr::CloudIdToRecordId(const std::string cloudId, bool isHdc)
752 {
753     std::stringstream result;
754     constexpr int width = 2;
755     for (std::size_t i = 0; i < cloudId.length(); i++) {
756         uint8_t u8Byte = cloudId[i];
757         result << std::setw(width) << std::setfill('0') << std::hex << static_cast<int>(u8Byte);
758     }
759     if (!isHdc) {
760         return result.str();
761     }
762     std::string recordId = result.str();
763     if (recordId.at(0) == HDC_ID_START) {
764         recordId = recordId.substr(1);
765     }
766     auto pos = recordId.find(HDC_ID_END);
767     if (pos == std::string::npos) {
768         return recordId;
769     }
770     return recordId.substr(0, pos);
771 }
772 
773 } // namespace FileManagement
774 } // namespace OHOS
775