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