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