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