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