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