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