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