• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef HICOLLIE_ENABLE
449         XcollieInput xcollieInput{child.get(), FaultOperation::LOOKUP};
450         auto xcollieId = XCollieHelper::SetTimer("CloudFileDaemon_CloudLookup", LOOKUP_TIMEOUT_S,
451             XcollieCallback, &xcollieInput, false);
452 #endif
453         wLock.lock();
454         data->inodeCache[cloudId] = child;
455         wLock.unlock();
456 #ifdef HICOLLIE_ENABLE
457         XCollieHelper::CancelTimer(xcollieId);
458 #endif
459     } else if (*(child->mBase) != mBase) {
460         LOGW("invalidate %s", childName.c_str());
461         child->mBase = make_shared<MetaBase>(mBase);
462     }
463     if (child->path != childName) {
464         LOGD("hash collision");
465     }
466     child->path = childName;
467     child->parent = parent;
468     LOGD("lookup success, child: %{private}s, refCount: %lld", GetAnonyString(child->path).c_str(),
469          static_cast<long long>(child->refCount));
470     GetMetaAttr(data, child, &e->attr);
471     e->ino = static_cast<fuse_ino_t>(cloudId);
472     return 0;
473 }
474 
CloudDoLookup(fuse_req_t req,fuse_ino_t parent,const char * name,struct fuse_entry_param * e)475 static int CloudDoLookup(fuse_req_t req, fuse_ino_t parent, const char *name,
476                          struct fuse_entry_param *e)
477 {
478     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
479     string parentName = CloudPath(data, parent);
480     if (parentName == "") {
481         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::LOOKUP,
482             FaultType::FILE, ENOENT, "parent name is empty"});
483         return ENOENT;
484     }
485 
486     return CloudDoLookupHelper(parent, name, e, data, parentName);
487 }
488 
CloudLookup(fuse_req_t req,fuse_ino_t parent,const char * name)489 static void CloudLookup(fuse_req_t req, fuse_ino_t parent,
490                         const char *name)
491 {
492     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
493     struct fuse_entry_param e;
494     int err;
495 
496     err = CloudDoLookup(req, parent, name, &e);
497     if (err) {
498         fuse_reply_err(req, err);
499     } else {
500         fuse_reply_entry(req, &e);
501     }
502 }
503 
PutNode(struct FuseData * data,shared_ptr<CloudInode> node,uint64_t num)504 static void PutNode(struct FuseData *data, shared_ptr<CloudInode> node, uint64_t num)
505 {
506     std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
507     node->refCount -= num;
508     LOGD("%s, put num: %lld,  current refCount: %d",
509          GetAnonyString(node->path).c_str(), (long long)num,  node->refCount.load());
510     if (node->refCount == 0) {
511         LOGD("node released: %s", GetAnonyString(node->path).c_str());
512 #ifdef HICOLLIE_ENABLE
513         XcollieInput xcollieInput{node.get(), FaultOperation::FORGET};
514         auto xcollieId = XCollieHelper::SetTimer("CloudFileDaemon_CloudForget", FORGET_TIMEOUT_S,
515             XcollieCallback, &xcollieInput, false);
516 #endif
517         if (node->mBase != nullptr && (node->mBase->mode & S_IFDIR)) {
518             LOGW("PutNode directory inode, path is %{public}s", GetAnonyString(node->path).c_str());
519         }
520         string inoKey = node->mBase->cloudId + node->mBase->name;
521         uint64_t cloudId = static_cast<uint64_t>(CloudDisk::CloudFileUtils::DentryHash(inoKey));
522         wLock.lock();
523         data->inodeCache.erase(cloudId);
524         wLock.unlock();
525 #ifdef HICOLLIE_ENABLE
526         XCollieHelper::CancelTimer(xcollieId);
527 #endif
528     }
529 }
530 
CloudForget(fuse_req_t req,fuse_ino_t ino,uint64_t nlookup)531 static void CloudForget(fuse_req_t req, fuse_ino_t ino,
532                         uint64_t nlookup)
533 {
534     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
535     shared_ptr<CloudInode> node = GetCloudInode(data, ino);
536     if (node) {
537         LOGD("forget %s, nlookup: %lld", GetAnonyString(node->path).c_str(), (long long)nlookup);
538         PutNode(data, node, nlookup);
539     }
540     fuse_reply_none(req);
541 }
542 
CloudGetAttr(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)543 static void CloudGetAttr(fuse_req_t req, fuse_ino_t ino,
544                          struct fuse_file_info *fi)
545 {
546     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
547     struct stat buf;
548     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
549     (void) fi;
550 
551     LOGD("getattr, %s", GetAnonyString(CloudPath(data, ino)).c_str());
552     shared_ptr<CloudInode> node = GetCloudInode(data, ino);
553     if (!node || !node->mBase) {
554         fuse_reply_err(req, ENOMEM);
555         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::GETATTR,
556             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
557         return;
558     }
559     GetMetaAttr(data, node, &buf);
560 
561     fuse_reply_attr(req, &buf, 0);
562 }
563 
GetAssetKey(int fileType)564 static string GetAssetKey(int fileType)
565 {
566     switch (fileType) {
567         case FILE_TYPE_CONTENT:
568             return "content";
569         case FILE_TYPE_THUMBNAIL:
570             return "thumbnail";
571         case FILE_TYPE_LCD:
572             return "lcd";
573         default:
574             LOGE("bad fileType %{public}d", fileType);
575             return "";
576     }
577 }
578 
GetCloudMergeViewPath(int32_t userId,const string & relativePath)579 static string GetCloudMergeViewPath(int32_t userId, const string &relativePath)
580 {
581     return HMDFS_PATH_PREFIX + to_string(userId) + CLOUD_MERGE_VIEW_PATH_SUFFIX + relativePath;
582 }
583 
GetAssetPath(shared_ptr<CloudInode> cInode,struct FuseData * data)584 static string GetAssetPath(shared_ptr<CloudInode> cInode, struct FuseData *data)
585 {
586     string path;
587     filesystem::path parentPath;
588     path = GetLocalTmpPath(data->userId, cInode->path);
589     parentPath = filesystem::path(path).parent_path();
590     ForceCreateDirectory(parentPath.string());
591     LOGD("fileType: %d, create dir: %s, relative path: %s",
592          cInode->mBase->fileType, GetAnonyString(parentPath.string()).c_str(), GetAnonyString(cInode->path).c_str());
593     return path;
594 }
595 
fuse_inval(fuse_session * se,fuse_ino_t parentIno,fuse_ino_t childIno,const string & childName)596 static void fuse_inval(fuse_session *se, fuse_ino_t parentIno, fuse_ino_t childIno, const string &childName)
597 {
598     auto task = [se, parentIno, childIno, childName] {
599         if (fuse_lowlevel_notify_inval_entry(se, parentIno, childName.c_str(), childName.size())) {
600             fuse_lowlevel_notify_inval_inode(se, childIno, 0, 0);
601         }
602     };
603     ffrt::submit(task, {}, {}, ffrt::task_attr().qos(ffrt_qos_background));
604 }
605 
CloudOpenOnLocal(struct FuseData * data,shared_ptr<CloudInode> cInode,struct fuse_file_info * fi)606 static int CloudOpenOnLocal(struct FuseData *data, shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)
607 {
608     string localPath = GetLocalPath(data->userId, cInode->path);
609     string tmpPath = GetLocalTmpPath(data->userId, cInode->path);
610     char resolvedPath[PATH_MAX + 1] = {'\0'};
611     char *realPath = realpath(tmpPath.c_str(), resolvedPath);
612     if (realPath == nullptr) {
613         LOGE("Failed to realpath, errno: %{public}d", errno);
614         return 0;
615     }
616     unsigned int flags = static_cast<unsigned int>(fi->flags);
617     if (flags & O_DIRECT) {
618         flags &= ~O_DIRECT;
619     }
620     auto fd = open(realPath, flags);
621     if (fd < 0) {
622         LOGE("Failed to open local file, errno: %{public}d", errno);
623         return 0;
624     }
625     string cloudMergeViewPath = GetCloudMergeViewPath(data->userId, cInode->path);
626     if (remove(cloudMergeViewPath.c_str()) < 0) {
627         LOGE("Failed to update kernel dentry cache, errno: %{public}d", errno);
628         close(fd);
629         return 0;
630     }
631 
632     filesystem::path parentPath = filesystem::path(localPath).parent_path();
633     ForceCreateDirectory(parentPath.string());
634     if (rename(tmpPath.c_str(), localPath.c_str()) < 0) {
635         LOGE("Failed to rename tmpPath to localPath, errno: %{public}d", errno);
636         close(fd);
637         return -errno;
638     }
639     auto parentInode = GetCloudInode(data, cInode->parent);
640     if (parentInode == nullptr) {
641         LOGE("fail to find parent inode");
642         close(fd);
643         return -ENOMEM;
644     }
645     MetaFile(data->userId, parentInode->path).DoRemove(*(cInode->mBase));
646     cInode->mBase->hasDownloaded = true;
647     fi->fh = static_cast<uint64_t>(fd);
648     return 0;
649 }
650 
HandleOpenResult(CloudFile::CloudError ckError,struct FuseData * data,shared_ptr<CloudInode> cInode,struct fuse_file_info * fi)651 static int HandleOpenResult(CloudFile::CloudError ckError, struct FuseData *data,
652     shared_ptr<CloudInode> cInode, struct fuse_file_info *fi)
653 {
654     auto ret = HandleCloudError(ckError, FaultOperation::OPEN);
655     if (ret < 0) {
656         return ret;
657     }
658     if (cInode->mBase->fileType != FILE_TYPE_CONTENT) {
659         ret = CloudOpenOnLocal(data, cInode, fi);
660         if (ret < 0) {
661             CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
662                 FaultType::INODE_FILE, ret, "inner error"});
663             return ret;
664         }
665     }
666     cInode->sessionRefCount++;
667     LOGI("open success, sessionRefCount: %{public}d", cInode->sessionRefCount.load());
668     return 0;
669 }
670 
UTCTimeMilliSeconds()671 static uint64_t UTCTimeMilliSeconds()
672 {
673     struct timespec t;
674     clock_gettime(CLOCK_REALTIME, &t);
675     return t.tv_sec * CloudDisk::SECOND_TO_MILLISECOND + t.tv_nsec / CloudDisk::MILLISECOND_TO_NANOSECOND;
676 }
677 
DownloadThmOrLcd(shared_ptr<CloudInode> cInode,shared_ptr<CloudError> err,shared_ptr<bool> openFinish,shared_ptr<ffrt::condition_variable> cond)678 static void DownloadThmOrLcd(shared_ptr<CloudInode> cInode, shared_ptr<CloudError> err, shared_ptr<bool> openFinish,
679     shared_ptr<ffrt::condition_variable> cond)
680 {
681     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
682     auto session = cInode->readSession;
683     if (!session) {
684         LOGE("readSession is nullptr");
685         return;
686     }
687     *err = session->InitSession();
688     {
689         unique_lock lck(cInode->openLock);
690         *openFinish = true;
691     }
692     cond->notify_one();
693     LOGI("download done, path: %{public}s", GetAnonyString(cInode->path).c_str());
694     return;
695 }
696 
IsVideoType(const string & name)697 static bool IsVideoType(const string &name)
698 {
699     return name.find(VIDEO_TYPE_PREFIX) == 0;
700 }
701 
LoadCacheFileIndex(shared_ptr<CloudInode> cInode,int32_t userId)702 static void LoadCacheFileIndex(shared_ptr<CloudInode> cInode, int32_t userId)
703 {
704     string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE +
705                        CLOUD_CACHE_DIR + cInode->path;
706     int filePageSize = static_cast<int32_t>(cInode->mBase->size / MAX_READ_SIZE + 1);
707     CLOUD_CACHE_STATUS *tmp = new CLOUD_CACHE_STATUS[filePageSize]();
708     std::unique_ptr<CLOUD_CACHE_STATUS[]> mp(tmp);
709     if (access(cachePath.c_str(), F_OK) != 0) {
710         string parentPath = filesystem::path(cachePath).parent_path().string();
711         if (!ForceCreateDirectory(parentPath)) {
712             LOGE("failed to create parent dir");
713             return;
714         }
715         int fd = open(cachePath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
716         if (fd < 0) {
717             LOGE("failed to open cache file, ret: %{public}d", errno);
718             return;
719         }
720         if (ftruncate(fd, cInode->mBase->size) == -1) {
721             LOGE("failed to truncate file, ret: %{public}d", errno);
722         }
723         close(fd);
724         cInode->cacheFileIndex = std::move(mp);
725         return;
726     }
727 
728     if (getxattr(cachePath.c_str(), CLOUD_CACHE_XATTR_NAME.c_str(), mp.get(),
729                  sizeof(CLOUD_CACHE_STATUS[filePageSize])) < 0 &&
730         errno != ENODATA) {
731         LOGE("getxattr fail, err: %{public}d", errno);
732         cInode->cacheFileIndex = std::move(mp);
733         return;
734     }
735     for (int i = 0; i < filePageSize; i++) {
736         if (mp.get()[i] == HAS_CACHED) {
737             auto memInfo = std::make_shared<ReadCacheInfo>();
738             memInfo->flags = PG_UPTODATE;
739             cInode->readCacheMap[i] = memInfo;
740         }
741     }
742     cInode->cacheFileIndex = std::move(mp);
743 }
744 
DoCloudOpen(shared_ptr<CloudInode> cInode,struct fuse_file_info * fi,struct FuseData * data)745 static int DoCloudOpen(shared_ptr<CloudInode> cInode, struct fuse_file_info *fi, struct FuseData *data)
746 {
747     auto error = make_shared<CloudError>();
748     auto openFinish = make_shared<bool>(false);
749     auto cond = make_shared<ffrt::condition_variable>();
750     ffrt::submit([cInode, error, openFinish, cond, data] {
751         if (IsVideoType(cInode->mBase->name)) {
752             LoadCacheFileIndex(cInode, data->userId);
753         }
754         DownloadThmOrLcd(cInode, error, openFinish, cond);
755     });
756     unique_lock lck(cInode->openLock);
757     auto waitStatus = cond->wait_for(lck, OPEN_TIMEOUT_S, [openFinish] {
758         return *openFinish;
759     });
760     if (!waitStatus) {
761         string msg = "init session timeout, path: " + GetAnonyString(cInode->path);
762         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
763                 FaultType::OPEN_CLOUD_FILE_TIMEOUT, ENETUNREACH, msg});
764         return -ENETUNREACH;
765     }
766     return HandleOpenResult(*error, data, cInode, fi);
767 }
768 
HandleReopen(fuse_req_t req,struct FuseData * data,shared_ptr<CloudInode> cInode,struct fuse_file_info * fi)769 static void HandleReopen(fuse_req_t req, struct FuseData *data, shared_ptr<CloudInode> cInode,
770     struct fuse_file_info *fi)
771 {
772     do {
773         if (!cInode->mBase->hasDownloaded) {
774             break;
775         }
776         string localPath = GetLocalPath(data->userId, cInode->path);
777         char realPath[PATH_MAX + 1]{'\0'};
778         if (realpath(localPath.c_str(), realPath) == nullptr) {
779             LOGE("realpath failed with %{public}d", errno);
780             break;
781         }
782         unsigned int flags = static_cast<unsigned int>(fi->flags);
783         if (flags & O_DIRECT) {
784             flags &= ~O_DIRECT;
785         }
786         auto fd = open(realPath, flags);
787         if (fd < 0) {
788             LOGE("Failed to open local file, errno: %{public}d", errno);
789             break;
790         }
791         fi->fh = static_cast<uint64_t>(fd);
792     } while (0);
793 
794     cInode->sessionRefCount++;
795     LOGI("open success, sessionRefCount: %{public}d", cInode->sessionRefCount.load());
796     fuse_reply_open(req, fi);
797 }
798 
UpdateReadStat(shared_ptr<CloudInode> cInode,uint64_t startTime)799 static void UpdateReadStat(shared_ptr<CloudInode> cInode, uint64_t startTime)
800 {
801     uint64_t endTime = UTCTimeMilliSeconds();
802     CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
803     readStat.UpdateOpenSizeStat(cInode->mBase->size);
804     readStat.UpdateOpenTimeStat(cInode->mBase->fileType, (endTime > startTime) ? (endTime - startTime) : 0);
805 }
806 
CloudOpenHelper(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi,struct FuseData * data,shared_ptr<CloudInode> & cInode)807 static void CloudOpenHelper(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
808     struct FuseData *data, shared_ptr<CloudInode>& cInode)
809 {
810     string recordId = MetaFileMgr::GetInstance().CloudIdToRecordId(cInode->mBase->cloudId);
811     shared_ptr<CloudFile::CloudDatabase> database = GetDatabase(data);
812     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
813 
814     LOGI("%{public}d open %{public}s", req->ctx.pid, GetAnonyString(CloudPath(data, ino)).c_str());
815     if (!database) {
816         LOGE("database is null");
817         fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
818         fuse_reply_err(req, EPERM);
819         return;
820     }
821     wSesLock.lock();
822     if (!cInode->readSession) {
823         /*
824          * 'recordType' is fixed to "media" now
825          * 'assetKey' is one of "content"/"lcd"/"thumbnail"
826          */
827         LOGD("recordId: %s", recordId.c_str());
828         uint64_t startTime = UTCTimeMilliSeconds();
829         cInode->readSession = database->NewAssetReadSession("media", recordId, GetAssetKey(cInode->mBase->fileType),
830                                                             GetAssetPath(cInode, data));
831         if (cInode->readSession) {
832             auto ret = DoCloudOpen(cInode, fi, data);
833             if (ret == 0) {
834                 fuse_reply_open(req, fi);
835             } else {
836                 fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
837                 fuse_reply_err(req, -ret);
838                 cInode->readSession = nullptr;
839             }
840             UpdateReadStat(cInode, startTime);
841             wSesLock.unlock();
842             return;
843         }
844     }
845     if (!cInode->readSession) {
846         fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
847         fuse_reply_err(req, EPERM);
848         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
849             FaultType::DRIVERKIT, EPERM, "readSession is null or fix size fail"});
850     } else {
851         HandleReopen(req, data, cInode, fi);
852     }
853     wSesLock.unlock();
854 }
855 
CloudOpen(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)856 static void CloudOpen(fuse_req_t req, fuse_ino_t ino,
857                       struct fuse_file_info *fi)
858 {
859     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
860     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
861     fi->fh = UINT64_MAX;
862     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
863     if (!cInode) {
864         fuse_reply_err(req, ENOMEM);
865         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::OPEN,
866             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
867         return;
868     }
869 
870     CloudOpenHelper(req, ino, fi, data, cInode);
871 }
872 
CloudRelease(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)873 static void CloudRelease(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
874 {
875     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
876     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
877     if (!cInode) {
878         fuse_reply_err(req, ENOMEM);
879         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::RELEASE,
880             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
881         return;
882     }
883     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
884     LOGI("%{public}d release %{public}s, sessionRefCount: %{public}d", req->ctx.pid,
885          GetAnonyString(cInode->path).c_str(), cInode->sessionRefCount.load());
886     wSesLock.lock();
887     cInode->sessionRefCount--;
888     if (fi->fh != UINT64_MAX) {
889         close(fi->fh);
890         fi->fh = UINT64_MAX;
891     }
892     if (cInode->sessionRefCount == 0) {
893         if (cInode->mBase->fileType == FILE_TYPE_CONTENT && (!cInode->readSession->Close(false))) {
894             LOGE("Failed to close readSession");
895         }
896         cInode->readSession = nullptr;
897         cInode->readCacheMap.clear();
898         {
899             std::unique_lock<std::mutex> lock(cInode->readArgsLock);
900             cInode->readCtlMap.clear();
901         }
902         if (cInode->cacheFileIndex) {
903             string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(data->userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE +
904                                CLOUD_CACHE_DIR + cInode->path;
905             if (setxattr(cachePath.c_str(), CLOUD_CACHE_XATTR_NAME.c_str(), cInode->cacheFileIndex.get(),
906                          sizeof(int[cInode->mBase->size / MAX_READ_SIZE + 1]), 0) < 0) {
907                 LOGE("setxattr fail, err: %{public}d", errno);
908             }
909         }
910         LOGD("readSession released");
911     }
912     wSesLock.unlock();
913 
914     LOGI("release end");
915     fuse_inval(data->se, cInode->parent, ino, cInode->mBase->name);
916     fuse_reply_err(req, 0);
917 }
918 
CloudReadDir(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)919 static void CloudReadDir(fuse_req_t req, fuse_ino_t ino, size_t size,
920                          off_t off, struct fuse_file_info *fi)
921 {
922     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
923     LOGE("readdir %s, not support", GetAnonyString(CloudPath(data, ino)).c_str());
924     fuse_reply_err(req, ENOENT);
925 }
926 
CloudForgetMulti(fuse_req_t req,size_t count,struct fuse_forget_data * forgets)927 static void CloudForgetMulti(fuse_req_t req, size_t count,
928 				struct fuse_forget_data *forgets)
929 {
930     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
931     LOGD("forget_multi");
932     for (size_t i = 0; i < count; i++) {
933         shared_ptr<CloudInode> node = GetCloudInode(data, forgets[i].ino);
934         if (!node) {
935             continue;
936         }
937         LOGD("forget (i=%zu) %s, nlookup: %lld", i, GetAnonyString(node->path).c_str(), (long long)forgets[i].nlookup);
938         PutNode(data, node, forgets[i].nlookup);
939     }
940     fuse_reply_none(req);
941 }
942 
HasCache(fuse_req_t req,fuse_ino_t ino,const void * inBuf)943 static void HasCache(fuse_req_t req, fuse_ino_t ino, const void *inBuf)
944 {
945     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
946     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
947     if (!cInode || !cInode->readSession) {
948         fuse_reply_err(req, ENOMEM);
949         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::IOCTL,
950             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
951         return;
952     }
953 
954     const struct HmdfsHasCache *ioctlData = reinterpret_cast<const struct HmdfsHasCache *>(inBuf);
955     if (!ioctlData || ioctlData->offset < 0 || ioctlData->readSize < 0) {
956         fuse_reply_err(req, EINVAL);
957         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::IOCTL,
958             FaultType::FILE, EINVAL, "invalid argument in ioctl"});
959         return;
960     }
961     int64_t headIndex = ioctlData->offset / MAX_READ_SIZE;
962     int64_t tailIndex = (ioctlData->offset + ioctlData->readSize - 1) / MAX_READ_SIZE;
963     if (cInode->IsReadFinished(headIndex) && cInode->IsReadFinished(tailIndex)) {
964         fuse_reply_ioctl(req, 0, NULL, 0);
965     } else {
966         fuse_reply_err(req, EIO);
967     }
968 }
969 
CheckReadIsCanceled(pid_t pid,shared_ptr<CloudInode> cInode)970 static bool CheckReadIsCanceled(pid_t pid, shared_ptr<CloudInode> cInode)
971 {
972     std::unique_lock<std::mutex> lock(cInode->readArgsLock);
973     return cInode->readCtlMap[pid];
974 }
975 
CancelRead(fuse_req_t req,fuse_ino_t ino)976 static void CancelRead(fuse_req_t req, fuse_ino_t ino)
977 {
978     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
979     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
980     if (!cInode) {
981         fuse_reply_err(req, ENOMEM);
982         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
983             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
984         return;
985     }
986     LOGI("Cancel read for pid: %{public}d", req->ctx.pid);
987     {
988         std::unique_lock<std::mutex> lock(cInode->readArgsLock);
989         cInode->readCtlMap[req->ctx.pid] = true;
990         for (const auto &it : cInode->readArgsSet) {
991             if (it->pid == req->ctx.pid) {
992                 {
993                     std::unique_lock lck(cInode->readLock);
994                     *it->readStatus = READ_CANCELED;
995                 }
996                 it->cond->notify_one();
997             }
998         }
999     }
1000     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1001     wSesLock.lock();
1002     for (auto &it : cInode->readCacheMap) {
1003         std::unique_lock<std::mutex> lock(it.second->mutex);
1004         if (!(it.second->flags & PG_UPTODATE)) {
1005             it.second->cond.notify_all();
1006         }
1007     }
1008     wSesLock.unlock();
1009 
1010     fuse_reply_ioctl(req, 0, NULL, 0);
1011 }
1012 
ResetRead(fuse_req_t req,fuse_ino_t ino)1013 static void ResetRead(fuse_req_t req, fuse_ino_t ino)
1014 {
1015     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1016     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
1017     if (!cInode) {
1018         fuse_reply_err(req, ENOMEM);
1019         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1020             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
1021         return;
1022     }
1023     LOGI("Reset read for pid: %{public}d", req->ctx.pid);
1024     {
1025         std::unique_lock<std::mutex> lock(cInode->readArgsLock);
1026         cInode->readCtlMap[req->ctx.pid] = false;
1027     }
1028     fuse_reply_ioctl(req, 0, NULL, 0);
1029 }
1030 
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)1031 static void CloudIoctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi,
1032                        unsigned flags, const void *inBuf, size_t inBufsz, size_t outBufsz)
1033 {
1034     switch (static_cast<unsigned int>(cmd)) {
1035         case HMDFS_IOC_HAS_CACHE:
1036             HasCache(req, ino, inBuf);
1037             break;
1038         case HMDFS_IOC_CANCEL_READ:
1039             CancelRead(req, ino);
1040             break;
1041         case HMDFS_IOC_RESET_READ:
1042             ResetRead(req, ino);
1043             break;
1044         default:
1045             fuse_reply_err(req, ENOTTY);
1046     }
1047 }
1048 
CheckAndWait(pid_t pid,shared_ptr<CloudInode> cInode,off_t off)1049 static bool CheckAndWait(pid_t pid, shared_ptr<CloudInode> cInode, off_t off)
1050 {
1051     int64_t cacheIndex = off / MAX_READ_SIZE;
1052     if (cInode->IsReadAhead(cacheIndex)) {
1053         auto it = cInode->readCacheMap[cacheIndex];
1054         std::unique_lock<std::mutex> lock(it->mutex);
1055         auto waitStatus = it->cond.wait_for(
1056             lock, READ_TIMEOUT_S, [&] { return (it->flags & PG_UPTODATE) || CheckReadIsCanceled(pid, cInode); });
1057         if (!waitStatus) {
1058             LOGE("CheckAndWait timeout: %{public}ld", static_cast<long>(cacheIndex));
1059             return false;
1060         }
1061     }
1062     /* true when read finish and not canceled */
1063     return !CheckReadIsCanceled(pid, cInode);
1064 }
1065 
SaveCacheToFile(shared_ptr<ReadArguments> readArgs,shared_ptr<CloudInode> cInode,int64_t cacheIndex,int32_t userId)1066 static void SaveCacheToFile(shared_ptr<ReadArguments> readArgs,
1067                             shared_ptr<CloudInode> cInode,
1068                             int64_t cacheIndex,
1069                             int32_t userId)
1070 {
1071     string cachePath =
1072         LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE + CLOUD_CACHE_DIR + cInode->path;
1073     char *realPaths = realpath(cachePath.c_str(), nullptr);
1074     if (realPaths == nullptr) {
1075         LOGE("realpath failed");
1076         return;
1077     }
1078     int fd = open(realPaths, O_RDWR);
1079     free(realPaths);
1080     if (fd < 0) {
1081         LOGE("Failed to open cache file, err: %{public}d", errno);
1082         return;
1083     }
1084     if (cInode->cacheFileIndex.get()[cacheIndex] == NOT_CACHE &&
1085         pwrite(fd, readArgs->buf.get(), *readArgs->readResult, readArgs->offset) == *readArgs->readResult) {
1086         LOGI("Write to cache file, offset: %{public}ld*4M ", static_cast<long>(cacheIndex));
1087         cInode->cacheFileIndex.get()[cacheIndex] = HAS_CACHED;
1088     }
1089     close(fd);
1090 }
1091 
CloudReadOnCloudFile(pid_t pid,int32_t userId,shared_ptr<ReadArguments> readArgs,shared_ptr<CloudInode> cInode,shared_ptr<CloudFile::CloudAssetReadSession> readSession)1092 static void CloudReadOnCloudFile(pid_t pid,
1093                                  int32_t userId,
1094                                  shared_ptr<ReadArguments> readArgs,
1095                                  shared_ptr<CloudInode> cInode,
1096                                  shared_ptr<CloudFile::CloudAssetReadSession> readSession)
1097 {
1098     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
1099     LOGI("PRead CloudFile, path: %{public}s, size: %{public}zd, off: %{public}lu", GetAnonyString(cInode->path).c_str(),
1100          readArgs->size, static_cast<unsigned long>(readArgs->offset));
1101 
1102     uint64_t startTime = UTCTimeMilliSeconds();
1103     bool isReading = true;
1104     int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1105     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1106 
1107     wSesLock.lock();
1108     if (readArgs->offset % MAX_READ_SIZE == 0 && cInode->readCacheMap.find(cacheIndex) == cInode->readCacheMap.end()) {
1109         auto memInfo = std::make_shared<ReadCacheInfo>();
1110         memInfo->flags = PG_READAHEAD;
1111         cInode->readCacheMap[cacheIndex] = memInfo;
1112         isReading = false;
1113         LOGI("To do read cloudfile, offset: %{public}ld*4M", static_cast<long>(cacheIndex));
1114     }
1115     wSesLock.unlock();
1116     if (isReading && !CheckAndWait(pid, cInode, readArgs->offset)) {
1117         return;
1118     }
1119 
1120     *readArgs->readResult =
1121         readSession->PRead(readArgs->offset, readArgs->size, readArgs->buf.get(), *readArgs->ckError);
1122 
1123     uint64_t endTime = UTCTimeMilliSeconds();
1124     uint64_t readTime = (endTime > startTime) ? (endTime - startTime) : 0;
1125     CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
1126     readStat.UpdateReadSizeStat(readArgs->size);
1127     readStat.UpdateReadTimeStat(readArgs->size, readTime);
1128 
1129     {
1130         unique_lock lck(cInode->readLock);
1131         *readArgs->readStatus = READ_FINISHED;
1132     }
1133     readArgs->cond->notify_one();
1134     if (readArgs->offset % MAX_READ_SIZE == 0) {
1135         cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1136         wSesLock.lock();
1137         if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1138             cInode->readCacheMap[cacheIndex]->cond.notify_all();
1139             LOGI("Read cloudfile done and notify all waiting threads, offset: %{public}ld*4M",
1140                 static_cast<long>(cacheIndex));
1141         }
1142         if (IsVideoType(cInode->mBase->name) && *readArgs->readResult > 0) {
1143             ffrt::submit(
1144                 [userId, readArgs, cInode, cacheIndex] { SaveCacheToFile(readArgs, cInode, cacheIndex, userId); });
1145         }
1146         wSesLock.unlock();
1147     }
1148     return;
1149 }
1150 
CloudReadOnCacheFile(shared_ptr<ReadArguments> readArgs,shared_ptr<CloudInode> cInode,shared_ptr<CloudFile::CloudAssetReadSession> readSession,int32_t userId)1151 static void CloudReadOnCacheFile(shared_ptr<ReadArguments> readArgs,
1152                                  shared_ptr<CloudInode> cInode,
1153                                  shared_ptr<CloudFile::CloudAssetReadSession> readSession,
1154                                  int32_t userId)
1155 {
1156     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
1157     if (static_cast<uint64_t>(readArgs->offset) >= cInode->mBase->size) {
1158         return;
1159     }
1160     usleep(READ_CACHE_SLEEP);
1161     uint64_t startTime = UTCTimeMilliSeconds();
1162     int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1163     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1164 
1165     wSesLock.lock();
1166     if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1167         wSesLock.unlock();
1168         return;
1169     }
1170     auto memInfo = std::make_shared<ReadCacheInfo>();
1171     memInfo->flags = PG_READAHEAD;
1172     cInode->readCacheMap[cacheIndex] = memInfo;
1173     wSesLock.unlock();
1174 
1175     readArgs->size = MAX_READ_SIZE;
1176     readArgs->buf.reset(new char[MAX_READ_SIZE], [](char *ptr) { delete[] ptr; });
1177     LOGI("To do preread cloudfile path: %{public}s, size: %{public}zd, offset: %{public}ld*4M",
1178          GetAnonyString(cInode->path).c_str(), readArgs->size, static_cast<long>(cacheIndex));
1179     *readArgs->readResult =
1180         readSession->PRead(readArgs->offset, readArgs->size, readArgs->buf.get(), *readArgs->ckError);
1181 
1182     uint64_t endTime = UTCTimeMilliSeconds();
1183     uint64_t readTime = (endTime > startTime) ? (endTime - startTime) : 0;
1184     CloudDaemonStatistic &readStat = CloudDaemonStatistic::GetInstance();
1185     readStat.UpdateReadSizeStat(readArgs->size);
1186     readStat.UpdateReadTimeStat(readArgs->size, readTime);
1187 
1188     cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1189     wSesLock.lock();
1190     if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1191         cInode->readCacheMap[cacheIndex]->cond.notify_all();
1192         LOGI("Preread cloudfile done and notify all waiting threads, offset: %{public}ld*4M",
1193             static_cast<long>(cacheIndex));
1194     }
1195     if (IsVideoType(cInode->mBase->name) && *readArgs->readResult > 0) {
1196         ffrt::submit([readArgs, cInode, cacheIndex, userId] { SaveCacheToFile(readArgs, cInode, cacheIndex, userId); });
1197     }
1198     wSesLock.unlock();
1199     return;
1200 }
1201 
CloudReadOnLocalFile(fuse_req_t req,shared_ptr<char> buf,size_t size,off_t off,struct fuse_file_info * fi)1202 static void CloudReadOnLocalFile(fuse_req_t req,  shared_ptr<char> buf, size_t size,
1203     off_t off, struct fuse_file_info *fi)
1204 {
1205     auto readSize = pread(fi->fh, buf.get(), size, off);
1206     if (readSize < 0) {
1207         fuse_reply_err(req, errno);
1208         string msg = "Failed to read local file, errno: " + to_string(errno);
1209         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1210             FaultType::FILE, errno, msg});
1211         return;
1212     }
1213     fuse_reply_buf(req, buf.get(), min(size, static_cast<size_t>(readSize)));
1214     return;
1215 }
1216 
InitReadSlice(size_t size,off_t off,ReadSlice & readSlice)1217 static void InitReadSlice(size_t size, off_t off, ReadSlice &readSlice)
1218 {
1219     readSlice.offHead = off;
1220     readSlice.offTail = (off + static_cast<off_t>(size)) / MAX_READ_SIZE * MAX_READ_SIZE;
1221     if (readSlice.offTail > readSlice.offHead) {
1222         readSlice.sizeHead = static_cast<size_t>(readSlice.offTail - readSlice.offHead);
1223         readSlice.sizeTail = MAX_READ_SIZE;
1224     } else {
1225         readSlice.sizeHead = static_cast<size_t>(readSlice.offTail + MAX_READ_SIZE - readSlice.offHead);
1226     }
1227     if ((static_cast<off_t>(size) + off) % MAX_READ_SIZE == 0) {
1228         readSlice.offTail -= MAX_READ_SIZE;
1229         readSlice.sizeTail = 0;
1230     }
1231 }
1232 
FixData(fuse_req_t req,shared_ptr<char> buf,size_t size,size_t sizeDone,shared_ptr<ReadArguments> readArgs)1233 static bool FixData(fuse_req_t req, shared_ptr<char> buf, size_t size, size_t sizeDone,
1234     shared_ptr<ReadArguments> readArgs)
1235 {
1236     if (*readArgs->readResult < 0) {
1237         fuse_reply_err(req, ENOMEM);
1238         string msg = "Pread failed, readResult: " + to_string(*readArgs->readResult);
1239         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1240             FaultType::DRIVERKIT, ENOMEM, msg});
1241         return false;
1242     }
1243 
1244     auto ret = HandleCloudError(*readArgs->ckError, FaultOperation::READ);
1245     if (ret < 0) {
1246         fuse_reply_err(req, -ret);
1247         return false;
1248     }
1249 
1250     int32_t copyRet = memcpy_s(buf.get() + sizeDone, min(size - sizeDone, static_cast<size_t>(*readArgs->readResult)),
1251                                readArgs->buf.get(), min(size - sizeDone, static_cast<size_t>(*readArgs->readResult)));
1252     if (copyRet != 0) {
1253         fuse_reply_err(req, ENETUNREACH);
1254         string msg = "Parcel data copy failed, err=" + to_string(copyRet);
1255         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1256             FaultType::FILE, copyRet, msg});
1257         return false;
1258     }
1259     return true;
1260 }
1261 
WaitData(fuse_req_t req,shared_ptr<CloudInode> cInode,shared_ptr<ReadArguments> readArgs)1262 static bool WaitData(fuse_req_t req, shared_ptr<CloudInode> cInode, shared_ptr<ReadArguments> readArgs)
1263 {
1264     std::unique_lock lock(cInode->readLock);
1265     auto waitStatus = readArgs->cond->wait_for(lock, READ_TIMEOUT_S, [readArgs] { return *readArgs->readStatus; });
1266     if (*readArgs->readStatus == READ_CANCELED) {
1267         LOGI("read is cancelled");
1268         fuse_reply_err(req, EIO);
1269         return false;
1270     }
1271     if (!waitStatus) {
1272         std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
1273         int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1274         cInode->SetReadCacheFlag(cacheIndex, PG_UPTODATE);
1275         wSesLock.lock();
1276         if (cInode->readCacheMap.find(cacheIndex) != cInode->readCacheMap.end()) {
1277             cInode->readCacheMap[cacheIndex]->cond.notify_all();
1278         }
1279         wSesLock.unlock();
1280         LOGE("Pread timeout, offset: %{public}ld*4M", static_cast<long>(cacheIndex));
1281         fuse_reply_err(req, ENETUNREACH);
1282         return false;
1283     }
1284     return true;
1285 }
1286 
WaitAndFixData(fuse_req_t req,shared_ptr<char> buf,size_t size,shared_ptr<CloudInode> cInode,vector<shared_ptr<ReadArguments>> readArgsList)1287 static void WaitAndFixData(fuse_req_t req,
1288                            shared_ptr<char> buf,
1289                            size_t size,
1290                            shared_ptr<CloudInode> cInode,
1291                            vector<shared_ptr<ReadArguments>> readArgsList)
1292 {
1293     size_t sizeDone = 0;
1294     for (auto &it : readArgsList) {
1295         if (!it) {
1296             continue;
1297         }
1298         if (!WaitData(req, cInode, it)) {
1299             return;
1300         }
1301         if (!FixData(req, buf, size, sizeDone, it)) {
1302             return;
1303         }
1304         sizeDone += it->size;
1305     }
1306     fuse_reply_buf(req, buf.get(), min(size, sizeDone));
1307 }
1308 
ReadCacheFile(shared_ptr<ReadArguments> readArgs,const string & path,int32_t userId)1309 static ssize_t ReadCacheFile(shared_ptr<ReadArguments> readArgs, const string &path, int32_t userId)
1310 {
1311     string cachePath =
1312         LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE + CLOUD_CACHE_DIR + path;
1313     char *realPaths = realpath(cachePath.c_str(), nullptr);
1314     if (realPaths == nullptr) {
1315         LOGE("realpath failed");
1316         return -1;
1317     }
1318     int fd = open(realPaths, O_RDONLY);
1319     free(realPaths);
1320     if (fd < 0) {
1321         return fd;
1322     }
1323     ssize_t bytesRead = pread(fd, readArgs->buf.get(), readArgs->size, readArgs->offset);
1324     close(fd);
1325     return bytesRead;
1326 }
1327 
DoReadSlice(fuse_req_t req,shared_ptr<CloudInode> cInode,shared_ptr<CloudFile::CloudAssetReadSession> readSession,shared_ptr<ReadArguments> readArgs,bool needCheck)1328 static bool DoReadSlice(fuse_req_t req,
1329                         shared_ptr<CloudInode> cInode,
1330                         shared_ptr<CloudFile::CloudAssetReadSession> readSession,
1331                         shared_ptr<ReadArguments> readArgs,
1332                         bool needCheck)
1333 {
1334     if (!readArgs) {
1335         return true;
1336     }
1337     if (!readArgs->buf) {
1338         fuse_reply_err(req, ENOMEM);
1339         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1340             FaultType::FILE, ENOMEM, "buffer is null"});
1341         return false;
1342     }
1343     int64_t cacheIndex = readArgs->offset / MAX_READ_SIZE;
1344     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1345     if (IsVideoType(cInode->mBase->name) && cInode->cacheFileIndex.get()[cacheIndex] == HAS_CACHED) {
1346         LOGI("DoReadSlice from local: %{public}ld", static_cast<long>(cacheIndex));
1347         *readArgs->readResult = ReadCacheFile(readArgs, cInode->path, data->userId);
1348         if (*readArgs->readResult >= 0) {
1349             unique_lock lock(cInode->readLock);
1350             *readArgs->readStatus = READ_FINISHED;
1351             return true;
1352         } else {
1353             LOGI("read cache file failed, errno: %{public}d", errno);
1354         }
1355     }
1356 
1357     LOGI("DoReadSlice from cloud: %{public}ld", static_cast<long>(cacheIndex));
1358     if (needCheck && !CheckAndWait(req->ctx.pid, cInode, readArgs->offset)) {
1359         fuse_reply_err(req, ENETUNREACH);
1360         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1361             FaultType::CLOUD_READ_FILE_TIMEOUT, ENETUNREACH, "network timeout"});
1362         return false;
1363     }
1364     ffrt::submit([req, readArgs, cInode, readSession, data] {
1365         CloudReadOnCloudFile(req->ctx.pid, data->userId, readArgs, cInode, readSession);
1366     });
1367     return true;
1368 }
1369 
DoCloudRead(fuse_req_t req,int flags,DoCloudReadParams params)1370 static bool DoCloudRead(fuse_req_t req, int flags, DoCloudReadParams params)
1371 {
1372     if (!DoReadSlice(req, params.cInode, params.readSession, params.readArgsHead, true)) {
1373         return false;
1374     }
1375     if (!DoReadSlice(req, params.cInode, params.readSession, params.readArgsTail, false)) {
1376         return false;
1377     }
1378     // no prefetch when contains O_NOFOLLOW
1379     unsigned int unflags = static_cast<unsigned int>(flags);
1380     if (unflags & O_NOFOLLOW) {
1381         return true;
1382     }
1383 
1384     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1385     for (uint32_t i = 1; i <= CACHE_PAGE_NUM; i++) {
1386         int64_t cacheIndex = static_cast<int64_t>(
1387             (params.readArgsTail ? params.readArgsTail->offset : params.readArgsHead->offset) / MAX_READ_SIZE + i);
1388         if (IsVideoType(params.cInode->mBase->name) && params.cInode->cacheFileIndex.get()[cacheIndex] == HAS_CACHED) {
1389             continue;
1390         }
1391         auto readArgsCache = make_shared<ReadArguments>(0, cacheIndex * MAX_READ_SIZE, req->ctx.pid);
1392         if (!readArgsCache) {
1393             LOGE("Init readArgsCache failed");
1394             break;
1395         }
1396         ffrt::submit([readArgsCache, params, data] {
1397             CloudReadOnCacheFile(readArgsCache, params.cInode, params.readSession, data->userId);
1398         });
1399     }
1400     return true;
1401 }
1402 
CloudReadHelper(fuse_req_t req,size_t size,shared_ptr<CloudInode> cInode)1403 static bool CloudReadHelper(fuse_req_t req, size_t size, shared_ptr<CloudInode> cInode)
1404 {
1405     if (CheckReadIsCanceled(req->ctx.pid, cInode)) {
1406         LOGI("read is cancelled");
1407         fuse_reply_err(req, EIO);
1408         return false;
1409     }
1410     if (size > MAX_READ_SIZE) {
1411         fuse_reply_err(req, EINVAL);
1412         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1413             FaultType::FILE, EINVAL, "input size exceeds MAX_READ_SIZE"});
1414         return false;
1415     }
1416     return true;
1417 }
1418 
CloudRead(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)1419 static void CloudRead(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1420                       struct fuse_file_info *fi)
1421 {
1422     HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__);
1423     shared_ptr<char> buf = nullptr;
1424     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
1425     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
1426     if (!cInode) {
1427         fuse_reply_err(req, ENOMEM);
1428         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1429             FaultType::INODE_FILE, ENOMEM, "failed to get cloud inode"});
1430         return;
1431     }
1432     LOGI("CloudRead: %{public}s, size=%{public}zd, off=%{public}lu",
1433          GetAnonyString(CloudPath(data, ino)).c_str(), size, (unsigned long)off);
1434 
1435     if (!CloudReadHelper(req, size, cInode)) {
1436         return;
1437     }
1438     auto dkReadSession = cInode->readSession;
1439     if (!dkReadSession) {
1440         fuse_reply_err(req, EPERM);
1441         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1442             FaultType::FILE, EPERM, "readSession is nullptr"});
1443         return;
1444     }
1445     buf.reset(new char[size], [](char *ptr) { delete[] ptr; });
1446     if (!buf) {
1447         fuse_reply_err(req, ENOMEM);
1448         CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{PHOTOS_BUNDLE_NAME, FaultOperation::READ,
1449             FaultType::FILE, ENOMEM, "buffer is null"});
1450         return;
1451     }
1452     if (cInode->mBase->hasDownloaded && fi->fh != UINT64_MAX) {
1453         CloudReadOnLocalFile(req, buf, size, off, fi);
1454         return;
1455     }
1456 
1457     ReadSlice readSlice;
1458     /* slice read request into 4M pages */
1459     InitReadSlice(size, off, readSlice);
1460     /* init readArgs for current page, and next page if cross pages */
1461     auto readArgsHead = make_shared<ReadArguments>(readSlice.sizeHead, readSlice.offHead, req->ctx.pid);
1462     shared_ptr<ReadArguments> readArgsTail = nullptr;
1463     if (readSlice.sizeTail > 0) {
1464         readArgsTail = make_shared<ReadArguments>(readSlice.sizeTail, readSlice.offTail, req->ctx.pid);
1465     }
1466     if (!DoCloudRead(req, fi->flags, {cInode, dkReadSession, readArgsHead, readArgsTail})) {
1467         return;
1468     }
1469     /* wait and reply current req data */
1470     InsertReadArgs(cInode, {readArgsHead, readArgsTail});
1471     WaitAndFixData(req, buf, size, cInode, {readArgsHead, readArgsTail});
1472     EraseReadArgs(cInode, {readArgsHead, readArgsTail});
1473 }
1474 
1475 static const struct fuse_lowlevel_ops cloudDiskFuseOps = {
1476     .lookup             = CloudDisk::FuseOperations::Lookup,
1477     .forget             = CloudDisk::FuseOperations::Forget,
1478     .getattr            = CloudDisk::FuseOperations::GetAttr,
1479     .setattr            = CloudDisk::FuseOperations::SetAttr,
1480     .mknod              = CloudDisk::FuseOperations::MkNod,
1481     .mkdir              = CloudDisk::FuseOperations::MkDir,
1482     .unlink             = CloudDisk::FuseOperations::Unlink,
1483     .rmdir              = CloudDisk::FuseOperations::RmDir,
1484     .rename             = CloudDisk::FuseOperations::Rename,
1485     .open               = CloudDisk::FuseOperations::Open,
1486     .read               = CloudDisk::FuseOperations::Read,
1487     .release            = CloudDisk::FuseOperations::Release,
1488     .readdir            = CloudDisk::FuseOperations::ReadDir,
1489     .setxattr           = CloudDisk::FuseOperations::SetXattr,
1490     .getxattr           = CloudDisk::FuseOperations::GetXattr,
1491     .access             = CloudDisk::FuseOperations::Access,
1492     .create             = CloudDisk::FuseOperations::Create,
1493     .write_buf          = CloudDisk::FuseOperations::WriteBuf,
1494     .forget_multi       = CloudDisk::FuseOperations::ForgetMulti,
1495     .lseek              = CloudDisk::FuseOperations::Lseek,
1496 };
1497 
1498 static const struct fuse_lowlevel_ops cloudMediaFuseOps = {
1499     .lookup             = CloudLookup,
1500     .forget             = CloudForget,
1501     .getattr            = CloudGetAttr,
1502     .open               = CloudOpen,
1503     .read               = CloudRead,
1504     .release            = CloudRelease,
1505     .readdir            = CloudReadDir,
1506     .ioctl              = CloudIoctl,
1507     .forget_multi       = CloudForgetMulti,
1508 };
1509 
CheckPathForStartFuse(const string & path)1510 static bool CheckPathForStartFuse(const string &path)
1511 {
1512     char resolvedPath[PATH_MAX] = {'\0'};
1513     char* ret = realpath(path.c_str(), resolvedPath);
1514     if (ret == nullptr) {
1515         return false;
1516     }
1517     std::string realPath(resolvedPath);
1518     std::string pathPrefix = "/mnt/data/";
1519     if (realPath.rfind(pathPrefix, 0) != 0) {
1520         return false;
1521     }
1522 
1523     size_t userIdBeginPos = pathPrefix.length();
1524     size_t userIdEndPos = realPath.find("/", userIdBeginPos);
1525     const std::string userId = realPath.substr(userIdBeginPos, userIdEndPos - userIdBeginPos);
1526     if (userId.find_first_not_of("0123456789") != std::string::npos) {
1527         return false;
1528     }
1529 
1530     size_t suffixBeginPos = userIdEndPos + 1;
1531     const std::string pathSuffix1 = "cloud";
1532     const std::string pathSuffix2 = "cloud_fuse";
1533     if (realPath.rfind(pathSuffix1) == suffixBeginPos &&
1534         suffixBeginPos + pathSuffix1.length() == realPath.length()) {
1535         return true;
1536     }
1537     if (realPath.rfind(pathSuffix2) == suffixBeginPos &&
1538         suffixBeginPos + pathSuffix2.length() == realPath.length()) {
1539         return true;
1540     }
1541     return false;
1542 }
1543 
SetNewSessionInfo(struct fuse_session * se,struct fuse_loop_config & config,int32_t devFd,const string & path)1544 static int SetNewSessionInfo(struct fuse_session *se, struct fuse_loop_config &config,
1545                              int32_t devFd, const string &path)
1546 {
1547     se->fd = devFd;
1548     se->mountpoint = strdup(path.c_str());
1549     if (se->mountpoint == nullptr) {
1550         return -ENOMEM;
1551     }
1552 
1553     fuse_daemonize(true);
1554     int ret = fuse_session_loop_mt(se, &config);
1555 
1556     fuse_session_unmount(se);
1557     LOGI("fuse_session_unmount");
1558     if (se->mountpoint) {
1559         free(se->mountpoint);
1560         se->mountpoint = nullptr;
1561     }
1562 
1563     fuse_session_destroy(se);
1564     return ret;
1565 }
1566 
StartFuse(int32_t userId,int32_t devFd,const string & path)1567 int32_t FuseManager::StartFuse(int32_t userId, int32_t devFd, const string &path)
1568 {
1569     LOGI("FuseManager::StartFuse entry");
1570     struct fuse_loop_config config;
1571     struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
1572     struct CloudDisk::CloudDiskFuseData cloudDiskData;
1573     struct FuseData data;
1574     struct fuse_session *se = nullptr;
1575     if (fuse_opt_add_arg(&args, path.c_str()) || !CheckPathForStartFuse(path)) {
1576         LOGE("Mount path invalid");
1577         return -EINVAL;
1578     }
1579     if (path.find("cloud_fuse") != string::npos) {
1580         fuse_set_log_func([](enum fuse_log_level level, const char *fmt, va_list ap) {
1581 #pragma clang diagnostic push
1582 #pragma clang diagnostic ignored "-Wformat-nonliteral"
1583             char *str = nullptr;
1584             if (vasprintf(&str, fmt, ap) < 0) {
1585                 LOGE("FUSE: log failed");
1586                 return ;
1587             }
1588 
1589             LOGE("FUSE: %{public}s", str);
1590             free(str);
1591 #pragma clang diagnostic pop
1592         });
1593         se = fuse_session_new(&args, &cloudDiskFuseOps,
1594                               sizeof(cloudDiskFuseOps), &cloudDiskData);
1595         if (se == nullptr) {
1596             LOGE("cloud disk fuse_session_new error");
1597             return -EINVAL;
1598         }
1599         cloudDiskData.userId = userId;
1600         cloudDiskData.se = se;
1601         config.max_idle_threads = 1;
1602     } else {
1603         se = fuse_session_new(&args, &cloudMediaFuseOps, sizeof(cloudMediaFuseOps), &data);
1604         if (se == nullptr) {
1605             LOGE("cloud media fuse_session_new error");
1606             return -EINVAL;
1607         }
1608         data.userId = userId;
1609         data.se = se;
1610         config.max_idle_threads = MAX_IDLE_THREADS;
1611     }
1612     LOGI("fuse_session_new success, userId: %{public}d", userId);
1613     int ret = SetNewSessionInfo(se, config, devFd, path);
1614     return ret;
1615 }
1616 
GetInstance()1617 FuseManager &FuseManager::GetInstance()
1618 {
1619     static FuseManager instance_;
1620     return instance_;
1621 }
1622 
1623 } // namespace CloudFile
1624 } // namespace FileManagement
1625 } // namespace OHOS
1626