1 /*
2 * Copyright (c) 2023-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 "fuse_manager/fuse_manager.h"
17
18 #include <atomic>
19 #include <cassert>
20 #include <cerrno>
21 #include <chrono>
22 #include <cstddef>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 #include <ctime>
27 #include <exception>
28 #include <filesystem>
29 #include <fcntl.h>
30 #include <iostream>
31 #include <map>
32 #include <mutex>
33 #include <pthread.h>
34 #include <shared_mutex>
35 #include <stdexcept>
36 #include <string>
37 #include <sys/stat.h>
38 #include <sys/xattr.h>
39 #include <thread>
40 #include <unistd.h>
41 #include <sys/ioctl.h>
42 #include <vector>
43
44 #include "cloud_daemon_statistic.h"
45 #include "cloud_disk_inode.h"
46 #include "cloud_file_fault_event.h"
47 #include "cloud_file_kit.h"
48 #include "clouddisk_type_const.h"
49 #include "datetime_ex.h"
50 #include "dfs_error.h"
51 #include "directory_ex.h"
52 #include "fdsan.h"
53 #include "ffrt_inner.h"
54 #include "fuse_operations.h"
55 #ifdef HICOLLIE_ENABLE
56 #include "xcollie_helper.h"
57 #endif
58 #include "hitrace_meter.h"
59 #include "meta_file.h"
60 #include "securec.h"
61 #include "utils_log.h"
62
63 namespace OHOS {
64 namespace FileManagement {
65 namespace CloudFile {
66 using namespace std;
67 using PAGE_FLAG_TYPE = const uint32_t;
68
69 static const string LOCAL_PATH_DATA_SERVICE_EL2 = "/data/service/el2/";
70 static const string LOCAL_PATH_HMDFS_CLOUD_CACHE = "/hmdfs/cache/cloud_cache";
71 static const string CLOUD_CACHE_DIR = "/pread_cache";
72 static const string CLOUD_CACHE_XATTR_NAME = "user.cloud.cacheMap";
73 static const string VIDEO_TYPE_PREFIX = "VID_";
74 static const string HMDFS_PATH_PREFIX = "/mnt/hmdfs/";
75 static const string LOCAL_PATH_SUFFIX = "/account/device_view/local";
76 static const string CLOUD_MERGE_VIEW_PATH_SUFFIX = "/account/cloud_merge_view";
77 static const string PATH_TEMP_SUFFIX = ".temp.fuse";
78 static const string PHOTOS_BUNDLE_NAME = "com.ohos.photos";
79 static const unsigned int OID_USER_DATA_RW = 1008;
80 static const unsigned int STAT_NLINK_REG = 1;
81 static const unsigned int STAT_NLINK_DIR = 2;
82 static const unsigned int STAT_MODE_REG = 0770;
83 static const unsigned int STAT_MODE_DIR = 0771;
84 static const unsigned int MAX_READ_SIZE = 4 * 1024 * 1024;
85 static const unsigned int KEY_FRAME_SIZE = 8192;
86 static const unsigned int MAX_IDLE_THREADS = 10;
87 static const unsigned int READ_CACHE_SLEEP = 10 * 1000;
88 static const unsigned int CACHE_PAGE_NUM = 2;
89 static const unsigned int HMDFS_IOC = 0xf2;
90 static const std::chrono::seconds READ_TIMEOUT_S = 16s;
91 static const std::chrono::seconds OPEN_TIMEOUT_S = 4s;
92 static const unsigned int LOOKUP_TIMEOUT_S = 1;
93 static const unsigned int FORGET_TIMEOUT_S = 1;
94
95 #define HMDFS_IOC_HAS_CACHE _IOW(HMDFS_IOC, 6, struct HmdfsHasCache)
96 #define HMDFS_IOC_CANCEL_READ _IO(HMDFS_IOC, 8)
97 #define HMDFS_IOC_RESET_READ _IO(HMDFS_IOC, 9)
98 PAGE_FLAG_TYPE PG_READAHEAD = 0x00000001;
99 PAGE_FLAG_TYPE PG_UPTODATE = 0x00000002;
100 PAGE_FLAG_TYPE PG_REFERENCED = 0x00000004;
101 PAGE_FLAG_TYPE PG_NEEDBECLEANED = 0x00000020;
102
103 enum CLOUD_READ_STATUS {
104 READING = 0,
105 READ_FINISHED,
106 READ_CANCELED,
107 };
108
109 enum CLOUD_CACHE_STATUS : int {
110 NOT_CACHE = 0,
111 HAS_CACHED,
112 };
113
114 struct ReadCacheInfo {
115 std::mutex mutex;
116 std::condition_variable cond{};
117 uint32_t flags{0};
118 };
119
120 struct ReadSlice {
121 size_t sizeHead{0};
122 size_t sizeTail{0};
123 off_t offHead{0};
124 off_t offTail{0};
125 };
126
127 struct ReadArguments {
128 size_t size{0};
129 off_t offset{0};
130 pid_t pid{0};
131 shared_ptr<int64_t> readResult{nullptr};
132 shared_ptr<CLOUD_READ_STATUS> readStatus{nullptr};
133 shared_ptr<CloudFile::CloudError> ckError{nullptr};
134 shared_ptr<ffrt::condition_variable> cond{nullptr};
135 shared_ptr<char> buf{nullptr};
136
ReadArgumentsOHOS::FileManagement::CloudFile::ReadArguments137 ReadArguments(size_t readSize, off_t readOffset, pid_t readPid) : size(readSize), offset(readOffset), pid(readPid)
138 {
139 readResult = make_shared<int64_t>(-1);
140 readStatus = make_shared<CLOUD_READ_STATUS>(READING);
141 ckError = make_shared<CloudFile::CloudError>();
142 cond = make_shared<ffrt::condition_variable>();
143 if (0 < readSize && readSize <= MAX_READ_SIZE) {
144 buf.reset(new char[readSize], [](char *ptr) { delete[] ptr; });
145 }
146 }
147 };
148
149 struct CloudInode {
150 shared_ptr<MetaBase> mBase{nullptr};
151 string path;
152 fuse_ino_t parent{0};
153 atomic<int> refCount{0};
154 shared_ptr<CloudFile::CloudAssetReadSession> readSession{nullptr};
155 atomic<int> sessionRefCount{0};
156 std::shared_mutex sessionLock;
157 ffrt::mutex readLock;
158 ffrt::mutex openLock;
159 std::mutex readArgsLock;
160 off_t offset{0xffffffff};
161 std::map<int64_t, std::shared_ptr<ReadCacheInfo>> readCacheMap;
162 /* variable readArguments for cancel*/
163 std::set<shared_ptr<ReadArguments>> readArgsSet;
164 /* process's read status for ioctl, true when canceled */
165 std::map<pid_t, bool> readCtlMap;
166 std::unique_ptr<CLOUD_CACHE_STATUS[]> cacheFileIndex{nullptr};
SetReadCacheFlagOHOS::FileManagement::CloudFile::CloudInode167 bool SetReadCacheFlag(int64_t index, PAGE_FLAG_TYPE flag)
168 {
169 std::shared_lock lock(sessionLock);
170 auto it = readCacheMap.find(index);
171 if (it != readCacheMap.end()) {
172 std::unique_lock<std::mutex> flock(it->second->mutex);
173 it->second->flags |= flag;
174 return true;
175 }
176 return false;
177 }
IsReadAheadOHOS::FileManagement::CloudFile::CloudInode178 bool IsReadAhead(int64_t index)
179 {
180 std::shared_lock lock(sessionLock);
181 auto it = readCacheMap.find(index);
182 if (it == readCacheMap.end()) {
183 return false;
184 }
185 std::unique_lock<std::mutex> flock(it->second->mutex);
186 return it->second->flags & PG_READAHEAD;
187 }
188
IsReadFinishedOHOS::FileManagement::CloudFile::CloudInode189 bool IsReadFinished(int64_t index)
190 {
191 std::shared_lock lock(sessionLock);
192 auto it = readCacheMap.find(index);
193 if (it == readCacheMap.end()) {
194 return false;
195 }
196 std::unique_lock<std::mutex> flock(it->second->mutex);
197 return it->second->flags & PG_UPTODATE;
198 }
199 };
200
201 struct DoCloudReadParams {
202 shared_ptr<CloudInode> cInode{nullptr};
203 shared_ptr<CloudFile::CloudAssetReadSession> readSession{nullptr};
204 shared_ptr<ReadArguments> readArgsHead{nullptr};
205 shared_ptr<ReadArguments> readArgsTail{nullptr};
206 };
207
208 struct HmdfsHasCache {
209 int64_t offset;
210 int64_t readSize;
211 };
212
213 struct FuseData {
214 int userId;
215 shared_ptr<CloudInode> rootNode{nullptr};
216 /* store CloudInode by path */
217 map<string, shared_ptr<CloudInode>> inodeCache;
218 std::shared_mutex cacheLock;
219 shared_ptr<CloudFile::CloudDatabase> database;
220 struct fuse_session *se;
221 };
222
InsertReadArgs(shared_ptr<CloudInode> cInode, vector<shared_ptr<ReadArguments>> readArgsList)223 static void InsertReadArgs(shared_ptr<CloudInode> cInode, vector<shared_ptr<ReadArguments>> readArgsList)
224 {
225 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
226 for (auto &it : readArgsList) {
227 if (it) {
228 cInode->readArgsSet.insert(it);
229 }
230 }
231 }
232
EraseReadArgs(shared_ptr<CloudInode> cInode, vector<shared_ptr<ReadArguments>> readArgsList)233 static void EraseReadArgs(shared_ptr<CloudInode> cInode, vector<shared_ptr<ReadArguments>> readArgsList)
234 {
235 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
236 for (auto &it : readArgsList) {
237 if (it) {
238 auto readArgs = cInode->readArgsSet.find(it);
239 if (readArgs != cInode->readArgsSet.end()) {
240 cInode->readArgsSet.erase(readArgs);
241 }
242 }
243 }
244 }
245
GetLocalPath(int32_t userId, const string &relativePath)246 static string GetLocalPath(int32_t userId, const string &relativePath)
247 {
248 return HMDFS_PATH_PREFIX + to_string(userId) + LOCAL_PATH_SUFFIX + relativePath;
249 }
250
GetCacheTmpPath(int32_t userId, const string &relativePath)251 static string GetCacheTmpPath(int32_t userId, const string &relativePath)
252 {
253 return HMDFS_PATH_PREFIX + to_string(userId) + LOCAL_PATH_SUFFIX + "/services/drivekit_cache/" +
254 relativePath + PATH_TEMP_SUFFIX;
255 }
256
HandleCloudError(CloudError error, FaultOperation faultOperation)257 static int HandleCloudError(CloudError error, FaultOperation faultOperation)
258 {
259 int ret = 0;
260 FaultType faultType = FaultType::DRIVERKIT;
261 switch (error) {
262 case CloudError::CK_NO_ERROR:
263 ret = 0;
264 break;
265 case CloudError::CK_NETWORK_ERROR:
266 ret = -ENOTCONN;
267 faultType = FaultType::DRIVERKIT_NETWORK;
268 break;
269 case CloudError::CK_SERVER_ERROR:
270 ret = -EIO;
271 faultType = FaultType::DRIVERKIT_SERVER;
272 break;
273 case CloudError::CK_LOCAL_ERROR:
274 ret = -EINVAL;
275 faultType = FaultType::DRIVERKIT_LOCAL;
276 break;
277 default:
278 ret = -EIO;
279 break;
280 }
281 if (ret < 0) {
282 string msg = "handle cloud failed, ret code: " + to_string(ret);
283 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, faultOperation,
284 faultType, ret, msg});
285 }
286 return ret;
287 }
288
289 #ifdef HICOLLIE_ENABLE
290
291 struct XcollieInput {
292 CloudInode *node_;
293 FaultOperation faultOperation_;
294 };
295
XcollieCallback(void *xcollie)296 static void XcollieCallback(void *xcollie)
297 {
298 auto xcollieInput = reinterpret_cast<XcollieInput *>(xcollie);
299 if (xcollieInput == nullptr) {
300 return;
301 }
302 CloudInode *inode = xcollieInput->node_;
303 if (inode == nullptr) {
304 return;
305 }
306
307 FaultType faultType = FaultType::TIMEOUT;
308 switch (xcollieInput->faultOperation_) {
309 case FaultOperation::LOOKUP:
310 faultType = FaultType::CLOUD_FILE_LOOKUP_TIMEOUT;
311 break;
312 case FaultOperation::FORGET:
313 faultType = FaultType::CLOUD_FILE_FORGET_TIMEOUT;
314 break;
315 default:
316 break;
317 }
318
319 string msg = "In XcollieCallback, path:" + GetAnonyString((inode->path).c_str());
320 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, xcollieInput->faultOperation_,
321 faultType, EWOULDBLOCK, msg});
322 }
323 #endif
324
GetDatabase(struct FuseData *data)325 static shared_ptr<CloudDatabase> GetDatabase(struct FuseData *data)
326 {
327 if (!data->database) {
328 auto instance = CloudFile::CloudFileKit::GetInstance();
329 if (instance == nullptr) {
330 LOGE("get cloud file helper instance failed");
331 return nullptr;
332 }
333
334 data->database = instance->GetCloudDatabase(data->userId, PHOTOS_BUNDLE_NAME);
335 if (data->database == nullptr) {
336 LOGE("get cloud file kit database fail");
337 return nullptr;
338 }
339 }
340 return data->database;
341 }
342
FindNode(struct FuseData *data, string path)343 static shared_ptr<CloudInode> FindNode(struct FuseData *data, string path)
344 {
345 shared_ptr<CloudInode> ret = nullptr;
346 std::shared_lock<std::shared_mutex> rLock(data->cacheLock, std::defer_lock);
347 rLock.lock();
348 if (data->inodeCache.count(path) != 0) {
349 ret = data->inodeCache.at(path);
350 }
351 rLock.unlock();
352 return ret;
353 }
354
GetRootInode(struct FuseData *data, fuse_ino_t ino)355 static shared_ptr<CloudInode> GetRootInode(struct FuseData *data, fuse_ino_t ino)
356 {
357 std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
358 shared_ptr<CloudInode> ret;
359
360 wLock.lock();
361 if (!data->rootNode) {
362 data->rootNode = make_shared<CloudInode>();
363 data->rootNode->path = "/";
364 data->rootNode->refCount = 1;
365 data->rootNode->mBase = make_shared<MetaBase>();
366 data->rootNode->mBase->mode = S_IFDIR;
367 data->rootNode->mBase->mtime = static_cast<uint64_t>(GetSecondsSince1970ToNow());
368 LOGD("create rootNode");
369 }
370 ret = data->rootNode;
371 wLock.unlock();
372
373 return ret;
374 }
375
GetCloudInode(struct FuseData *data, fuse_ino_t ino)376 static shared_ptr<CloudInode> GetCloudInode(struct FuseData *data, fuse_ino_t ino)
377 {
378 if (ino == FUSE_ROOT_ID) {
379 return GetRootInode(data, ino);
380 } else {
381 struct CloudInode *inoPtr = reinterpret_cast<struct CloudInode *>(ino);
382 if (inoPtr == nullptr) {
383 LOGE("inoPtr is nullptr");
384 return nullptr;
385 }
386 return FindNode(data, inoPtr->path);
387 }
388 }
389
CloudPath(struct FuseData *data, fuse_ino_t ino)390 static string CloudPath(struct FuseData *data, fuse_ino_t ino)
391 {
392 auto inode = GetCloudInode(data, ino);
393 if (inode) {
394 return inode->path;
395 } else {
396 LOGE("find node is nullptr");
397 return "";
398 }
399 }
400
GetMetaAttr(struct FuseData *data, shared_ptr<CloudInode> ino, struct stat *stbuf)401 static void GetMetaAttr(struct FuseData *data, shared_ptr<CloudInode> ino, struct stat *stbuf)
402 {
403 stbuf->st_ino = reinterpret_cast<fuse_ino_t>(ino.get());
404 stbuf->st_uid = OID_USER_DATA_RW;
405 stbuf->st_gid = OID_USER_DATA_RW;
406 stbuf->st_mtime = static_cast<int64_t>(ino->mBase->mtime);
407 if (ino->mBase->mode & S_IFDIR) {
408 stbuf->st_mode = S_IFDIR | STAT_MODE_DIR;
409 stbuf->st_nlink = STAT_NLINK_DIR;
410 LOGD("directory, ino:%s", GetAnonyString(ino->path).c_str());
411 } else {
412 stbuf->st_mode = S_IFREG | STAT_MODE_REG;
413 stbuf->st_nlink = STAT_NLINK_REG;
414 stbuf->st_size = static_cast<decltype(stbuf->st_size)>(ino->mBase->size);
415 LOGD("regular file, ino:%s, size: %lld", GetAnonyString(ino->path).c_str(), (long long)stbuf->st_size);
416 }
417 }
418
CloudDoLookupHelper(fuse_ino_t parent, const char *name, struct fuse_entry_param *e, FuseData *data, string& parentName)419 static int CloudDoLookupHelper(fuse_ino_t parent, const char *name, struct fuse_entry_param *e,
420 FuseData *data, string& parentName)
421 {
422 shared_ptr<CloudInode> child;
423 bool create = false;
424 string childName = (parent == FUSE_ROOT_ID) ? parentName + name : parentName + "/" + name;
425 std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
426
427 LOGD("parent: %{private}s, name: %s", GetAnonyString(parentName).c_str(), GetAnonyString(name).c_str());
428
429 child = FindNode(data, childName);
430 if (!child) {
431 child = make_shared<CloudInode>();
432 create = true;
433 LOGD("new child %s", GetAnonyString(child->path).c_str());
434 }
435 MetaBase mBase(name);
436 int err = MetaFile(data->userId, parentName).DoLookup(mBase);
437 if (err) {
438 LOGE("lookup %s error, err: %{public}d", GetAnonyString(childName).c_str(), err);
439 return err;
440 }
441
442 child->refCount++;
443 if (create) {
444 child->mBase = make_shared<MetaBase>(mBase);
445 child->path = childName;
446 child->parent = parent;
447 #ifdef HICOLLIE_ENABLE
448 XcollieInput xcollieInput{child.get(), FaultOperation::LOOKUP};
449 auto xcollieId = XCollieHelper::SetTimer("CloudFileDaemon_CloudLookup", LOOKUP_TIMEOUT_S,
450 XcollieCallback, &xcollieInput, false);
451 #endif
452 wLock.lock();
453 data->inodeCache[child->path] = child;
454 wLock.unlock();
455 #ifdef HICOLLIE_ENABLE
456 XCollieHelper::CancelTimer(xcollieId);
457 #endif
458 } else if (*(child->mBase) != mBase) {
459 LOGW("invalidate %s", childName.c_str());
460 child->mBase = make_shared<MetaBase>(mBase);
461 }
462 LOGD("lookup success, child: %{private}s, refCount: %lld", GetAnonyString(child->path).c_str(),
463 static_cast<long long>(child->refCount));
464 GetMetaAttr(data, child, &e->attr);
465 e->ino = reinterpret_cast<fuse_ino_t>(child.get());
466 return 0;
467 }
468
CloudDoLookup(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse_entry_param *e)469 static int CloudDoLookup(fuse_req_t req, fuse_ino_t parent, const char *name,
470 struct fuse_entry_param *e)
471 {
472 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
473 string parentName = CloudPath(data, parent);
474 if (parentName == "") {
475 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::LOOKUP,
476 FaultType::FILE, ENOENT, "parent name is empty"});
477 return ENOENT;
478 }
479
480 return CloudDoLookupHelper(parent, name, e, data, parentName);
481 }
482
CloudLookup(fuse_req_t req, fuse_ino_t parent, const char *name)483 static void CloudLookup(fuse_req_t req, fuse_ino_t parent,
484 const char *name)
485 {
486 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
487 struct fuse_entry_param e;
488 int err;
489
490 err = CloudDoLookup(req, parent, name, &e);
491 if (err) {
492 fuse_reply_err(req, err);
493 } else {
494 fuse_reply_entry(req, &e);
495 }
496 }
497
PutNode(struct FuseData *data, shared_ptr<CloudInode> node, uint64_t num)498 static void PutNode(struct FuseData *data, shared_ptr<CloudInode> node, uint64_t num)
499 {
500 std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
501 node->refCount -= num;
502 LOGD("%s, put num: %lld, current refCount: %d",
503 GetAnonyString(node->path).c_str(), (long long)num, node->refCount.load());
504 if (node->refCount == 0) {
505 LOGD("node released: %s", GetAnonyString(node->path).c_str());
506 #ifdef HICOLLIE_ENABLE
507 XcollieInput xcollieInput{node.get(), FaultOperation::FORGET};
508 auto xcollieId = XCollieHelper::SetTimer("CloudFileDaemon_CloudForget", FORGET_TIMEOUT_S,
509 XcollieCallback, &xcollieInput, false);
510 #endif
511 if (node->mBase != nullptr && (node->mBase->mode & S_IFDIR)) {
512 LOGW("PutNode directory inode, path is %{public}s", GetAnonyString(node->path).c_str());
513 }
514 wLock.lock();
515 data->inodeCache.erase(node->path);
516 wLock.unlock();
517 #ifdef HICOLLIE_ENABLE
518 XCollieHelper::CancelTimer(xcollieId);
519 #endif
520 }
521 }
522
CloudForget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)523 static void CloudForget(fuse_req_t req, fuse_ino_t ino,
524 uint64_t nlookup)
525 {
526 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
527 shared_ptr<CloudInode> node = GetCloudInode(data, ino);
528 if (node) {
529 LOGD("forget %s, nlookup: %lld", GetAnonyString(node->path).c_str(), (long long)nlookup);
530 PutNode(data, node, nlookup);
531 }
532 fuse_reply_none(req);
533 }
534
CloudGetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)535 static void CloudGetAttr(fuse_req_t req, fuse_ino_t ino,
536 struct fuse_file_info *fi)
537 {
538 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
539 struct stat buf;
540 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
541 (void) fi;
542
543 LOGD("getattr, %s", GetAnonyString(CloudPath(data, ino)).c_str());
544 shared_ptr<CloudInode> node = GetCloudInode(data, ino);
545 if (!node || !node->mBase) {
546 fuse_reply_err(req, ENOMEM);
547 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::GETATTR,
548 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
549 return;
550 }
551 GetMetaAttr(data, node, &buf);
552
553 fuse_reply_attr(req, &buf, 0);
554 }
555
GetAssetKey(int fileType)556 static string GetAssetKey(int fileType)
557 {
558 switch (fileType) {
559 case FILE_TYPE_CONTENT:
560 return "content";
561 case FILE_TYPE_THUMBNAIL:
562 return "thumbnail";
563 case FILE_TYPE_LCD:
564 return "lcd";
565 default:
566 LOGE("bad fileType %{public}d", fileType);
567 return "";
568 }
569 }
570
GetCloudMergeViewPath(int32_t userId, const string &relativePath)571 static string GetCloudMergeViewPath(int32_t userId, const string &relativePath)
572 {
573 return HMDFS_PATH_PREFIX + to_string(userId) + CLOUD_MERGE_VIEW_PATH_SUFFIX + relativePath;
574 }
575
GetAssetPath(shared_ptr<CloudInode> cInode, struct FuseData *data)576 static string GetAssetPath(shared_ptr<CloudInode> cInode, struct FuseData *data)
577 {
578 string path;
579 filesystem::path parentPath;
580 path = GetCacheTmpPath(data->userId, cInode->path);
581 parentPath = filesystem::path(path).parent_path();
582 ForceCreateDirectory(parentPath.string());
583 LOGD("fileType: %d, create dir: %s, relative path: %s",
584 cInode->mBase->fileType, GetAnonyString(parentPath.string()).c_str(), GetAnonyString(cInode->path).c_str());
585 return path;
586 }
587
fuse_inval(fuse_session *se, fuse_ino_t parentIno, fuse_ino_t childIno, const string &childName)588 static void fuse_inval(fuse_session *se, fuse_ino_t parentIno, fuse_ino_t childIno, const string &childName)
589 {
590 auto task = [se, parentIno, childIno, childName] {
591 if (fuse_lowlevel_notify_inval_entry(se, parentIno, childName.c_str(), childName.size())) {
592 fuse_lowlevel_notify_inval_inode(se, childIno, 0, 0);
593 }
594 };
595 ffrt::submit(task, {}, {}, ffrt::task_attr().qos(ffrt_qos_background));
596 }
597
CloudOpenOnLocal(struct FuseData *data, shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)598 static int CloudOpenOnLocal(struct FuseData *data, shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)
599 {
600 string localPath = GetLocalPath(data->userId, cInode->path);
601 string tmpPath = GetCacheTmpPath(data->userId, cInode->path);
602 char resolvedPath[PATH_MAX + 1] = {'\0'};
603 char *realPath = realpath(tmpPath.c_str(), resolvedPath);
604 if (realPath == nullptr) {
605 LOGE("Failed to realpath, errno: %{public}d", errno);
606 return 0;
607 }
608 unsigned int flags = static_cast<unsigned int>(fi->flags);
609 if (flags & O_DIRECT) {
610 flags &= ~O_DIRECT;
611 }
612 auto fd = open(realPath, flags);
613 if (fd < 0) {
614 LOGE("Failed to open local file, errno: %{public}d", errno);
615 return 0;
616 }
617 auto fdsan = new fdsan_fd(fd);
618 string cloudMergeViewPath = GetCloudMergeViewPath(data->userId, cInode->path);
619 if (remove(cloudMergeViewPath.c_str()) < 0) {
620 LOGE("Failed to update kernel dentry cache, errno: %{public}d", errno);
621 delete fdsan;
622 return 0;
623 }
624
625 filesystem::path parentPath = filesystem::path(localPath).parent_path();
626 ForceCreateDirectory(parentPath.string());
627 if (rename(tmpPath.c_str(), localPath.c_str()) < 0) {
628 LOGE("Failed to rename tmpPath to localPath, errno: %{public}d", errno);
629 delete fdsan;
630 return -errno;
631 }
632 auto parentInode = GetCloudInode(data, cInode->parent);
633 if (parentInode == nullptr) {
634 LOGE("fail to find parent inode");
635 delete fdsan;
636 return -ENOMEM;
637 }
638 MetaFile(data->userId, parentInode->path).DoRemove(*(cInode->mBase));
639 cInode->mBase->hasDownloaded = true;
640 fi->fh = reinterpret_cast<uint64_t>(fdsan);
641 return 0;
642 }
643
HandleOpenResult(CloudFile::CloudError ckError, struct FuseData *data, shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)644 static int HandleOpenResult(CloudFile::CloudError ckError, struct FuseData *data,
645 shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)
646 {
647 auto ret = HandleCloudError(ckError, FaultOperation::OPEN);
648 if (ret < 0) {
649 return ret;
650 }
651 if (cInode->mBase->fileType != FILE_TYPE_CONTENT) {
652 ret = CloudOpenOnLocal(data, cInode, fi);
653 if (ret < 0) {
654 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
655 FaultType::INODE_FILE, ret, "inner error"});
656 return ret;
657 }
658 }
659 cInode->sessionRefCount++;
660 LOGI("open success, sessionRefCount: %{public}d", cInode->sessionRefCount.load());
661 return 0;
662 }
663
UTCTimeMilliSeconds()664 static uint64_t UTCTimeMilliSeconds()
665 {
666 struct timespec t;
667 clock_gettime(CLOCK_REALTIME, &t);
668 return t.tv_sec * CloudDisk::SECOND_TO_MILLISECOND + t.tv_nsec / CloudDisk::MILLISECOND_TO_NANOSECOND;
669 }
670
DownloadThmOrLcd(shared_ptr<CloudInode> cInode, shared_ptr<CloudError> err, shared_ptr<bool> openFinish, shared_ptr<ffrt::condition_variable> cond)671 static void DownloadThmOrLcd(shared_ptr<CloudInode> cInode, shared_ptr<CloudError> err, shared_ptr<bool> openFinish,
672 shared_ptr<ffrt::condition_variable> cond)
673 {
674 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
675 auto session = cInode->readSession;
676 if (!session) {
677 LOGE("readSession is nullptr");
678 return;
679 }
680 *err = session->InitSession();
681 {
682 unique_lock lck(cInode->openLock);
683 *openFinish = true;
684 }
685 cond->notify_one();
686 LOGI("download done, path: %{public}s", GetAnonyString(cInode->path).c_str());
687 return;
688 }
689
IsVideoType(const string &name)690 static bool IsVideoType(const string &name)
691 {
692 return name.find(VIDEO_TYPE_PREFIX) == 0;
693 }
694
LoadCacheFileIndex(shared_ptr<CloudInode> cInode, int32_t userId)695 static void LoadCacheFileIndex(shared_ptr<CloudInode> cInode, int32_t userId)
696 {
697 string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE +
698 CLOUD_CACHE_DIR + cInode->path;
699 int filePageSize = static_cast<int32_t>(cInode->mBase->size / MAX_READ_SIZE + 1);
700 CLOUD_CACHE_STATUS *tmp = new CLOUD_CACHE_STATUS[filePageSize]();
701 std::unique_ptr<CLOUD_CACHE_STATUS[]> mp(tmp);
702 if (access(cachePath.c_str(), F_OK) != 0) {
703 cInode->cacheFileIndex = std::move(mp);
704 string parentPath = filesystem::path(cachePath).parent_path().string();
705 if (!ForceCreateDirectory(parentPath)) {
706 LOGE("failed to create parent dir");
707 return;
708 }
709 int fd = open(cachePath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
710 if (fd < 0) {
711 LOGE("failed to open cache file, ret: %{public}d", errno);
712 return;
713 }
714 if (ftruncate(fd, cInode->mBase->size) == -1) {
715 LOGE("failed to truncate file, ret: %{public}d", errno);
716 }
717 close(fd);
718 return;
719 }
720
721 if (getxattr(cachePath.c_str(), CLOUD_CACHE_XATTR_NAME.c_str(), mp.get(),
722 sizeof(CLOUD_CACHE_STATUS[filePageSize])) < 0 &&
723 errno != ENODATA) {
724 LOGE("getxattr fail, err: %{public}d", errno);
725 cInode->cacheFileIndex = std::move(mp);
726 return;
727 }
728 for (int i = 0; i < filePageSize; i++) {
729 if (mp.get()[i] == HAS_CACHED) {
730 auto memInfo = std::make_shared<ReadCacheInfo>();
731 memInfo->flags = PG_UPTODATE;
732 cInode->readCacheMap[i] = memInfo;
733 }
734 }
735 cInode->cacheFileIndex = std::move(mp);
736 }
737
DoCloudOpen(shared_ptr<CloudInode> cInode, struct fuse_file_info *fi, struct FuseData *data)738 static int DoCloudOpen(shared_ptr<CloudInode> cInode, struct fuse_file_info *fi, struct FuseData *data)
739 {
740 auto error = make_shared<CloudError>();
741 auto openFinish = make_shared<bool>(false);
742 auto cond = make_shared<ffrt::condition_variable>();
743 ffrt::submit([cInode, error, openFinish, cond, data] {
744 if (IsVideoType(cInode->mBase->name)) {
745 LoadCacheFileIndex(cInode, data->userId);
746 }
747 DownloadThmOrLcd(cInode, error, openFinish, cond);
748 });
749 unique_lock lck(cInode->openLock);
750 auto waitStatus = cond->wait_for(lck, OPEN_TIMEOUT_S, [openFinish] {
751 return *openFinish;
752 });
753 if (!waitStatus) {
754 string msg = "init session timeout, path: " + GetAnonyString((cInode->path).c_str());
755 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
756 FaultType::OPEN_CLOUD_FILE_TIMEOUT, ENETUNREACH, msg});
757 return -ENETUNREACH;
758 }
759 return HandleOpenResult(*error, data, cInode, fi);
760 }
761
UpdateReadStat(shared_ptr<CloudInode> cInode, uint64_t startTime)762 static void UpdateReadStat(shared_ptr<CloudInode> cInode, uint64_t startTime)
763 {
764 uint64_t endTime = UTCTimeMilliSeconds();
765 CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
766 readStat.UpdateOpenSizeStat(cInode->mBase->size);
767 readStat.UpdateOpenTimeStat(cInode->mBase->fileType, (endTime > startTime) ? (endTime - startTime) : 0);
768 }
769
CloudOpenHelper(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct FuseData *data, shared_ptr<CloudInode>& cInode)770 static void CloudOpenHelper(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
771 struct FuseData *data, shared_ptr<CloudInode>& cInode)
772 {
773 string recordId = MetaFileMgr::GetInstance().CloudIdToRecordId(cInode->mBase->cloudId);
774 shared_ptr<CloudFile::CloudDatabase> database = GetDatabase(data);
775 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
776
777 LOGI("%{public}d open %{public}s", req->ctx.pid, GetAnonyString(CloudPath(data, ino)).c_str());
778 if (!database) {
779 LOGE("database is null");
780 fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
781 fuse_reply_err(req, EPERM);
782 return;
783 }
784 wSesLock.lock();
785 if (!cInode->readSession) {
786 /*
787 * 'recordType' is fixed to "media" now
788 * 'assetKey' is one of "content"/"lcd"/"thumbnail"
789 */
790 LOGD("recordId: %s", recordId.c_str());
791 uint64_t startTime = UTCTimeMilliSeconds();
792 cInode->readSession = database->NewAssetReadSession("media", recordId, GetAssetKey(cInode->mBase->fileType),
793 GetAssetPath(cInode, data));
794 if (cInode->readSession) {
795 auto ret = DoCloudOpen(cInode, fi, data);
796 if (ret == 0) {
797 fuse_reply_open(req, fi);
798 } else {
799 fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
800 fuse_reply_err(req, -ret);
801 cInode->readSession = nullptr;
802 }
803 UpdateReadStat(cInode, startTime);
804 wSesLock.unlock();
805 return;
806 }
807 }
808 if (!cInode->readSession) {
809 fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
810 fuse_reply_err(req, EPERM);
811 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
812 FaultType::DRIVERKIT, EPERM, "readSession is null or fix size fail"});
813 } else {
814 cInode->sessionRefCount++;
815 LOGI("open success, sessionRefCount: %{public}d", cInode->sessionRefCount.load());
816 fuse_reply_open(req, fi);
817 }
818 wSesLock.unlock();
819 }
820
CloudOpen(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)821 static void CloudOpen(fuse_req_t req, fuse_ino_t ino,
822 struct fuse_file_info *fi)
823 {
824 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
825 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
826 fi->fh = UINT64_MAX;
827 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
828 if (!cInode) {
829 fuse_reply_err(req, ENOMEM);
830 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
831 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
832 return;
833 }
834
835 CloudOpenHelper(req, ino, fi, data, cInode);
836 }
837
CloudRelease(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)838 static void CloudRelease(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
839 {
840 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
841 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
842 if (!cInode) {
843 fuse_reply_err(req, ENOMEM);
844 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::RELEASE,
845 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
846 return;
847 }
848 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
849 LOGI("%{public}d release %{public}s, sessionRefCount: %{public}d", req->ctx.pid,
850 GetAnonyString(cInode->path).c_str(), cInode->sessionRefCount.load());
851 wSesLock.lock();
852 cInode->sessionRefCount--;
853 if (cInode->sessionRefCount == 0) {
854 if (fi->fh != UINT64_MAX) {
855 auto fdsan = reinterpret_cast<fdsan_fd *>(fi->fh);
856 delete fdsan;
857 fi->fh = UINT64_MAX;
858 }
859 if (cInode->mBase->fileType == FILE_TYPE_CONTENT && (!cInode->readSession->Close(false))) {
860 LOGE("Failed to close readSession");
861 }
862 cInode->readSession = nullptr;
863 cInode->readCacheMap.clear();
864 {
865 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
866 cInode->readCtlMap.clear();
867 }
868 if (cInode->cacheFileIndex) {
869 string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(data->userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE +
870 CLOUD_CACHE_DIR + cInode->path;
871 if (setxattr(cachePath.c_str(), CLOUD_CACHE_XATTR_NAME.c_str(), cInode->cacheFileIndex.get(),
872 sizeof(int[cInode->mBase->size / MAX_READ_SIZE + 1]), 0) < 0) {
873 LOGE("setxattr fail, err: %{public}d", errno);
874 }
875 }
876 LOGD("readSession released");
877 }
878 wSesLock.unlock();
879
880 LOGI("release end");
881 fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
882 fuse_reply_err(req, 0);
883 }
884
CloudReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)885 static void CloudReadDir(fuse_req_t req, fuse_ino_t ino, size_t size,
886 off_t off, struct fuse_file_info *fi)
887 {
888 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
889 LOGE("readdir %s, not support", GetAnonyString(CloudPath(data, ino)).c_str());
890 fuse_reply_err(req, ENOENT);
891 }
892
CloudForgetMulti(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)893 static void CloudForgetMulti(fuse_req_t req, size_t count,
894 struct fuse_forget_data *forgets)
895 {
896 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
897 LOGD("forget_multi");
898 for (size_t i = 0; i < count; i++) {
899 shared_ptr<CloudInode> node = GetCloudInode(data, forgets[i].ino);
900 if (!node) {
901 continue;
902 }
903 LOGD("forget (i=%zu) %s, nlookup: %lld", i, GetAnonyString(node->path).c_str(), (long long)forgets[i].nlookup);
904 PutNode(data, node, forgets[i].nlookup);
905 }
906 fuse_reply_none(req);
907 }
908
HasCache(fuse_req_t req, fuse_ino_t ino, const void *inBuf)909 static void HasCache(fuse_req_t req, fuse_ino_t ino, const void *inBuf)
910 {
911 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
912 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
913 if (!cInode || !cInode->readSession) {
914 fuse_reply_err(req, ENOMEM);
915 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::IOCTL,
916 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
917 return;
918 }
919
920 const struct HmdfsHasCache *ioctlData = reinterpret_cast<const struct HmdfsHasCache *>(inBuf);
921 if (!ioctlData || ioctlData->offset < 0 || ioctlData->readSize < 0) {
922 fuse_reply_err(req, EINVAL);
923 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::IOCTL,
924 FaultType::FILE, EINVAL, "invalid argument in ioctl"});
925 return;
926 }
927 int64_t headIndex = ioctlData->offset / MAX_READ_SIZE;
928 int64_t tailIndex = (ioctlData->offset + ioctlData->readSize - 1) / MAX_READ_SIZE;
929 if (cInode->IsReadFinished(headIndex) && cInode->IsReadFinished(tailIndex)) {
930 fuse_reply_ioctl(req, 0, NULL, 0);
931 } else {
932 fuse_reply_err(req, EIO);
933 }
934 }
935
CheckReadIsCanceled(pid_t pid, shared_ptr<CloudInode> cInode)936 static bool CheckReadIsCanceled(pid_t pid, shared_ptr<CloudInode> cInode)
937 {
938 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
939 return cInode->readCtlMap[pid];
940 }
941
CancelRead(fuse_req_t req, fuse_ino_t ino)942 static void CancelRead(fuse_req_t req, fuse_ino_t ino)
943 {
944 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
945 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
946 if (!cInode) {
947 fuse_reply_err(req, ENOMEM);
948 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
949 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
950 return;
951 }
952 LOGI("Cancel read for pid: %{public}d", req->ctx.pid);
953 {
954 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
955 cInode->readCtlMap[req->ctx.pid] = true;
956 for (const auto &it : cInode->readArgsSet) {
957 if (it->pid == req->ctx.pid) {
958 {
959 std::unique_lock lck(cInode->readLock);
960 *it->readStatus = READ_CANCELED;
961 }
962 it->cond->notify_one();
963 }
964 }
965 }
966 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
967 wSesLock.lock();
968 for (auto &it : cInode->readCacheMap) {
969 std::unique_lock<std::mutex> lock(it.second->mutex);
970 if (!(it.second->flags & PG_UPTODATE)) {
971 it.second->cond.notify_all();
972 }
973 }
974 wSesLock.unlock();
975
976 fuse_reply_ioctl(req, 0, NULL, 0);
977 }
978
ResetRead(fuse_req_t req, fuse_ino_t ino)979 static void ResetRead(fuse_req_t req, fuse_ino_t ino)
980 {
981 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
982 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
983 if (!cInode) {
984 fuse_reply_err(req, ENOMEM);
985 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
986 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
987 return;
988 }
989 LOGI("Reset read for pid: %{public}d", req->ctx.pid);
990 {
991 std::unique_lock<std::mutex> lock(cInode->readArgsLock);
992 cInode->readCtlMap[req->ctx.pid] = false;
993 }
994 fuse_reply_ioctl(req, 0, NULL, 0);
995 }
996
CloudIoctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *inBuf, size_t inBufsz, size_t outBufsz)997 static void CloudIoctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi,
998 unsigned flags, const void *inBuf, size_t inBufsz, size_t outBufsz)
999 {
1000 switch (static_cast<unsigned int>(cmd)) {
1001 case HMDFS_IOC_HAS_CACHE:
1002 HasCache(req, ino, inBuf);
1003 break;
1004 case HMDFS_IOC_CANCEL_READ:
1005 CancelRead(req, ino);
1006 break;
1007 case HMDFS_IOC_RESET_READ:
1008 ResetRead(req, ino);
1009 break;
1010 default:
1011 fuse_reply_err(req, ENOTTY);
1012 }
1013 }
1014
CheckAndWait(pid_t pid, shared_ptr<CloudInode> cInode, off_t off)1015 static bool CheckAndWait(pid_t pid, shared_ptr<CloudInode> cInode, off_t off)
1016 {
1017 int64_t cacheIndex = off / MAX_READ_SIZE;
1018 if (cInode->IsReadAhead(cacheIndex)) {
1019 auto &it = cInode->readCacheMap[cacheIndex];
1020 std::unique_lock<std::mutex> lock(it->mutex);
1021 auto waitStatus = it->cond.wait_for(
1022 lock, READ_TIMEOUT_S, [&] { return (it->flags & PG_UPTODATE) || CheckReadIsCanceled(pid, cInode); });
1023 if (!waitStatus) {
1024 LOGE("CheckAndWait timeout: %{public}ld", static_cast<long>(cacheIndex));
1025 return false;
1026 }
1027 }
1028 /* true when read finish and not canceled */
1029 return !CheckReadIsCanceled(pid, cInode);
1030 }
1031
SaveCacheToFile(shared_ptr<ReadArguments> readArgs, shared_ptr<CloudInode> cInode, int64_t cacheIndex, int32_t userId)1032 static void SaveCacheToFile(shared_ptr<ReadArguments> readArgs,
1033 shared_ptr<CloudInode> cInode,
1034 int64_t cacheIndex,
1035 int32_t userId)
1036 {
1037 string cachePath =
1038 LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE + CLOUD_CACHE_DIR + cInode->path;
1039 char *realPaths = realpath(cachePath.c_str(), nullptr);
1040 if (realPaths == nullptr) {
1041 LOGE("realpath failed");
1042 return;
1043 }
1044 int fd = open(realPaths, O_RDWR);
1045 free(realPaths);
1046 if (fd < 0) {
1047 LOGE("Failed to open cache file, err: %{public}d", errno);
1048 return;
1049 }
1050 if (cInode->cacheFileIndex.get()[cacheIndex] == NOT_CACHE &&
1051 pwrite(fd, readArgs->buf.get(), *readArgs->readResult, readArgs->offset) == *readArgs->readResult) {
1052 LOGI("Write to cache file, offset: %{public}ld*4M ", static_cast<long>(cacheIndex));
1053 cInode->cacheFileIndex.get()[cacheIndex] = HAS_CACHED;
1054 }
1055 close(fd);
1056 }
1057
CloudReadOnCloudFile(pid_t pid, int32_t userId, shared_ptr<ReadArguments> readArgs, shared_ptr<CloudInode> cInode, shared_ptr<CloudFile::CloudAssetReadSession> readSession)1058 static void CloudReadOnCloudFile(pid_t pid,
1059 int32_t userId,
1060 shared_ptr<ReadArguments> readArgs,
1061 shared_ptr<CloudInode> cInode,
1062 shared_ptr<CloudFile::CloudAssetReadSession> readSession)
1063 {
1064 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1065 LOGI("PRead CloudFile, path: %{public}s, size: %{public}zd, off: %{public}lu", GetAnonyString(cInode->path).c_str(),
1066 readArgs->size, static_cast<unsigned long>(readArgs->offset));
1067
1068 uint64_t startTime = UTCTimeMilliSeconds();
1069 bool isReading = true;
1070 int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1071 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1072
1073 wSesLock.lock();
1074 if (readArgs->offset % MAX_READ_SIZE == 0 && cInode->readCacheMap.find(cacheIndex) == cInode->readCacheMap.end()) {
1075 auto memInfo = std::make_shared<ReadCacheInfo>();
1076 memInfo->flags = PG_READAHEAD;
1077 cInode->readCacheMap[cacheIndex] = memInfo;
1078 isReading = false;
1079 LOGI("To do read cloudfile, offset: %{public}ld*4M", static_cast<long>(cacheIndex));
1080 }
1081 wSesLock.unlock();
1082 if (isReading && !CheckAndWait(pid, cInode, readArgs->offset)) {
1083 return;
1084 }
1085
1086 *readArgs->readResult =
1087 readSession->PRead(readArgs->offset, readArgs->size, readArgs->buf.get(), *readArgs->ckError);
1088
1089 uint64_t endTime = UTCTimeMilliSeconds();
1090 uint64_t readTime = (endTime > startTime) ? (endTime - startTime) : 0;
1091 CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
1092 readStat.UpdateReadSizeStat(readArgs->size);
1093 readStat.UpdateReadTimeStat(readArgs->size, readTime);
1094
1095 {
1096 unique_lock lck(cInode->readLock);
1097 *readArgs->readStatus = READ_FINISHED;
1098 }
1099 readArgs->cond->notify_one();
1100 if (readArgs->offset % MAX_READ_SIZE == 0) {
1101 cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1102 wSesLock.lock();
1103 if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1104 cInode->readCacheMap[cacheIndex]->cond.notify_all();
1105 LOGI("Read cloudfile done and notify all waiting threads, offset: %{public}ld*4M",
1106 static_cast<long>(cacheIndex));
1107 }
1108 if (IsVideoType(cInode->mBase->name) && *readArgs->readResult > 0) {
1109 ffrt::submit(
1110 [userId, readArgs, cInode, cacheIndex] { SaveCacheToFile(readArgs, cInode, cacheIndex, userId); });
1111 }
1112 wSesLock.unlock();
1113 }
1114 return;
1115 }
1116
CloudReadOnCacheFile(shared_ptr<ReadArguments> readArgs, shared_ptr<CloudInode> cInode, shared_ptr<CloudFile::CloudAssetReadSession> readSession, int32_t userId)1117 static void CloudReadOnCacheFile(shared_ptr<ReadArguments> readArgs,
1118 shared_ptr<CloudInode> cInode,
1119 shared_ptr<CloudFile::CloudAssetReadSession> readSession,
1120 int32_t userId)
1121 {
1122 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1123 if (static_cast<uint64_t>(readArgs->offset) > cInode->mBase->size) {
1124 return;
1125 }
1126 usleep(READ_CACHE_SLEEP);
1127 uint64_t startTime = UTCTimeMilliSeconds();
1128 int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1129 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1130
1131 wSesLock.lock();
1132 if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1133 wSesLock.unlock();
1134 return;
1135 }
1136 auto memInfo = std::make_shared<ReadCacheInfo>();
1137 memInfo->flags = PG_READAHEAD;
1138 cInode->readCacheMap[cacheIndex] = memInfo;
1139 wSesLock.unlock();
1140
1141 readArgs->size = MAX_READ_SIZE;
1142 readArgs->buf.reset(new char[MAX_READ_SIZE], [](char *ptr) { delete[] ptr; });
1143 LOGI("To do preread cloudfile path: %{public}s, size: %{public}zd, offset: %{public}ld*4M",
1144 GetAnonyString(cInode->path).c_str(), readArgs->size, static_cast<long>(cacheIndex));
1145 *readArgs->readResult =
1146 readSession->PRead(readArgs->offset, readArgs->size, readArgs->buf.get(), *readArgs->ckError);
1147
1148 uint64_t endTime = UTCTimeMilliSeconds();
1149 uint64_t readTime = (endTime > startTime) ? (endTime - startTime) : 0;
1150 CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
1151 readStat.UpdateReadSizeStat(readArgs->size);
1152 readStat.UpdateReadTimeStat(readArgs->size, readTime);
1153
1154 cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1155 wSesLock.lock();
1156 if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1157 cInode->readCacheMap[cacheIndex]->cond.notify_all();
1158 LOGI("Preread cloudfile done and notify all waiting threads, offset: %{public}ld*4M",
1159 static_cast<long>(cacheIndex));
1160 }
1161 if (IsVideoType(cInode->mBase->name) && *readArgs->readResult > 0) {
1162 ffrt::submit([readArgs, cInode, cacheIndex, userId] { SaveCacheToFile(readArgs, cInode, cacheIndex, userId); });
1163 }
1164 wSesLock.unlock();
1165 return;
1166 }
1167
CloudReadOnLocalFile(fuse_req_t req, shared_ptr<char> buf, size_t size, off_t off, struct fuse_file_info *fi)1168 static void CloudReadOnLocalFile(fuse_req_t req, shared_ptr<char> buf, size_t size,
1169 off_t off, struct fuse_file_info *fi)
1170 {
1171 auto fdsan = reinterpret_cast<fdsan_fd *>(fi->fh);
1172 auto readSize = pread(fdsan->get(), buf.get(), size, off);
1173 if (readSize < 0) {
1174 fuse_reply_err(req, errno);
1175 string msg = "Failed to read local file, errno: " + to_string(errno);
1176 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1177 FaultType::FILE, errno, msg});
1178 return;
1179 }
1180 fuse_reply_buf(req, buf.get(), min(size, static_cast<size_t>(readSize)));
1181 return;
1182 }
1183
InitReadSlice(size_t size, off_t off, ReadSlice &readSlice)1184 static void InitReadSlice(size_t size, off_t off, ReadSlice &readSlice)
1185 {
1186 readSlice.offHead = off;
1187 readSlice.offTail = (off + static_cast<off_t>(size)) / MAX_READ_SIZE * MAX_READ_SIZE;
1188 if (readSlice.offTail > readSlice.offHead) {
1189 readSlice.sizeHead = static_cast<size_t>(readSlice.offTail - readSlice.offHead);
1190 readSlice.sizeTail = MAX_READ_SIZE;
1191 } else {
1192 readSlice.sizeHead = static_cast<size_t>(readSlice.offTail + MAX_READ_SIZE - readSlice.offHead);
1193 }
1194 if ((static_cast<off_t>(size) + off) % MAX_READ_SIZE == 0) {
1195 readSlice.offTail -= MAX_READ_SIZE;
1196 readSlice.sizeTail = 0;
1197 }
1198 }
1199
FixData(fuse_req_t req, shared_ptr<char> buf, size_t size, size_t sizeDone, shared_ptr<ReadArguments> readArgs)1200 static bool FixData(fuse_req_t req, shared_ptr<char> buf, size_t size, size_t sizeDone,
1201 shared_ptr<ReadArguments> readArgs)
1202 {
1203 if (*readArgs->readResult < 0) {
1204 fuse_reply_err(req, ENOMEM);
1205 string msg = "Pread failed, readResult: " + to_string(*readArgs->readResult);
1206 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1207 FaultType::DRIVERKIT, ENOMEM, msg});
1208 return false;
1209 }
1210
1211 auto ret = HandleCloudError(*readArgs->ckError, FaultOperation::READ);
1212 if (ret < 0) {
1213 fuse_reply_err(req, -ret);
1214 return false;
1215 }
1216
1217 int32_t copyRet = memcpy_s(buf.get() + sizeDone, min(size - sizeDone, static_cast<size_t>(*readArgs->readResult)),
1218 readArgs->buf.get(), min(size - sizeDone, static_cast<size_t>(*readArgs->readResult)));
1219 if (copyRet != 0) {
1220 fuse_reply_err(req, ENETUNREACH);
1221 string msg = "Parcel data copy failed, err=" + to_string(copyRet);
1222 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1223 FaultType::FILE, copyRet, msg});
1224 return false;
1225 }
1226 return true;
1227 }
1228
WaitData(fuse_req_t req, shared_ptr<CloudInode> cInode, shared_ptr<ReadArguments> readArgs)1229 static bool WaitData(fuse_req_t req, shared_ptr<CloudInode> cInode, shared_ptr<ReadArguments> readArgs)
1230 {
1231 std::unique_lock lock(cInode->readLock);
1232 auto waitStatus = readArgs->cond->wait_for(lock, READ_TIMEOUT_S, [readArgs] { return *readArgs->readStatus; });
1233 if (*readArgs->readStatus == READ_CANCELED) {
1234 LOGI("read is cancelled");
1235 fuse_reply_err(req, EIO);
1236 return false;
1237 }
1238 if (!waitStatus) {
1239 std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1240 int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1241 cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1242 wSesLock.lock();
1243 if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1244 cInode->readCacheMap[cacheIndex]->cond.notify_all();
1245 }
1246 wSesLock.unlock();
1247 LOGE("Pread timeout, offset: %{public}ld*4M", static_cast<long>(cacheIndex));
1248 fuse_reply_err(req, ENETUNREACH);
1249 return false;
1250 }
1251 return true;
1252 }
1253
WaitAndFixData(fuse_req_t req, shared_ptr<char> buf, size_t size, shared_ptr<CloudInode> cInode, vector<shared_ptr<ReadArguments>> readArgsList)1254 static void WaitAndFixData(fuse_req_t req,
1255 shared_ptr<char> buf,
1256 size_t size,
1257 shared_ptr<CloudInode> cInode,
1258 vector<shared_ptr<ReadArguments>> readArgsList)
1259 {
1260 size_t sizeDone = 0;
1261 for (auto &it : readArgsList) {
1262 if (!it) {
1263 continue;
1264 }
1265 if (!WaitData(req, cInode, it)) {
1266 return;
1267 }
1268 if (!FixData(req, buf, size, sizeDone, it)) {
1269 return;
1270 }
1271 sizeDone += it->size;
1272 }
1273 fuse_reply_buf(req, buf.get(), min(size, sizeDone));
1274 }
1275
ReadCacheFile(shared_ptr<ReadArguments> readArgs, const string &path, int32_t userId)1276 static ssize_t ReadCacheFile(shared_ptr<ReadArguments> readArgs, const string &path, int32_t userId)
1277 {
1278 string cachePath =
1279 LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE + CLOUD_CACHE_DIR + path;
1280 char *realPaths = realpath(cachePath.c_str(), nullptr);
1281 if (realPaths == nullptr) {
1282 LOGE("realpath failed");
1283 return -1;
1284 }
1285 int fd = open(realPaths, O_RDONLY);
1286 free(realPaths);
1287 if (fd < 0) {
1288 return fd;
1289 }
1290 ssize_t bytesRead = pread(fd, readArgs->buf.get(), readArgs->size, readArgs->offset);
1291 close(fd);
1292 return bytesRead;
1293 }
1294
DoReadSlice(fuse_req_t req, shared_ptr<CloudInode> cInode, shared_ptr<CloudFile::CloudAssetReadSession> readSession, shared_ptr<ReadArguments> readArgs, bool needCheck)1295 static bool DoReadSlice(fuse_req_t req,
1296 shared_ptr<CloudInode> cInode,
1297 shared_ptr<CloudFile::CloudAssetReadSession> readSession,
1298 shared_ptr<ReadArguments> readArgs,
1299 bool needCheck)
1300 {
1301 if (!readArgs) {
1302 return true;
1303 }
1304 if (!readArgs->buf) {
1305 fuse_reply_err(req, ENOMEM);
1306 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1307 FaultType::FILE, ENOMEM, "buffer is null"});
1308 return false;
1309 }
1310 int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1311 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1312 if (IsVideoType(cInode->mBase->name) && cInode->cacheFileIndex.get()[cacheIndex] == HAS_CACHED) {
1313 LOGI("DoReadSlice from local: %{public}ld", static_cast<long>(cacheIndex));
1314 *readArgs->readResult = ReadCacheFile(readArgs, cInode->path, data->userId);
1315 if (*readArgs->readResult >= 0) {
1316 unique_lock lock(cInode->readLock);
1317 *readArgs->readStatus = READ_FINISHED;
1318 return true;
1319 } else {
1320 LOGI("read cache file failed, errno: %{public}d", errno);
1321 }
1322 }
1323
1324 LOGI("DoReadSlice from cloud: %{public}ld", static_cast<long>(cacheIndex));
1325 if (needCheck && !CheckAndWait(req->ctx.pid, cInode, readArgs->offset)) {
1326 fuse_reply_err(req, ENETUNREACH);
1327 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1328 FaultType::CLOUD_READ_FILE_TIMEOUT, ENETUNREACH, "network timeout"});
1329 return false;
1330 }
1331 ffrt::submit([req, readArgs, cInode, readSession, data] {
1332 CloudReadOnCloudFile(req->ctx.pid, data->userId, readArgs, cInode, readSession);
1333 });
1334 return true;
1335 }
1336
DoCloudRead(fuse_req_t req, int flags, DoCloudReadParams params)1337 static bool DoCloudRead(fuse_req_t req, int flags, DoCloudReadParams params)
1338 {
1339 if (!DoReadSlice(req, params.cInode, params.readSession, params.readArgsHead, true)) {
1340 return false;
1341 }
1342 if (!DoReadSlice(req, params.cInode, params.readSession, params.readArgsTail, false)) {
1343 return false;
1344 }
1345 // no prefetch when contains O_NOFOLLOW
1346 unsigned int unflags = static_cast<unsigned int>(flags);
1347 if (unflags & O_NOFOLLOW) {
1348 return true;
1349 }
1350
1351 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1352 for (uint32_t i = 1; i <= CACHE_PAGE_NUM; i++) {
1353 int64_t cacheIndex = static_cast<int64_t>(
1354 (params.readArgsTail ? params.readArgsTail->offset : params.readArgsHead->offset) / MAX_READ_SIZE + i);
1355 if (IsVideoType(params.cInode->mBase->name) && params.cInode->cacheFileIndex.get()[cacheIndex] == HAS_CACHED) {
1356 continue;
1357 }
1358 auto readArgsCache = make_shared<ReadArguments>(0, cacheIndex * MAX_READ_SIZE, req->ctx.pid);
1359 if (!readArgsCache) {
1360 LOGE("Init readArgsCache failed");
1361 break;
1362 }
1363 ffrt::submit([readArgsCache, params, data] {
1364 CloudReadOnCacheFile(readArgsCache, params.cInode, params.readSession, data->userId);
1365 });
1366 }
1367 return true;
1368 }
1369
CloudReadHelper(fuse_req_t req, size_t size, shared_ptr<CloudInode> cInode)1370 static bool CloudReadHelper(fuse_req_t req, size_t size, shared_ptr<CloudInode> cInode)
1371 {
1372 if (CheckReadIsCanceled(req->ctx.pid, cInode)) {
1373 LOGI("read is cancelled");
1374 fuse_reply_err(req, EIO);
1375 return false;
1376 }
1377 if (size > MAX_READ_SIZE) {
1378 fuse_reply_err(req, EINVAL);
1379 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1380 FaultType::FILE, EINVAL, "input size exceeds MAX_READ_SIZE"});
1381 return false;
1382 }
1383 return true;
1384 }
1385
CloudRead(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)1386 static void CloudRead(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1387 struct fuse_file_info *fi)
1388 {
1389 HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1390 shared_ptr<char> buf = nullptr;
1391 struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1392 shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
1393 if (!cInode) {
1394 fuse_reply_err(req, ENOMEM);
1395 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1396 FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
1397 return;
1398 }
1399 LOGI("CloudRead: %{public}s, size=%{public}zd, off=%{public}lu",
1400 GetAnonyString(CloudPath(data, ino)).c_str(), size, (unsigned long)off);
1401
1402 if (!CloudReadHelper(req, size, cInode)) {
1403 return;
1404 }
1405 auto dkReadSession = cInode->readSession;
1406 if (!dkReadSession) {
1407 fuse_reply_err(req, EPERM);
1408 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1409 FaultType::FILE, EPERM, "readSession is nullptr"});
1410 return;
1411 }
1412 buf.reset(new char[size], [](char *ptr) { delete[] ptr; });
1413 if (!buf) {
1414 fuse_reply_err(req, ENOMEM);
1415 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1416 FaultType::FILE, ENOMEM, "buffer is null"});
1417 return;
1418 }
1419 if (cInode->mBase->hasDownloaded && fi->fh != UINT64_MAX) {
1420 CloudReadOnLocalFile(req, buf, size, off, fi);
1421 return;
1422 }
1423
1424 ReadSlice readSlice;
1425 /* slice read request into 4M pages */
1426 InitReadSlice(size, off, readSlice);
1427 /* init readArgs for current page, and next page if cross pages */
1428 auto readArgsHead = make_shared<ReadArguments>(readSlice.sizeHead, readSlice.offHead, req->ctx.pid);
1429 shared_ptr<ReadArguments> readArgsTail = nullptr;
1430 if (readSlice.sizeTail > 0) {
1431 readArgsTail = make_shared<ReadArguments>(readSlice.sizeTail, readSlice.offTail, req->ctx.pid);
1432 }
1433 if (!DoCloudRead(req, fi->flags, {cInode, dkReadSession, readArgsHead, readArgsTail})) {
1434 return;
1435 }
1436 /* wait and reply current req data */
1437 InsertReadArgs(cInode, {readArgsHead, readArgsTail});
1438 WaitAndFixData(req, buf, size, cInode, {readArgsHead, readArgsTail});
1439 EraseReadArgs(cInode, {readArgsHead, readArgsTail});
1440 }
1441
1442 static const struct fuse_lowlevel_ops cloudDiskFuseOps = {
1443 .lookup = CloudDisk::FuseOperations::Lookup,
1444 .forget = CloudDisk::FuseOperations::Forget,
1445 .getattr = CloudDisk::FuseOperations::GetAttr,
1446 .setattr = CloudDisk::FuseOperations::SetAttr,
1447 .mknod = CloudDisk::FuseOperations::MkNod,
1448 .mkdir = CloudDisk::FuseOperations::MkDir,
1449 .unlink = CloudDisk::FuseOperations::Unlink,
1450 .rmdir = CloudDisk::FuseOperations::RmDir,
1451 .rename = CloudDisk::FuseOperations::Rename,
1452 .open = CloudDisk::FuseOperations::Open,
1453 .read = CloudDisk::FuseOperations::Read,
1454 .release = CloudDisk::FuseOperations::Release,
1455 .readdir = CloudDisk::FuseOperations::ReadDir,
1456 .setxattr = CloudDisk::FuseOperations::SetXattr,
1457 .getxattr = CloudDisk::FuseOperations::GetXattr,
1458 .access = CloudDisk::FuseOperations::Access,
1459 .create = CloudDisk::FuseOperations::Create,
1460 .write_buf = CloudDisk::FuseOperations::WriteBuf,
1461 .forget_multi = CloudDisk::FuseOperations::ForgetMulti,
1462 .lseek = CloudDisk::FuseOperations::Lseek,
1463 };
1464
1465 static const struct fuse_lowlevel_ops cloudMediaFuseOps = {
1466 .lookup = CloudLookup,
1467 .forget = CloudForget,
1468 .getattr = CloudGetAttr,
1469 .open = CloudOpen,
1470 .read = CloudRead,
1471 .release = CloudRelease,
1472 .readdir = CloudReadDir,
1473 .ioctl = CloudIoctl,
1474 .forget_multi = CloudForgetMulti,
1475 };
1476
CheckPathForStartFuse(const string &path)1477 static bool CheckPathForStartFuse(const string &path)
1478 {
1479 char resolvedPath[PATH_MAX] = {'\0'};
1480 char* ret = realpath(path.c_str(), resolvedPath);
1481 if (ret == nullptr) {
1482 return false;
1483 }
1484 std::string realPath(resolvedPath);
1485 std::string pathPrefix = "/mnt/data/";
1486 if (realPath.rfind(pathPrefix, 0) != 0) {
1487 return false;
1488 }
1489
1490 size_t userIdBeginPos = pathPrefix.length();
1491 size_t userIdEndPos = realPath.find("/", userIdBeginPos);
1492 const std::string userId = realPath.substr(userIdBeginPos, userIdEndPos - userIdBeginPos);
1493 if (userId.find_first_not_of("0123456789") != std::string::npos) {
1494 return false;
1495 }
1496
1497 size_t suffixBeginPos = userIdEndPos + 1;
1498 const std::string pathSuffix1 = "cloud";
1499 const std::string pathSuffix2 = "cloud_fuse";
1500 if (realPath.rfind(pathSuffix1) == suffixBeginPos &&
1501 suffixBeginPos + pathSuffix1.length() == realPath.length()) {
1502 return true;
1503 }
1504 if (realPath.rfind(pathSuffix2) == suffixBeginPos &&
1505 suffixBeginPos + pathSuffix2.length() == realPath.length()) {
1506 return true;
1507 }
1508 return false;
1509 }
1510
StartFuse(int32_t userId, int32_t devFd, const string &path)1511 int32_t FuseManager::StartFuse(int32_t userId, int32_t devFd, const string &path)
1512 {
1513 LOGI("FuseManager::StartFuse entry");
1514 struct fuse_loop_config config;
1515 struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
1516 struct CloudDisk::CloudDiskFuseData cloudDiskData;
1517 struct FuseData data;
1518 struct fuse_session *se = nullptr;
1519 int ret;
1520
1521 if (fuse_opt_add_arg(&args, path.c_str()) || !CheckPathForStartFuse(path)) {
1522 LOGE("Mount path invalid");
1523 return -EINVAL;
1524 }
1525
1526 if (path.find("cloud_fuse") != string::npos) {
1527 se = fuse_session_new(&args, &cloudDiskFuseOps,
1528 sizeof(cloudDiskFuseOps), &cloudDiskData);
1529 if (se == nullptr) {
1530 LOGE("cloud disk fuse_session_new error");
1531 return -EINVAL;
1532 }
1533 cloudDiskData.userId = userId;
1534 cloudDiskData.se = se;
1535 config.max_idle_threads = 1;
1536 } else {
1537 se = fuse_session_new(&args, &cloudMediaFuseOps, sizeof(cloudMediaFuseOps), &data);
1538 if (se == nullptr) {
1539 LOGE("cloud media fuse_session_new error");
1540 return -EINVAL;
1541 }
1542 data.userId = userId;
1543 data.se = se;
1544 config.max_idle_threads = MAX_IDLE_THREADS;
1545 }
1546
1547 LOGI("fuse_session_new success, userId: %{public}d", userId);
1548 se->fd = devFd;
1549 se->mountpoint = strdup(path.c_str());
1550 if (se->mountpoint == nullptr) {
1551 return -ENOMEM;
1552 }
1553
1554 fuse_daemonize(true);
1555 ret = fuse_session_loop_mt(se, &config);
1556
1557 fuse_session_unmount(se);
1558 LOGI("fuse_session_unmount");
1559 if (se->mountpoint) {
1560 free(se->mountpoint);
1561 se->mountpoint = nullptr;
1562 }
1563
1564 fuse_session_destroy(se);
1565 return ret;
1566 }
1567
GetInstance()1568 FuseManager &FuseManager::GetInstance()
1569 {
1570 static FuseManager instance_;
1571 return instance_;
1572 }
1573
1574 } // namespace CloudFile
1575 } // namespace FileManagement
1576 } // namespace OHOS
1577