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