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