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