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