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