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 <fcntl.h>
19 #include <iomanip>
20 #include <sstream>
21 #include <sys/stat.h>
22
23 #include "dfs_error.h"
24 #include "directory_ex.h"
25 #include "file_utils.h"
26 #include "securec.h"
27 #include "string_ex.h"
28 #include "sys/xattr.h"
29 #include "utils_log.h"
30
31 namespace OHOS {
32 namespace FileManagement {
33 constexpr uint32_t DENTRYGROUP_SIZE = 4096;
34 constexpr uint32_t DENTRY_NAME_LEN = 8;
35 constexpr uint32_t DENTRY_RESERVED_LENGTH = 3;
36 constexpr uint32_t DENTRY_PER_GROUP = 60;
37 constexpr uint32_t DENTRY_BITMAP_LENGTH = 8;
38 constexpr uint32_t DENTRY_GROUP_RESERVED = 7;
39 constexpr uint32_t CLOUD_RECORD_ID_LEN = 33;
40 constexpr uint32_t DENTRYGROUP_HEADER = 4096;
41 constexpr uint32_t MAX_BUCKET_LEVEL = 63;
42 constexpr uint32_t BUCKET_BLOCKS = 2;
43 constexpr uint32_t BITS_PER_BYTE = 8;
44 constexpr uint32_t HMDFS_SLOT_LEN_BITS = 3;
45 constexpr uint64_t DELTA = 0x9E3779B9; /* Hashing code copied from f2fs */
46 constexpr uint64_t HMDFS_HASH_COL_BIT = (0x1ULL) << 63;
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 ForceCreateDirectory(cacheDir);
120 std::string dentryFileName = GetDentryfileName(path, caseSense);
121
122 return cacheDir + dentryFileName;
123 }
124
GetParentDir(const std::string & path)125 std::string MetaFile::GetParentDir(const std::string &path)
126 {
127 if ((path == "/") || (path == "")) {
128 return "";
129 }
130
131 auto pos = path.find_last_of('/');
132 if ((pos == std::string::npos) || (pos == 0)) {
133 return "/";
134 }
135
136 return path.substr(0, pos);
137 }
138
GetFileName(const std::string & path)139 std::string MetaFile::GetFileName(const std::string &path)
140 {
141 if ((path == "/") || (path == "")) {
142 return "";
143 }
144
145 auto pos = path.find_last_of('/');
146 if (pos == std::string::npos) {
147 return "";
148 }
149
150 return path.substr(pos + 1);
151 }
152
GetParentMetaFile(uint32_t userId,const std::string & path)153 static std::shared_ptr<MetaFile> GetParentMetaFile(uint32_t userId, const std::string &path)
154 {
155 std::string parentPath = MetaFile::GetParentDir(path);
156 if (parentPath == "") {
157 return nullptr;
158 }
159
160 return MetaFileMgr::GetInstance().GetMetaFile(userId, parentPath);
161 }
162
MetaFile(uint32_t userId,const std::string & path)163 MetaFile::MetaFile(uint32_t userId, const std::string &path)
164 {
165 path_ = path;
166 cacheFile_ = GetDentryfileByPath(userId, path);
167 fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)};
168 LOGD("fd=%{public}d, errno :%{public}d", fd_.Get(), errno);
169
170 int ret = fsetxattr(fd_, "user.hmdfs_cache", path.c_str(), path.size(), 0);
171 if (ret != 0) {
172 LOGE("setxattr failed, errno %{public}d, cacheFile_ %s", errno, cacheFile_.c_str());
173 }
174
175 HmdfsDcacheHeader header{};
176 (void)FileUtils::ReadFile(fd_, 0, sizeof(header), &header);
177 dentryCount_ = header.dentryCount;
178
179 /* lookup and create in parent */
180 parentMetaFile_ = GetParentMetaFile(userId, path);
181 std::string dirName = GetFileName(path);
182 if ((parentMetaFile_ == nullptr) || (dirName == "")) {
183 return;
184 }
185 MetaBase m(dirName, std::to_string(PathHash(path, false)));
186 ret = parentMetaFile_->DoLookup(m);
187 if (ret != E_OK) {
188 m.mode = S_IFDIR;
189 m.size = DIR_SIZE;
190 ret = parentMetaFile_->DoCreate(m);
191 if (ret != E_OK) {
192 LOGE("create parent failed, ret %{public}d", ret);
193 }
194 }
195 }
196
~MetaFile()197 MetaFile::~MetaFile()
198 {
199 if ((dentryCount_ == 0) && (path_ != "/")) {
200 unlink(cacheFile_.c_str());
201 if (parentMetaFile_) {
202 std::string dirName = GetFileName(path_);
203 if (dirName == "") {
204 return;
205 }
206 MetaBase m(dirName, std::to_string(PathHash(path_, false)));
207 parentMetaFile_->DoRemove(m);
208 }
209 return;
210 }
211
212 HmdfsDcacheHeader header{.dentryCount = dentryCount_};
213 (void)FileUtils::WriteFile(fd_, &header, 0, sizeof(header));
214 }
215
IsDotDotdot(const std::string & name)216 static bool IsDotDotdot(const std::string &name)
217 {
218 return name == "." || name == "..";
219 }
220
Str2HashBuf(const char * msg,size_t len,uint32_t * buf,int num)221 static void Str2HashBuf(const char *msg, size_t len, uint32_t *buf, int num)
222 {
223 uint32_t pad = static_cast<uint32_t>(len) | (static_cast<uint32_t>(len) << 8);
224 pad |= pad << 16; /* hash pad length 16 */
225
226 uint32_t val = pad;
227 len = std::min(len, static_cast<size_t>(num * sizeof(int)));
228 for (uint32_t i = 0; i < len; i++) {
229 if ((i % sizeof(int)) == 0) {
230 val = pad;
231 }
232 uint8_t c = static_cast<uint8_t>(tolower(msg[i]));
233 val = c + (val << 8); /* hash shift size 8 */
234 if ((i % 4) == 3) { /* msg size 4, shift when 3 */
235 *buf++ = val;
236 val = pad;
237 num--;
238 }
239 }
240 if (--num >= 0) {
241 *buf++ = val;
242 }
243 while (--num >= 0) {
244 *buf++ = pad;
245 }
246 }
247
TeaTransform(uint32_t buf[4],uint32_t const in[])248 static void TeaTransform(uint32_t buf[4], uint32_t const in[])
249 {
250 int n = 16; /* transform total rounds 16 */
251 uint32_t a = in[0]; /* transform input pos 0 */
252 uint32_t b = in[1]; /* transform input pos 1 */
253 uint32_t c = in[2]; /* transform input pos 2 */
254 uint32_t d = in[3]; /* transform input pos 3 */
255 uint32_t b0 = buf[0]; /* buf pos 0 */
256 uint32_t b1 = buf[1]; /* buf pos 1 */
257 uint32_t sum = 0;
258
259 do {
260 sum += DELTA;
261 b0 += ((b1 << 4) + a) ^ (b1 + sum) ^ ((b1 >> 5) + b); /* tea transform width 4 and 5 */
262 b1 += ((b0 << 4) + c) ^ (b0 + sum) ^ ((b0 >> 5) + d); /* tea transform width 4 and 5 */
263 } while (--n);
264
265 buf[0] += b0;
266 buf[1] += b1;
267 }
268
DentryHash(const std::string & name)269 static uint32_t DentryHash(const std::string &name)
270 {
271 if (IsDotDotdot(name)) {
272 return 0;
273 }
274
275 constexpr int inLen = 8; /* hash input buf size 8 */
276 constexpr int bufLen = 4; /* hash output buf size 4 */
277 uint32_t in[inLen];
278 uint32_t buf[bufLen];
279 auto len = name.length();
280 constexpr decltype(len) hashWidth = 16; /* hash operation width 4 */
281 const char *p = name.c_str();
282
283 buf[0] = 0x67452301; /* hash magic 1 */
284 buf[1] = 0xefcdab89; /* hash magic 2 */
285 buf[2] = 0x98badcfe; /* hash magic 3 */
286 buf[3] = 0x10325476; /* hash magic 4 */
287
288 while (true) {
289 Str2HashBuf(p, len, in, bufLen);
290 TeaTransform(buf, in);
291
292 if (len <= hashWidth) {
293 break;
294 }
295
296 p += hashWidth;
297 len -= hashWidth;
298 };
299 uint32_t hash = buf[0];
300 uint32_t hmdfsHash = hash & ~HMDFS_HASH_COL_BIT;
301
302 return hmdfsHash;
303 }
304
GetDentrySlots(size_t nameLen)305 static inline uint32_t GetDentrySlots(size_t nameLen)
306 {
307 return static_cast<uint32_t>((nameLen + BITS_PER_BYTE - 1) >> HMDFS_SLOT_LEN_BITS);
308 }
309
GetDentryGroupPos(size_t bidx)310 static inline off_t GetDentryGroupPos(size_t bidx)
311 {
312 return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER;
313 }
314
GetDentryGroupCnt(uint64_t size)315 static inline uint64_t GetDentryGroupCnt(uint64_t size)
316 {
317 return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0;
318 }
319
GetOverallBucket(uint32_t level)320 static uint32_t GetOverallBucket(uint32_t level)
321 {
322 if (level >= MAX_BUCKET_LEVEL) {
323 LOGI("level = %{public}d overflow", level);
324 return 0;
325 }
326 uint64_t buckets = (1ULL << (level + 1)) - 1;
327 return static_cast<uint32_t>(buckets);
328 }
329
GetDcacheFileSize(uint32_t level)330 static size_t GetDcacheFileSize(uint32_t level)
331 {
332 size_t buckets = GetOverallBucket(level);
333 return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER;
334 }
335
GetBucketaddr(uint32_t level,uint32_t buckoffset)336 static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset)
337 {
338 if (level >= MAX_BUCKET_LEVEL) {
339 return 0;
340 }
341
342 uint64_t curLevelMaxBucks = (1ULL << level);
343 if (buckoffset >= curLevelMaxBucks) {
344 return 0;
345 }
346
347 return static_cast<uint32_t>(curLevelMaxBucks) + buckoffset - 1;
348 }
349
GetBucketByLevel(uint32_t level)350 static uint32_t GetBucketByLevel(uint32_t level)
351 {
352 if (level >= MAX_BUCKET_LEVEL) {
353 LOGI("level = %{public}d overflow", level);
354 return 0;
355 }
356
357 uint64_t buckets = (1ULL << level);
358 return static_cast<uint32_t>(buckets);
359 }
360
RoomForFilename(const uint8_t bitmap[],size_t slots,uint32_t maxSlots)361 static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots)
362 {
363 uint32_t bitStart = 0;
364
365 while (1) {
366 uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart);
367 if (zeroStart >= maxSlots) {
368 return maxSlots;
369 }
370
371 uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart);
372 if (zeroEnd - zeroStart >= slots) {
373 return zeroStart;
374 }
375
376 bitStart = zeroEnd + 1;
377 if (zeroEnd + 1 >= maxSlots) {
378 return maxSlots;
379 }
380 }
381 return 0;
382 }
383
UpdateDentry(HmdfsDentryGroup & d,const MetaBase & base,uint32_t nameHash,uint32_t bitPos)384 static void UpdateDentry(HmdfsDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos)
385 {
386 HmdfsDentry *de;
387 const std::string name = base.name;
388 uint32_t slots = GetDentrySlots(name.length());
389
390 de = &d.nsl[bitPos];
391 de->hash = nameHash;
392 de->namelen = name.length();
393 errno_t ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length());
394 if (ret != EOK) {
395 return;
396 } else {
397 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length());
398 }
399 de->mtime = base.mtime;
400 de->fileType = base.fileType;
401 de->size = base.size;
402 de->mode = base.mode;
403 if (memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length())) {
404 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
405 }
406
407 for (uint32_t i = 0; i < slots; i++) {
408 BitOps::SetBit(bitPos + i, d.bitmap);
409 if (i) {
410 (de + i)->namelen = 0;
411 }
412 }
413 }
414
DoCreate(const MetaBase & base)415 int32_t MetaFile::DoCreate(const MetaBase &base)
416 {
417 if (fd_ < 0) {
418 LOGE("bad metafile fd");
419 return EINVAL;
420 }
421
422 off_t pos = 0;
423 uint32_t level = 0;
424 uint32_t bitPos, namehash;
425 unsigned long bidx;
426 HmdfsDentryGroup dentryBlk = {0};
427
428 std::unique_lock<std::mutex> lock(mtx_);
429 namehash = DentryHash(base.name);
430
431 bool found = false;
432 while (!found) {
433 if (level == MAX_BUCKET_LEVEL) {
434 return ENOSPC;
435 }
436 bidx = BUCKET_BLOCKS * GetBucketaddr(level, namehash % GetBucketByLevel(level));
437 unsigned long endBlock = bidx + BUCKET_BLOCKS;
438
439 struct stat fileStat;
440 int err = fstat(fd_, &fileStat);
441 if (err < 0) {
442 return EINVAL;
443 }
444 if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) &&
445 ftruncate(fd_, GetDcacheFileSize(level))) {
446 return ENOENT;
447 }
448
449 for (; bidx < endBlock; bidx++) {
450 pos = GetDentryGroupPos(bidx);
451 int size = FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk);
452 if (size != DENTRYGROUP_SIZE) {
453 return ENOENT;
454 }
455 bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP);
456 if (bitPos < DENTRY_PER_GROUP) {
457 found = true;
458 break;
459 }
460 }
461 ++level;
462 }
463
464 pos = GetDentryGroupPos(bidx);
465 UpdateDentry(dentryBlk, base, namehash, bitPos);
466 int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE);
467 if (size != DENTRYGROUP_SIZE) {
468 LOGI("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE);
469 return EINVAL;
470 }
471
472 ++dentryCount_;
473 return E_OK;
474 }
475
476 struct DcacheLookupCtx {
477 int fd{-1};
478 std::string name{};
479 uint32_t hash{0};
480 uint32_t bidx{0};
481 std::unique_ptr<HmdfsDentryGroup> page{nullptr};
482 };
483
InitDcacheLookupCtx(DcacheLookupCtx * ctx,const MetaBase & base,int fd)484 static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd)
485 {
486 ctx->fd = fd;
487 ctx->name = base.name;
488 ctx->bidx = 0;
489 ctx->page = nullptr;
490 ctx->hash = DentryHash(ctx->name);
491 }
492
FindDentryPage(uint64_t index,DcacheLookupCtx * ctx)493 static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx)
494 {
495 auto dentryBlk = std::make_unique<HmdfsDentryGroup>();
496
497 off_t pos = GetDentryGroupPos(index);
498 ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get());
499 if (size != DENTRYGROUP_SIZE) {
500 return nullptr;
501 }
502 return dentryBlk;
503 }
504
FindInBlock(HmdfsDentryGroup & dentryBlk,uint32_t namehash,const std::string & name)505 static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name)
506 {
507 int maxLen = 0;
508 uint32_t bitPos = 0;
509 HmdfsDentry *de = nullptr;
510
511 while (bitPos < DENTRY_PER_GROUP) {
512 if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) {
513 bitPos++;
514 maxLen++;
515 continue;
516 }
517 de = &dentryBlk.nsl[bitPos];
518 if (!de->namelen) {
519 bitPos++;
520 continue;
521 }
522
523 if (de->hash == namehash && de->namelen == name.length() &&
524 !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) {
525 return de;
526 }
527 maxLen = 0;
528 bitPos += GetDentrySlots(de->namelen);
529 }
530
531 return nullptr;
532 }
533
InLevel(uint32_t level,DcacheLookupCtx * ctx)534 static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx)
535 {
536 HmdfsDentry *de = nullptr;
537
538 uint32_t nbucket = GetBucketByLevel(level);
539 if (!nbucket) {
540 return de;
541 }
542
543 uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
544 uint32_t endBlock = bidx + BUCKET_BLOCKS;
545
546 for (; bidx < endBlock; bidx++) {
547 auto dentryBlk = FindDentryPage(bidx, ctx);
548 if (dentryBlk == nullptr) {
549 break;
550 }
551
552 de = FindInBlock(*dentryBlk, ctx->hash, ctx->name);
553 if (de != nullptr) {
554 ctx->page = std::move(dentryBlk);
555 break;
556 }
557 }
558 ctx->bidx = bidx;
559 return de;
560 }
561
FindDentry(DcacheLookupCtx * ctx)562 static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx)
563 {
564 for (uint32_t level = 0; level < MAX_BUCKET_LEVEL; level++) {
565 HmdfsDentry *de = InLevel(level, ctx);
566 if (de != nullptr) {
567 return de;
568 }
569 }
570 return nullptr;
571 }
572
DoRemove(const MetaBase & base)573 int32_t MetaFile::DoRemove(const MetaBase &base)
574 {
575 if (fd_ < 0) {
576 LOGE("bad metafile fd");
577 return EINVAL;
578 }
579
580 std::unique_lock<std::mutex> lock(mtx_);
581 DcacheLookupCtx ctx;
582 InitDcacheLookupCtx(&ctx, base, fd_);
583 HmdfsDentry *de = FindDentry(&ctx);
584 if (de == nullptr) {
585 LOGE("find dentry failed");
586 return ENOENT;
587 }
588
589 uint32_t bitPos = (de - ctx.page->nsl);
590 uint32_t slots = GetDentrySlots(de->namelen);
591 for (uint32_t i = 0; i < slots; i++) {
592 BitOps::ClearBit(bitPos + i, ctx.page->bitmap);
593 }
594
595 off_t ipos = GetDentryGroupPos(ctx.bidx);
596 ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(HmdfsDentryGroup));
597 if (size != sizeof(HmdfsDentryGroup)) {
598 LOGE("WriteFile failed!, ret = %{public}zd", size);
599 return EIO;
600 }
601
602 --dentryCount_;
603 return E_OK;
604 }
605
DoLookup(MetaBase & base)606 int32_t MetaFile::DoLookup(MetaBase &base)
607 {
608 if (fd_ < 0) {
609 LOGE("bad metafile fd");
610 return EINVAL;
611 }
612
613 std::unique_lock<std::mutex> lock(mtx_);
614 struct DcacheLookupCtx ctx;
615 InitDcacheLookupCtx(&ctx, base, fd_);
616 struct HmdfsDentry *de = FindDentry(&ctx);
617 if (de == nullptr) {
618 LOGI("find dentry failed");
619 return ENOENT;
620 }
621
622 base.size = de->size;
623 base.mtime = de->mtime;
624 base.mode = de->mode;
625 base.fileType = de->fileType;
626 base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN - 1);
627
628 return E_OK;
629 }
630
DoUpdate(const MetaBase & base)631 int32_t MetaFile::DoUpdate(const MetaBase &base)
632 {
633 if (fd_ < 0) {
634 LOGE("bad metafile fd");
635 return EINVAL;
636 }
637
638 std::unique_lock<std::mutex> lock(mtx_);
639 struct DcacheLookupCtx ctx;
640 InitDcacheLookupCtx(&ctx, base, fd_);
641 struct HmdfsDentry *de = FindDentry(&ctx);
642 if (de == nullptr) {
643 LOGI("find dentry failed");
644 return ENOENT;
645 }
646
647 de->mtime = base.mtime;
648 de->size = base.size;
649 de->mode = base.mode;
650 de->fileType = base.fileType;
651 if (memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length())) {
652 LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
653 }
654
655 off_t ipos = GetDentryGroupPos(ctx.bidx);
656 ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), sizeof(struct HmdfsDentryGroup), ipos);
657 if (size != sizeof(struct HmdfsDentryGroup)) {
658 LOGI("write failed, ret = %zd", size);
659 return EIO;
660 }
661 return E_OK;
662 }
663
DoRename(const MetaBase & oldBase,const std::string & newName)664 int32_t MetaFile::DoRename(const MetaBase &oldBase, const std::string &newName)
665 {
666 MetaBase base{oldBase.name};
667 int32_t ret = DoLookup(base);
668 if (ret) {
669 LOGE("ret = %{public}d, lookup %s failed", ret, base.name.c_str());
670 return ret;
671 }
672
673 base.name = newName;
674 ret = DoCreate(base);
675 if (ret) {
676 LOGE("ret = %{public}d, create %s failed", ret, base.name.c_str());
677 return ret;
678 }
679
680 base.name = oldBase.name;
681 ret = DoRemove(oldBase);
682 if (ret) {
683 LOGE("ret = %{public}d, remove %s failed", ret, oldBase.name.c_str());
684 base.name = newName;
685 (void)DoRemove(base);
686 return ret;
687 }
688
689 return E_OK;
690 }
691
DecodeDentrys(const HmdfsDentryGroup & dentryGroup,std::vector<MetaBase> & bases)692 static int32_t DecodeDentrys(const HmdfsDentryGroup &dentryGroup, std::vector<MetaBase> &bases)
693 {
694 for (uint32_t i = 0; i < DENTRY_PER_GROUP; i++) {
695 int len = dentryGroup.nsl[i].namelen;
696 if (!BitOps::TestBit(i, dentryGroup.bitmap) || len == 0 || len >= PATH_MAX) {
697 continue;
698 }
699
700 std::string name(reinterpret_cast<const char *>(dentryGroup.fileName[i]), len);
701
702 MetaBase base(name);
703 base.mode = dentryGroup.nsl[i].mode;
704 base.mtime = dentryGroup.nsl[i].mtime;
705 base.size = dentryGroup.nsl[i].size;
706 base.fileType = dentryGroup.nsl[i].fileType;
707 base.cloudId = std::string(reinterpret_cast<const char *>(dentryGroup.nsl[i].recordId), CLOUD_RECORD_ID_LEN);
708 bases.emplace_back(base);
709 }
710 return 0;
711 }
712
LoadChildren(std::vector<MetaBase> & bases)713 int32_t MetaFile::LoadChildren(std::vector<MetaBase> &bases)
714 {
715 if (fd_ < 0) {
716 LOGE("bad metafile fd");
717 return EINVAL;
718 }
719
720 std::lock_guard<std::mutex> lock(mtx_);
721 struct stat fileStat;
722 int ret = fstat(fd_, &fileStat);
723 if (ret != E_OK) {
724 return EINVAL;
725 }
726
727 uint64_t fileSize = static_cast<uint64_t>(fileStat.st_size);
728 uint64_t groupCnt = GetDentryGroupCnt(fileSize);
729 HmdfsDentryGroup dentryGroup;
730
731 for (uint64_t i = 1; i < groupCnt + 1; i++) {
732 uint64_t off = i * sizeof(HmdfsDentryGroup);
733 FileUtils::ReadFile(fd_, off, sizeof(HmdfsDentryGroup), &dentryGroup);
734 DecodeDentrys(dentryGroup, bases);
735 }
736 return E_OK;
737 }
738
GetInstance()739 MetaFileMgr& MetaFileMgr::GetInstance()
740 {
741 static MetaFileMgr instance_;
742 return instance_;
743 }
744
GetMetaFile(uint32_t userId,const std::string & path)745 std::shared_ptr<MetaFile> MetaFileMgr::GetMetaFile(uint32_t userId, const std::string &path)
746 {
747 std::shared_ptr<MetaFile> mFile = nullptr;
748 std::lock_guard<std::recursive_mutex> lock(mtx_);
749
750 if (metaFiles_.find({userId, path}) != metaFiles_.end()) {
751 mFile = metaFiles_[{userId, path}];
752 } else {
753 mFile = std::make_shared<MetaFile>(userId, path);
754 metaFiles_[{userId, path}] = mFile;
755 }
756 return mFile;
757 }
758
ClearAll()759 void MetaFileMgr::ClearAll()
760 {
761 std::lock_guard<std::recursive_mutex> lock(mtx_);
762 metaFiles_.clear();
763 }
764
RecordIdToCloudId(const std::string hexStr)765 std::string MetaFileMgr::RecordIdToCloudId(const std::string hexStr)
766 {
767 std::string result;
768 constexpr std::size_t offset = 2;
769 constexpr int changeBase = 16;
770 for (std::size_t i = 0; i < hexStr.length(); i += offset) {
771 std::string hexByte = hexStr.substr(i, offset);
772 char *endPtr;
773 unsigned long hexValue = std::strtoul(hexByte.c_str(), &endPtr, changeBase);
774
775 if (endPtr != hexByte.c_str() + hexByte.length()) {
776 LOGE("Invalid hexadecimal string: %{public}s", hexStr.c_str());
777 return "";
778 }
779 result += static_cast<char>(hexValue);
780 }
781 if (result.size() > CLOUD_RECORD_ID_LEN) {
782 LOGE("Invalid result length %{public}zu", result.size());
783 return "";
784 }
785
786 return result;
787 }
788
CloudIdToRecordId(const std::string cloudId)789 std::string MetaFileMgr::CloudIdToRecordId(const std::string cloudId)
790 {
791 std::stringstream result;
792 constexpr int width = 2;
793 for (std::size_t i = 0; i < cloudId.length(); i++) {
794 uint8_t u8Byte = cloudId[i];
795 result << std::setw(width) << std::setfill('0') << std::hex << static_cast<int>(u8Byte);
796 }
797 return result.str();
798 }
799
800 } // namespace FileManagement
801 } // namespace OHOS
802