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