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