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