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