• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 #include <unordered_set>
24 
25 #include "cloud_file_utils.h"
26 #include "dfs_error.h"
27 #include "file_utils.h"
28 #include "securec.h"
29 #include "string_ex.h"
30 #include "sys/xattr.h"
31 #include "utils_directory.h"
32 #include "utils_log.h"
33 
34 namespace OHOS {
35 namespace FileManagement {
36 constexpr uint32_t DENTRYGROUP_SIZE = 4096;
37 constexpr uint32_t DENTRY_NAME_LEN = 8;
38 constexpr uint32_t DENTRY_RESERVED_LENGTH = 4;
39 constexpr uint32_t DENTRY_PER_GROUP = 52;
40 constexpr uint32_t DENTRY_BITMAP_LENGTH = 7;
41 constexpr uint32_t DENTRY_GROUP_RESERVED = 32;
42 constexpr uint32_t CLOUD_RECORD_ID_LEN = 33;
43 constexpr uint32_t DENTRYGROUP_HEADER = 4096;
44 constexpr uint32_t MAX_BUCKET_LEVEL = 63;
45 constexpr uint32_t BUCKET_BLOCKS = 2;
46 constexpr uint32_t BITS_PER_BYTE = 8;
47 constexpr uint32_t HMDFS_SLOT_LEN_BITS = 3;
48 constexpr uint32_t FILE_TYPE_OFFSET = 2;
49 constexpr uint32_t NO_UPLOAD_OFFSET = 4;
50 
51 #pragma pack(push, 1)
52 struct HmdfsDentry {
53     uint32_t hash{0};
54     uint16_t mode{0};
55     uint16_t namelen{0};
56     uint64_t size{0};
57     uint64_t mtime{0};
58     uint64_t atime{0};
59     uint8_t recordId[CLOUD_RECORD_ID_LEN]{0};
60     uint8_t flags{0};
61     /* reserved bytes for long term extend, total 60 bytes */
62     uint8_t reserved[DENTRY_RESERVED_LENGTH];
63 };
64 
65 struct HmdfsDentryGroup {
66     uint8_t dentryVersion;
67     uint8_t bitmap[DENTRY_BITMAP_LENGTH];
68     struct HmdfsDentry nsl[DENTRY_PER_GROUP];
69     uint8_t fileName[DENTRY_PER_GROUP][DENTRY_NAME_LEN];
70     uint8_t reserved[DENTRY_GROUP_RESERVED];
71 };
72 static_assert(sizeof(HmdfsDentryGroup) == DENTRYGROUP_SIZE);
73 
74 struct HmdfsDcacheHeader {
75     uint64_t dcacheCrtime{0};
76     uint64_t dcacheCrtimeNsec{0};
77 
78     uint64_t dentryCtime{0};
79     uint64_t dentryCtimeNsec{0};
80 
81     uint64_t dentryCount{0};
82 };
83 #pragma pack(pop)
84 
SetFileType(struct HmdfsDentry * de,uint8_t fileType)85 void MetaHelper::SetFileType(struct HmdfsDentry *de, uint8_t fileType)
86 {
87     de->flags &= 0x13;
88     de->flags |= (fileType << FILE_TYPE_OFFSET);
89 }
90 
SetPosition(struct HmdfsDentry * de,uint8_t position)91 void MetaHelper::SetPosition(struct HmdfsDentry *de, uint8_t position)
92 {
93     de->flags &= 0xFC;
94     de->flags |= position;
95 }
96 
SetNoUpload(struct HmdfsDentry * de,uint8_t noUpload)97 void MetaHelper::SetNoUpload(struct HmdfsDentry *de, uint8_t noUpload)
98 {
99     de->flags &= 0xEF;
100     de->flags |= (noUpload << NO_UPLOAD_OFFSET);
101 }
102 
GetNoUpload(const struct HmdfsDentry * de)103 uint8_t MetaHelper::GetNoUpload(const struct HmdfsDentry *de)
104 {
105     return (de->flags & 0x10) >> NO_UPLOAD_OFFSET;
106 }
107 
GetFileType(const struct HmdfsDentry * de)108 uint8_t MetaHelper::GetFileType(const struct HmdfsDentry *de)
109 {
110     return (de->flags & 0xC) >> FILE_TYPE_OFFSET;
111 }
112 
GetPosition(const struct HmdfsDentry * de)113 uint8_t MetaHelper::GetPosition(const struct HmdfsDentry *de)
114 {
115     return de->flags & 0x3;
116 }
117 
GetCloudDiskDentryFileByPath(uint32_t userId,const std::string & bundleName,const std::string & cloudId)118 static std::string GetCloudDiskDentryFileByPath(uint32_t userId, const std::string &bundleName,
119     const std::string &cloudId)
120 {
121     std::string cacheDir =
122         "/data/service/el2/" + std::to_string(userId) +
123         "/hmdfs/cloud/data/" + bundleName + "/" +
124         std::to_string(CloudDisk::CloudFileUtils::GetBucketId(cloudId)) + "/";
125     std::string dentryFileName = MetaFileMgr::GetInstance().CloudIdToRecordId(cloudId);
126     Storage::DistributedFile::Utils::ForceCreateDirectory(cacheDir, STAT_MODE_DIR);
127     return cacheDir + dentryFileName;
128 }
129 
CloudDiskMetaFile(uint32_t userId,const std::string & bundleName,const std::string & cloudId)130 CloudDiskMetaFile::CloudDiskMetaFile(uint32_t userId, const std::string &bundleName, const std::string &cloudId)
131 {
132     userId_ = userId;
133     bundleName_ = bundleName;
134     cloudId_ = cloudId;
135     cacheFile_ = GetCloudDiskDentryFileByPath(userId_, bundleName_, cloudId_);
136     fd_ = UniqueFd{open(cacheFile_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)};
137     LOGD("CloudDiskMetaFile cloudId=%{public}s, path=%{public}s", cloudId_.c_str(), GetAnonyString(cacheFile_).c_str());
138     LOGD("CloudDiskMetaFile fd=%{public}d, errno :%{public}d", fd_.Get(), errno);
139 
140     HmdfsDcacheHeader header{};
141     (void)FileUtils::ReadFile(fd_, 0, sizeof(header), &header);
142 }
143 
GetDentryFilePath()144 std::string CloudDiskMetaFile::GetDentryFilePath()
145 {
146     return cacheFile_;
147 }
148 
DoLookupAndCreate(const std::string & name,CloudDiskMetaFileCallBack metaFileCallBack)149 int32_t CloudDiskMetaFile::DoLookupAndCreate(const std::string &name, CloudDiskMetaFileCallBack metaFileCallBack)
150 {
151     MetaBase m(name);
152     /* lookup and create in parent */
153     int32_t ret = DoLookup(m);
154     if (ret != E_OK) {
155         metaFileCallBack(m);
156         ret = DoCreate(m);
157         if (ret != E_OK) {
158             LOGE("create dentry file failed, ret %{public}d", ret);
159             return ret;
160         }
161         return ret;
162     } else {
163         LOGE("dentry file is not null");
164     }
165     return E_OK;
166 }
167 
DoLookupAndUpdate(const std::string & name,CloudDiskMetaFileCallBack callback)168 int32_t CloudDiskMetaFile::DoLookupAndUpdate(const std::string &name, CloudDiskMetaFileCallBack callback)
169 {
170     MetaBase m(name);
171     /* lookup and create in parent */
172     int32_t ret = DoLookup(m);
173     if (ret != E_OK) {
174         callback(m);
175         ret = DoCreate(m);
176         if (ret != E_OK) {
177             LOGE("create dentry file failed, ret %{public}d", ret);
178             return ret;
179         }
180         return ret;
181     } else {
182         callback(m);
183         ret = DoUpdate(m);
184         if (ret != E_OK) {
185             LOGE("update dentry file failed, ret %{public}d", ret);
186             return ret;
187         }
188     }
189     return E_OK;
190 }
191 
DoChildUpdate(const std::string & name,CloudDiskMetaFileCallBack callback)192 int32_t CloudDiskMetaFile::DoChildUpdate(const std::string &name, CloudDiskMetaFileCallBack callback)
193 {
194     MetaBase m(name);
195     /* lookup and update */
196     int32_t ret = DoLookup(m);
197     if (ret != E_OK) {
198         LOGE("lookup dentry file failed, ret %{public}d", ret);
199         return ret;
200     } else {
201         callback(m);
202         ret = DoUpdate(m);
203         if (ret != E_OK) {
204             LOGE("update dentry file failed, ret %{public}d", ret);
205             return ret;
206         }
207     }
208     return E_OK;
209 }
210 
DoLookupAndRemove(MetaBase & metaBase)211 int32_t CloudDiskMetaFile::DoLookupAndRemove(MetaBase &metaBase)
212 {
213     /* lookup and remove in parent */
214     int32_t ret = DoLookup(metaBase);
215     if (ret == E_OK) {
216         ret = DoRemove(metaBase);
217         if (ret != E_OK) {
218             LOGE("remove dentry file failed, ret %{public}d", ret);
219             return ret;
220         }
221         return E_OK;
222     }
223     return E_OK;
224 }
225 
~CloudDiskMetaFile()226 CloudDiskMetaFile::~CloudDiskMetaFile()
227 {
228 }
229 
GetDentrySlots(size_t nameLen)230 static inline uint32_t GetDentrySlots(size_t nameLen)
231 {
232     return static_cast<uint32_t>((nameLen + BITS_PER_BYTE - 1) >> HMDFS_SLOT_LEN_BITS);
233 }
234 
GetDentryGroupPos(size_t bidx)235 static inline off_t GetDentryGroupPos(size_t bidx)
236 {
237     return bidx * DENTRYGROUP_SIZE + DENTRYGROUP_HEADER;
238 }
239 
GetDentryGroupCnt(uint64_t size)240 static inline uint64_t GetDentryGroupCnt(uint64_t size)
241 {
242     return (size >= DENTRYGROUP_HEADER) ? ((size - DENTRYGROUP_HEADER) / DENTRYGROUP_SIZE) : 0;
243 }
244 
GetOverallBucket(uint32_t level)245 static uint32_t GetOverallBucket(uint32_t level)
246 {
247     if (level >= MAX_BUCKET_LEVEL) {
248         LOGD("level = %{public}d overflow", level);
249         return 0;
250     }
251     uint64_t buckets = (1ULL << (level + 1)) - 1;
252     return static_cast<uint32_t>(buckets);
253 }
254 
GetDcacheFileSize(uint32_t level)255 static size_t GetDcacheFileSize(uint32_t level)
256 {
257     size_t buckets = GetOverallBucket(level);
258     return buckets * DENTRYGROUP_SIZE * BUCKET_BLOCKS + DENTRYGROUP_HEADER;
259 }
260 
GetBucketaddr(uint32_t level,uint32_t buckoffset)261 static uint32_t GetBucketaddr(uint32_t level, uint32_t buckoffset)
262 {
263     if (level >= MAX_BUCKET_LEVEL) {
264         return 0;
265     }
266 
267     uint64_t curLevelMaxBucks = (1ULL << level);
268     if (buckoffset >= curLevelMaxBucks) {
269         return 0;
270     }
271 
272     return static_cast<uint32_t>(curLevelMaxBucks) + buckoffset - 1;
273 }
274 
GetBucketByLevel(uint32_t level)275 static uint32_t GetBucketByLevel(uint32_t level)
276 {
277     if (level >= MAX_BUCKET_LEVEL) {
278         LOGD("level = %{public}d overflow", level);
279         return 0;
280     }
281 
282     uint64_t buckets = (1ULL << level);
283     return static_cast<uint32_t>(buckets);
284 }
285 
RoomForFilename(const uint8_t bitmap[],size_t slots,uint32_t maxSlots)286 static uint32_t RoomForFilename(const uint8_t bitmap[], size_t slots, uint32_t maxSlots)
287 {
288     uint32_t bitStart = 0;
289     bool loopFlag = true;
290     while (loopFlag) {
291         uint32_t zeroStart = BitOps::FindNextZeroBit(bitmap, maxSlots, bitStart);
292         if (zeroStart >= maxSlots) {
293             return maxSlots;
294         }
295 
296         uint32_t zeroEnd = BitOps::FindNextBit(bitmap, maxSlots, zeroStart);
297         if (zeroEnd - zeroStart >= slots) {
298             return zeroStart;
299         }
300 
301         bitStart = zeroEnd + 1;
302         if (zeroEnd + 1 >= maxSlots) {
303             return maxSlots;
304         }
305     }
306     return 0;
307 }
308 
UpdateDentry(HmdfsDentryGroup & d,const MetaBase & base,uint32_t nameHash,uint32_t bitPos)309 static bool UpdateDentry(HmdfsDentryGroup &d, const MetaBase &base, uint32_t nameHash, uint32_t bitPos)
310 {
311     HmdfsDentry *de;
312     const std::string name = base.name;
313     uint32_t slots = GetDentrySlots(name.length());
314 
315     de = &d.nsl[bitPos];
316     de->hash = nameHash;
317     de->namelen = name.length();
318     auto ret = memcpy_s(d.fileName[bitPos], slots * DENTRY_NAME_LEN, name.c_str(), name.length());
319     if (ret != 0) {
320         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", slots * DENTRY_NAME_LEN, name.length());
321         return false;
322     }
323     de->atime = base.atime;
324     de->mtime = base.mtime;
325     de->size = base.size;
326     de->mode = base.mode;
327     MetaHelper::SetPosition(de, base.position);
328     MetaHelper::SetFileType(de, base.fileType);
329     MetaHelper::SetNoUpload(de, base.noUpload);
330     (void) memset_s(de->recordId, CLOUD_RECORD_ID_LEN, 0, CLOUD_RECORD_ID_LEN);
331     ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length());
332     if (ret != 0) {
333         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
334         return false;
335     }
336 
337     for (uint32_t i = 0; i < slots; i++) {
338         BitOps::SetBit(bitPos + i, d.bitmap);
339         if (i) {
340             (de + i)->namelen = 0;
341         }
342     }
343     return true;
344 }
345 
HandleFileByFd(unsigned long & endBlock,uint32_t & level)346 int32_t CloudDiskMetaFile::HandleFileByFd(unsigned long &endBlock, uint32_t &level)
347 {
348     struct stat fileStat;
349     int err = fstat(fd_, &fileStat);
350     if (err < 0) {
351         return EINVAL;
352     }
353     if ((endBlock > GetDentryGroupCnt(fileStat.st_size)) &&
354         ftruncate(fd_, GetDcacheFileSize(level))) {
355         return ENOENT;
356     }
357     return E_OK;
358 }
359 
GetBidxFromLevel(uint32_t level,uint32_t namehash)360 static unsigned long GetBidxFromLevel(uint32_t level, uint32_t namehash)
361 {
362     uint32_t bucket = GetBucketByLevel(level);
363     if (bucket == 0) {
364         return 0;
365     }
366     return BUCKET_BLOCKS * GetBucketaddr(level, namehash % bucket);
367 }
368 
369 struct DcacheLookupCtx {
370     int fd{-1};
371     std::string name{};
372     uint32_t hash{0};
373     uint32_t bidx{0};
374     std::unique_ptr<HmdfsDentryGroup> page{nullptr};
375 };
376 
InitDcacheLookupCtx(DcacheLookupCtx * ctx,const MetaBase & base,int fd)377 static void InitDcacheLookupCtx(DcacheLookupCtx *ctx, const MetaBase &base, int fd)
378 {
379     ctx->fd = fd;
380     ctx->name = base.name;
381     ctx->bidx = 0;
382     ctx->page = nullptr;
383     ctx->hash = CloudDisk::CloudFileUtils::DentryHash(ctx->name);
384 }
385 
FindDentryPage(uint64_t index,DcacheLookupCtx * ctx)386 static std::unique_ptr<HmdfsDentryGroup> FindDentryPage(uint64_t index, DcacheLookupCtx *ctx)
387 {
388     auto dentryBlk = std::make_unique<HmdfsDentryGroup>();
389 
390     off_t pos = GetDentryGroupPos(index);
391     ssize_t size = FileUtils::ReadFile(ctx->fd, pos, DENTRYGROUP_SIZE, dentryBlk.get());
392     if (size != DENTRYGROUP_SIZE) {
393         return nullptr;
394     }
395     return dentryBlk;
396 }
397 
FindInBlock(HmdfsDentryGroup & dentryBlk,uint32_t namehash,const std::string & name)398 static HmdfsDentry *FindInBlock(HmdfsDentryGroup &dentryBlk, uint32_t namehash, const std::string &name)
399 {
400     int maxLen = 0;
401     uint32_t bitPos = 0;
402     HmdfsDentry *de = nullptr;
403 
404     while (bitPos < DENTRY_PER_GROUP) {
405         if (!BitOps::TestBit(bitPos, dentryBlk.bitmap)) {
406             bitPos++;
407             maxLen++;
408             continue;
409         }
410         de = &dentryBlk.nsl[bitPos];
411         if (!de->namelen) {
412             bitPos++;
413             continue;
414         }
415 
416         if (de->hash == namehash && de->namelen == name.length() &&
417             !memcmp(name.c_str(), dentryBlk.fileName[bitPos], de->namelen)) {
418             return de;
419         }
420         maxLen = 0;
421         bitPos += GetDentrySlots(de->namelen);
422     }
423 
424     return nullptr;
425 }
426 
InLevel(uint32_t level,DcacheLookupCtx * ctx)427 static HmdfsDentry *InLevel(uint32_t level, DcacheLookupCtx *ctx)
428     __attribute__((no_sanitize("unsigned-integer-overflow")))
429 {
430     HmdfsDentry *de = nullptr;
431 
432     uint32_t nbucket = GetBucketByLevel(level);
433     if (nbucket == 0) {
434         return de;
435     }
436 
437     uint32_t bidx = GetBucketaddr(level, ctx->hash % nbucket) * BUCKET_BLOCKS;
438     uint32_t endBlock = bidx + BUCKET_BLOCKS;
439 
440     for (; bidx < endBlock; bidx++) {
441         auto dentryBlk = FindDentryPage(bidx, ctx);
442         if (dentryBlk == nullptr) {
443             break;
444         }
445 
446         de = FindInBlock(*dentryBlk, ctx->hash, ctx->name);
447         if (de != nullptr) {
448             ctx->page = std::move(dentryBlk);
449             break;
450         }
451     }
452     ctx->bidx = bidx;
453     return de;
454 }
455 
FindDentry(DcacheLookupCtx * ctx)456 static HmdfsDentry *FindDentry(DcacheLookupCtx *ctx)
457 {
458     for (uint32_t level = 0; level < MAX_BUCKET_LEVEL; level++) {
459         HmdfsDentry *de = InLevel(level, ctx);
460         if (de != nullptr) {
461             return de;
462         }
463     }
464     return nullptr;
465 }
466 
GetCreateInfo(const MetaBase & base,uint32_t & bitPos,uint32_t & namehash,unsigned long & bidx,struct HmdfsDentryGroup & dentryBlk)467 int32_t CloudDiskMetaFile::GetCreateInfo(const MetaBase &base, uint32_t &bitPos, uint32_t &namehash,
468     unsigned long &bidx, struct HmdfsDentryGroup &dentryBlk)
469 {
470     uint32_t level = 0;
471     namehash = CloudDisk::CloudFileUtils::DentryHash(base.name);
472     bool found = false;
473     while (!found) {
474         if (level == MAX_BUCKET_LEVEL) {
475             return ENOSPC;
476         }
477         bidx = GetBidxFromLevel(level, namehash);
478         unsigned long endBlock = bidx + BUCKET_BLOCKS;
479         int32_t ret = HandleFileByFd(endBlock, level);
480         if (ret != E_OK) {
481             return ret;
482         }
483         for (; bidx < endBlock; bidx++) {
484             off_t pos = GetDentryGroupPos(bidx);
485             if (FileUtils::ReadFile(fd_, pos, DENTRYGROUP_SIZE, &dentryBlk) != DENTRYGROUP_SIZE) {
486                 return ENOENT;
487             }
488             bitPos = RoomForFilename(dentryBlk.bitmap, GetDentrySlots(base.name.length()), DENTRY_PER_GROUP);
489             if (bitPos < DENTRY_PER_GROUP) {
490                 found = true;
491                 break;
492             }
493         }
494         ++level;
495     }
496     return E_OK;
497 }
498 
DoCreate(const MetaBase & base)499 int32_t CloudDiskMetaFile::DoCreate(const MetaBase &base)
500 {
501     if (fd_ < 0) {
502         LOGE("bad metafile fd");
503         return EINVAL;
504     }
505 
506     std::unique_lock<std::mutex> lock(mtx_);
507     DcacheLookupCtx ctx;
508     InitDcacheLookupCtx(&ctx, base, fd_);
509     HmdfsDentry *de = FindDentry(&ctx);
510     if (de != nullptr) {
511         LOGE("this name dentry is exist");
512         return EEXIST;
513     }
514     uint32_t bitPos = 0;
515     unsigned long bidx = 0;
516     HmdfsDentryGroup dentryBlk = {0};
517     uint32_t namehash = 0;
518     GetCreateInfo(base, bitPos, namehash, bidx, dentryBlk);
519     off_t pos = GetDentryGroupPos(bidx);
520     if (!UpdateDentry(dentryBlk, base, namehash, bitPos)) {
521         LOGI("UpdateDentry fail, stop write.");
522         return EINVAL;
523     }
524     int size = FileUtils::WriteFile(fd_, &dentryBlk, pos, DENTRYGROUP_SIZE);
525     if (size != DENTRYGROUP_SIZE) {
526         LOGD("WriteFile failed, size %{public}d != %{public}d", size, DENTRYGROUP_SIZE);
527         return EINVAL;
528     }
529     return E_OK;
530 }
531 
DoRemove(const MetaBase & base)532 int32_t CloudDiskMetaFile::DoRemove(const MetaBase &base)
533 {
534     if (fd_ < 0) {
535         LOGE("bad metafile fd");
536         return EINVAL;
537     }
538 
539     std::unique_lock<std::mutex> lock(mtx_);
540     DcacheLookupCtx ctx;
541     InitDcacheLookupCtx(&ctx, base, fd_);
542     HmdfsDentry *de = FindDentry(&ctx);
543     if (de == nullptr) {
544         LOGE("find dentry failed");
545         return ENOENT;
546     }
547 
548     uint32_t bitPos = (de - ctx.page->nsl);
549     uint32_t slots = GetDentrySlots(de->namelen);
550     for (uint32_t i = 0; i < slots; i++) {
551         BitOps::ClearBit(bitPos + i, ctx.page->bitmap);
552     }
553 
554     off_t ipos = GetDentryGroupPos(ctx.bidx);
555     ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(HmdfsDentryGroup));
556     if (size != sizeof(HmdfsDentryGroup)) {
557         LOGE("WriteFile failed!, ret = %{public}zd", size);
558         return EIO;
559     }
560 
561     return E_OK;
562 }
563 
DoLookup(MetaBase & base)564 int32_t CloudDiskMetaFile::DoLookup(MetaBase &base)
565 {
566     if (fd_ < 0) {
567         LOGE("bad metafile fd");
568         return EINVAL;
569     }
570 
571     std::unique_lock<std::mutex> lock(mtx_);
572     struct DcacheLookupCtx ctx;
573     InitDcacheLookupCtx(&ctx, base, fd_);
574     struct HmdfsDentry *de = FindDentry(&ctx);
575     if (de == nullptr) {
576         LOGD("find dentry failed");
577         return ENOENT;
578     }
579 
580     base.size = de->size;
581     base.atime = de->atime;
582     base.mtime = de->mtime;
583     base.mode = de->mode;
584     base.position = MetaHelper::GetPosition(de);
585     base.fileType = MetaHelper::GetFileType(de);
586     base.noUpload = MetaHelper::GetNoUpload(de);
587     base.cloudId = std::string(reinterpret_cast<const char *>(de->recordId), CLOUD_RECORD_ID_LEN);
588     return E_OK;
589 }
590 
DoUpdate(const MetaBase & base)591 int32_t CloudDiskMetaFile::DoUpdate(const MetaBase &base)
592 {
593     if (fd_ < 0) {
594         LOGE("bad metafile fd");
595         return EINVAL;
596     }
597 
598     std::unique_lock<std::mutex> lock(mtx_);
599     struct DcacheLookupCtx ctx;
600     InitDcacheLookupCtx(&ctx, base, fd_);
601     struct HmdfsDentry *de = FindDentry(&ctx);
602     if (de == nullptr) {
603         LOGD("find dentry failed");
604         return ENOENT;
605     }
606 
607     de->atime = base.atime;
608     de->mtime = base.mtime;
609     de->size = base.size;
610     de->mode = base.mode;
611     MetaHelper::SetPosition(de, base.position);
612     MetaHelper::SetFileType(de, base.fileType);
613     MetaHelper::SetNoUpload(de, base.noUpload);
614     auto ret = memcpy_s(de->recordId, CLOUD_RECORD_ID_LEN, base.cloudId.c_str(), base.cloudId.length());
615     if (ret != 0) {
616         LOGE("memcpy_s failed, dstLen = %{public}d, srcLen = %{public}zu", CLOUD_RECORD_ID_LEN, base.cloudId.length());
617     }
618 
619     off_t ipos = GetDentryGroupPos(ctx.bidx);
620     ssize_t size = FileUtils::WriteFile(fd_, ctx.page.get(), ipos, sizeof(struct HmdfsDentryGroup));
621     if (size != sizeof(struct HmdfsDentryGroup)) {
622         LOGE("write failed, ret = %{public}zd", size);
623         return EIO;
624     }
625     return E_OK;
626 }
627 
DoRename(MetaBase & metaBase,const std::string & newName,std::shared_ptr<CloudDiskMetaFile> newMetaFile)628 int32_t CloudDiskMetaFile::DoRename(MetaBase &metaBase, const std::string &newName,
629     std::shared_ptr<CloudDiskMetaFile> newMetaFile)
630 {
631     std::string oldName = metaBase.name;
632     metaBase.name = newName;
633     int32_t ret = newMetaFile->DoCreate(metaBase);
634     if (ret != E_OK) {
635         LOGE("create dentry failed, ret = %{public}d", ret);
636         return ret;
637     }
638     metaBase.name = oldName;
639     ret = DoRemove(metaBase);
640     if (ret != E_OK) {
641         LOGE("remove dentry failed, ret = %{public}d", ret);
642         metaBase.name = newName;
643         (void)newMetaFile->DoRemove(metaBase);
644         return ret;
645     }
646     return E_OK;
647 }
648 
DecodeDentrys(const HmdfsDentryGroup & dentryGroup,std::vector<MetaBase> & bases)649 static int32_t DecodeDentrys(const HmdfsDentryGroup &dentryGroup, std::vector<MetaBase> &bases)
650 {
651     for (uint32_t i = 0; i < DENTRY_PER_GROUP; i++) {
652         int len = dentryGroup.nsl[i].namelen;
653         if (!BitOps::TestBit(i, dentryGroup.bitmap) || len == 0 || len >= PATH_MAX) {
654             continue;
655         }
656 
657         std::string name(reinterpret_cast<const char *>(dentryGroup.fileName[i]), len);
658 
659         MetaBase base(name);
660         base.mode = dentryGroup.nsl[i].mode;
661         base.mtime = dentryGroup.nsl[i].mtime;
662         base.size = dentryGroup.nsl[i].size;
663         base.position = MetaHelper::GetPosition(&dentryGroup.nsl[i]);
664         base.fileType = MetaHelper::GetFileType(&dentryGroup.nsl[i]);
665         base.cloudId = std::string(reinterpret_cast<const char *>(dentryGroup.nsl[i].recordId), CLOUD_RECORD_ID_LEN);
666         bases.emplace_back(base);
667     }
668     return 0;
669 }
670 
LoadChildren(std::vector<MetaBase> & bases)671 int32_t CloudDiskMetaFile::LoadChildren(std::vector<MetaBase> &bases)
672 {
673     if (fd_ < 0) {
674         LOGE("bad metafile fd");
675         return EINVAL;
676     }
677 
678     std::lock_guard<std::mutex> lock(mtx_);
679     struct stat fileStat;
680     int ret = fstat(fd_, &fileStat);
681     if (ret != E_OK) {
682         return EINVAL;
683     }
684 
685     uint64_t fileSize = static_cast<uint64_t>(fileStat.st_size);
686     uint64_t groupCnt = GetDentryGroupCnt(fileSize);
687     HmdfsDentryGroup dentryGroup;
688 
689     for (uint64_t i = 1; i < groupCnt + 1; i++) {
690         uint64_t off = i * sizeof(HmdfsDentryGroup);
691         FileUtils::ReadFile(fd_, off, sizeof(HmdfsDentryGroup), &dentryGroup);
692         DecodeDentrys(dentryGroup, bases);
693     }
694     return E_OK;
695 }
696 
Clear(uint32_t userId,const std::string & bundleName,const std::string & cloudId)697 void MetaFileMgr::Clear(uint32_t userId, const std::string &bundleName,
698     const std::string &cloudId)
699 {
700     std::lock_guard<std::mutex> lock(cloudDiskMutex_);
701     MetaFileKey key(userId, cloudId + bundleName);
702     cloudDiskMetaFile_.erase(key);
703     cloudDiskMetaFileList_.remove_if([key](CloudDiskMetaFileListEle &item) { return item.first == key; });
704 }
705 
CloudDiskClearAll()706 void MetaFileMgr::CloudDiskClearAll()
707 {
708     std::lock_guard<std::mutex> lock(cloudDiskMutex_);
709     cloudDiskMetaFile_.clear();
710     cloudDiskMetaFileList_.clear();
711 }
712 
GetCloudDiskMetaFile(uint32_t userId,const std::string & bundleName,const std::string & cloudId)713 std::shared_ptr<CloudDiskMetaFile> MetaFileMgr::GetCloudDiskMetaFile(uint32_t userId, const std::string &bundleName,
714     const std::string &cloudId)
715 {
716     std::shared_ptr<CloudDiskMetaFile> mFile = nullptr;
717     std::lock_guard<std::mutex> lock(cloudDiskMutex_);
718     MetaFileKey key(userId, cloudId + bundleName);
719     auto it = cloudDiskMetaFile_.find(key);
720     if (it != cloudDiskMetaFile_.end()) {
721         cloudDiskMetaFileList_.splice(cloudDiskMetaFileList_.begin(), cloudDiskMetaFileList_, it->second);
722         mFile = it->second->second;
723     } else {
724         if (cloudDiskMetaFile_.size() == MAX_CLOUDDISK_META_FILE_NUM) {
725             auto deleteKey = cloudDiskMetaFileList_.back().first;
726             cloudDiskMetaFile_.erase(deleteKey);
727             cloudDiskMetaFileList_.pop_back();
728         }
729         mFile = std::make_shared<CloudDiskMetaFile>(userId, bundleName, cloudId);
730         cloudDiskMetaFileList_.emplace_front(key, mFile);
731         cloudDiskMetaFile_[key] = cloudDiskMetaFileList_.begin();
732     }
733     return mFile;
734 }
735 
CreateRecycleDentry(uint32_t userId,const std::string & bundleName)736 int32_t MetaFileMgr::CreateRecycleDentry(uint32_t userId, const std::string &bundleName)
737 {
738     MetaBase metaBase(RECYCLE_NAME);
739     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, ROOT_CLOUD_ID);
740     int32_t ret = metaFile->DoLookup(metaBase);
741     if (ret != 0) {
742         metaBase.cloudId = RECYCLE_CLOUD_ID;
743         metaBase.mode = S_IFDIR | STAT_MODE_DIR;
744         metaBase.position = static_cast<uint8_t>(LOCAL);
745         ret = metaFile->DoCreate(metaBase);
746         if (ret != 0) {
747             return ret;
748         }
749     }
750     return 0;
751 }
752 
MoveIntoRecycleDentryfile(uint32_t userId,const std::string & bundleName,const std::string & name,const std::string & parentCloudId,int64_t rowId)753 int32_t MetaFileMgr::MoveIntoRecycleDentryfile(uint32_t userId, const std::string &bundleName, const std::string &name,
754     const std::string &parentCloudId, int64_t rowId)
755 {
756     MetaBase metaBase(name);
757     auto srcMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, parentCloudId);
758     auto dstMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, RECYCLE_CLOUD_ID);
759     std::string uniqueName = name + "_" + std::to_string(rowId);
760     int32_t ret = srcMetaFile->DoLookup(metaBase);
761     if (ret != E_OK) {
762         LOGE("lookup src metafile failed, ret = %{public}d", ret);
763         return ret;
764     }
765     metaBase.name = uniqueName;
766     ret = dstMetaFile->DoCreate(metaBase);
767     if (ret != E_OK) {
768         LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
769         return ret;
770     }
771     metaBase.name = name;
772     ret = srcMetaFile->DoLookupAndRemove(metaBase);
773     if (ret != E_OK) {
774         LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
775         metaBase.name = uniqueName;
776         (void)dstMetaFile->DoLookupAndRemove(metaBase);
777         return ret;
778     }
779     return E_OK;
780 }
781 
RemoveFromRecycleDentryfile(uint32_t userId,const std::string & bundleName,const struct RestoreInfo & restoreInfo)782 int32_t MetaFileMgr::RemoveFromRecycleDentryfile(uint32_t userId, const std::string &bundleName,
783     const struct RestoreInfo &restoreInfo)
784 {
785     auto srcMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, RECYCLE_CLOUD_ID);
786     auto dstMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(userId, bundleName, restoreInfo.parentCloudId);
787     std::string uniqueName = restoreInfo.oldName + "_" + std::to_string(restoreInfo.rowId);
788     MetaBase metaBase(uniqueName);
789     int32_t ret = srcMetaFile->DoLookup(metaBase);
790     if (ret != E_OK) {
791         LOGE("lookup and update dentry failed, ret = %{public}d", ret);
792         return ret;
793     }
794     metaBase.name = restoreInfo.newName;
795     ret = dstMetaFile->DoCreate(metaBase);
796     if (ret != E_OK) {
797         LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
798         return ret;
799     }
800     metaBase.name = uniqueName;
801     ret = srcMetaFile->DoLookupAndRemove(metaBase);
802     if (ret != E_OK) {
803         LOGE("lookup and remove dentry failed, ret = %{public}d", ret);
804         metaBase.name = restoreInfo.newName;
805         (void)dstMetaFile->DoLookupAndRemove(metaBase);
806         return ret;
807     }
808     return E_OK;
809 }
810 
GetNewName(std::shared_ptr<CloudDiskMetaFile> metaFile,const std::string & oldName,std::string & newName)811 int32_t MetaFileMgr::GetNewName(std::shared_ptr<CloudDiskMetaFile> metaFile, const std::string &oldName,
812     std::string &newName)
813 {
814     std::vector<MetaBase> metaBases;
815     int32_t ret = metaFile->LoadChildren(metaBases);
816     if (ret != E_OK) {
817         LOGE("load children dentry fail. ret = %{public}d", ret);
818         return ret;
819     }
820 
821     size_t lastDot = oldName.rfind('.');
822     if (lastDot == std::string::npos) {
823         lastDot = oldName.length();
824     }
825     std::string name = oldName.substr(0, lastDot);
826     std::string extension = oldName.substr(lastDot);
827     int32_t renameTimes = 1;
828     std::unordered_set<std::string> fileNames;
829     for (const MetaBase &meta : metaBases) {
830         fileNames.insert(meta.name);
831     }
832     bool conflict = true;
833     while (conflict) {
834         newName = name + "(" + std::to_string(renameTimes) + ")" + extension;
835         if (fileNames.find(newName) == fileNames.end()) {
836             conflict = false;
837         }
838         renameTimes++;
839     }
840     return E_OK;
841 }
842 } // namespace FileManagement
843 } // namespace OHOS
844