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