• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #define FUSE_USE_VERSION 34
17 
18 #include "fuse_manager/fuse_manager.h"
19 
20 #include <atomic>
21 #include <cassert>
22 #include <cerrno>
23 #include <cstddef>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <cstring>
27 #include <exception>
28 #include <filesystem>
29 #include <fcntl.h>
30 #include <iostream>
31 #include <map>
32 #include <mutex>
33 #include <pthread.h>
34 #include <shared_mutex>
35 #include <stdexcept>
36 #include <string>
37 #include <thread>
38 #include <unistd.h>
39 
40 #include <fuse.h>
41 #include <fuse_i.h>
42 #include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
43 
44 #include "datetime_ex.h"
45 #include "dfs_error.h"
46 #include "directory_ex.h"
47 #include "dk_database.h"
48 #include "dk_asset_read_session.h"
49 #include "drive_kit.h"
50 #include "meta_file.h"
51 #include "sdk_helper.h"
52 #include "utils_log.h"
53 
54 namespace OHOS {
55 namespace FileManagement {
56 namespace CloudFile {
57 using namespace std;
58 
59 static const string LOCAL_PATH_PREFIX = "/mnt/hmdfs/";
60 static const string LOCAL_PATH_SUFFIX = "/account/device_view/local";
61 static const string FUSE_CACHE_PATH_PREFIX = "/data/service/el2/";
62 static const string FUSE_CACHE_PATH_SUFFIX = "/hmdfs/fuse";
63 static const string PHOTOS_BUNDLE_NAME = "com.ohos.photos";
64 static const unsigned int OID_USER_DATA_RW = 1008;
65 static const unsigned int STAT_NLINK_REG = 1;
66 static const unsigned int STAT_NLINK_DIR = 2;
67 static const unsigned int STAT_MODE_REG = 0770;
68 static const unsigned int STAT_MODE_DIR = 0771;
69 
70 struct CloudInode {
71     shared_ptr<MetaBase> mBase{nullptr};
72     shared_ptr<MetaFile> mFile{nullptr};
73     string path;
74     fuse_ino_t parent{0};
75     atomic<int> refCount{0};
76     shared_ptr<DriveKit::DKAssetReadSession> readSession{nullptr};
77     atomic<int> sessionRefCount{0};
78     std::shared_mutex sessionLock;
79 };
80 
81 struct FuseData {
82     int userId;
83     shared_ptr<CloudInode> rootNode{nullptr};
84     /* store CloudInode by path */
85     map<string, shared_ptr<CloudInode>> inodeCache;
86     std::shared_mutex cacheLock;
87     shared_ptr<DriveKit::DKDatabase> database;
88     struct fuse_session *se;
89 };
90 
GetDatabase(struct FuseData * data)91 static shared_ptr<DriveKit::DKDatabase> GetDatabase(struct FuseData *data)
92 {
93     if (!data->database) {
94         auto driveKit = DriveKit::DriveKitNative::GetInstance(data->userId);
95         if (driveKit == nullptr) {
96             LOGE("sdk helper get drive kit instance fail");
97             return nullptr;
98         }
99 
100         auto container = driveKit->GetDefaultContainer(PHOTOS_BUNDLE_NAME);
101         if (container == nullptr) {
102             LOGE("sdk helper get drive kit container fail");
103             return nullptr;
104         }
105 
106         data->database = container->GetPrivateDatabase();
107         if (data->database == nullptr) {
108             LOGE("sdk helper get drive kit database fail");
109             return nullptr;
110         }
111     }
112     return data->database;
113 }
114 
FindNode(struct FuseData * data,string path)115 static shared_ptr<CloudInode> FindNode(struct FuseData *data, string path)
116 {
117     shared_ptr<CloudInode> ret;
118     std::shared_lock<std::shared_mutex> rLock(data->cacheLock, std::defer_lock);
119     rLock.lock();
120     ret = data->inodeCache[path];
121     rLock.unlock();
122     return ret;
123 }
124 
GetRootInode(struct FuseData * data,fuse_ino_t ino)125 static shared_ptr<CloudInode> GetRootInode(struct FuseData *data, fuse_ino_t ino)
126 {
127     std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
128     shared_ptr<CloudInode> ret;
129 
130     wLock.lock();
131     if (!data->rootNode) {
132         data->rootNode = make_shared<CloudInode>();
133         data->rootNode->path = "/";
134         data->rootNode->refCount = 1;
135         data->rootNode->mBase = make_shared<MetaBase>();
136         data->rootNode->mBase->mode = S_IFDIR;
137         data->rootNode->mBase->mtime = static_cast<uint64_t>(GetSecondsSince1970ToNow());
138         LOGD("create rootNode");
139     }
140     data->rootNode->mFile = MetaFileMgr::GetInstance().GetMetaFile(data->userId, "/");
141     ret = data->rootNode;
142     wLock.unlock();
143 
144     return ret;
145 }
146 
GetCloudInode(struct FuseData * data,fuse_ino_t ino)147 static shared_ptr<CloudInode> GetCloudInode(struct FuseData *data, fuse_ino_t ino)
148 {
149     if (ino == FUSE_ROOT_ID) {
150         return GetRootInode(data, ino);
151     } else {
152         struct CloudInode *inoPtr = reinterpret_cast<struct CloudInode *>(ino);
153         return FindNode(data, inoPtr->path);
154     }
155 }
156 
CloudPath(struct FuseData * data,fuse_ino_t ino)157 static string CloudPath(struct FuseData *data, fuse_ino_t ino)
158 {
159     return GetCloudInode(data, ino)->path;
160 }
161 
GetMetaAttr(shared_ptr<CloudInode> ino,struct stat * stbuf)162 static void GetMetaAttr(shared_ptr<CloudInode> ino, struct stat *stbuf)
163 {
164     stbuf->st_ino = reinterpret_cast<fuse_ino_t>(ino.get());
165     stbuf->st_uid = OID_USER_DATA_RW;
166     stbuf->st_gid = OID_USER_DATA_RW;
167     stbuf->st_mtime = static_cast<int64_t>(ino->mBase->mtime);
168     if (ino->mBase->mode & S_IFDIR) {
169         stbuf->st_mode = S_IFDIR | STAT_MODE_DIR;
170         stbuf->st_nlink = STAT_NLINK_DIR;
171         LOGD("directory, ino:%s", ino->path.c_str());
172     } else {
173         stbuf->st_mode = S_IFREG | STAT_MODE_REG;
174         stbuf->st_nlink = STAT_NLINK_REG;
175         stbuf->st_size = static_cast<decltype(stbuf->st_size)>(ino->mBase->size);
176         LOGD("regular file, ino:%s, size: %lld", ino->path.c_str(), (long long)stbuf->st_size);
177     }
178 }
179 
CloudDoLookup(fuse_req_t req,fuse_ino_t parent,const char * name,struct fuse_entry_param * e)180 static int CloudDoLookup(fuse_req_t req, fuse_ino_t parent, const char *name,
181                          struct fuse_entry_param *e)
182 {
183     int err = 0;
184     shared_ptr<CloudInode> child;
185     bool create = false;
186     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
187     string childName = (parent == FUSE_ROOT_ID) ? CloudPath(data, parent) + name :
188                                                   CloudPath(data, parent) + "/" + name;
189     std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
190 
191     LOGD("parent: %{private}s, name: %s", CloudPath(data, parent).c_str(), name);
192 
193     child = FindNode(data, childName);
194     if (!child) {
195         child = make_shared<CloudInode>();
196         create = true;
197         LOGD("new child %s", child->path.c_str());
198     }
199     child->mBase = make_shared<MetaBase>(name);
200     child->path = childName;
201     child->refCount++;
202     err = GetCloudInode(data, parent)->mFile->DoLookup(*(child->mBase));
203     if (err) {
204         LOGE("lookup %s error, err: %{public}d", childName.c_str(), err);
205         return err;
206     }
207     if (child->mBase->mode & S_IFDIR) {
208         child->mFile = MetaFileMgr::GetInstance().GetMetaFile(data->userId,
209                                                               childName);
210     }
211     child->parent = parent;
212     if (create) {
213         wLock.lock();
214         data->inodeCache[child->path] = child;
215         wLock.unlock();
216     }
217     LOGD("lookup success, child: %{private}s, refCount: %lld", child->path.c_str(),
218          static_cast<long long>(child->refCount));
219     GetMetaAttr(child, &e->attr);
220     e->ino = reinterpret_cast<fuse_ino_t>(child.get());
221     return 0;
222 }
223 
CloudLookup(fuse_req_t req,fuse_ino_t parent,const char * name)224 static void CloudLookup(fuse_req_t req, fuse_ino_t parent,
225                         const char *name)
226 {
227     struct fuse_entry_param e;
228     int err;
229 
230     err = CloudDoLookup(req, parent, name, &e);
231     if (err) {
232         fuse_reply_err(req, err);
233     } else {
234         fuse_reply_entry(req, &e);
235     }
236 }
237 
PutNode(struct FuseData * data,shared_ptr<CloudInode> node,uint64_t num)238 static void PutNode(struct FuseData *data, shared_ptr<CloudInode> node, uint64_t num)
239 {
240     std::unique_lock<std::shared_mutex> wLock(data->cacheLock, std::defer_lock);
241     node->refCount -= num;
242     LOGD("%s, put num: %lld,  current refCount: %d", node->path.c_str(), (long long)num,  node->refCount.load());
243     if (node->refCount == 0) {
244         LOGD("node released: %s", node->path.c_str());
245         wLock.lock();
246         data->inodeCache.erase(node->path);
247         wLock.unlock();
248     }
249 }
250 
CloudForget(fuse_req_t req,fuse_ino_t ino,uint64_t nlookup)251 static void CloudForget(fuse_req_t req, fuse_ino_t ino,
252                         uint64_t nlookup)
253 {
254     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
255     shared_ptr<CloudInode> node = GetCloudInode(data, ino);
256     LOGD("forget %s, nlookup: %lld", node->path.c_str(), (long long)nlookup);
257     PutNode(data, node, nlookup);
258     fuse_reply_none(req);
259 }
260 
CloudGetAttr(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)261 static void CloudGetAttr(fuse_req_t req, fuse_ino_t ino,
262                          struct fuse_file_info *fi)
263 {
264     struct stat buf;
265     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
266     (void) fi;
267 
268     LOGD("getattr, %s", CloudPath(data, ino).c_str());
269     GetMetaAttr(GetCloudInode(data, ino), &buf);
270 
271     fuse_reply_attr(req, &buf, 0);
272 }
273 
GetAssetKey(int fileType)274 static string GetAssetKey(int fileType)
275 {
276     switch (fileType) {
277         case FILE_TYPE_CONTENT:
278             return "content";
279         case FILE_TYPE_THUMBNAIL:
280             return "thumbnail";
281         case FILE_TYPE_LCD:
282             return "lcd";
283         default:
284             LOGE("bad fileType %{public}d", fileType);
285             return "";
286     }
287 }
288 
GetLocalPath(int32_t userId)289 static string GetLocalPath(int32_t userId)
290 {
291     return LOCAL_PATH_PREFIX + to_string(userId) + LOCAL_PATH_SUFFIX;
292 }
293 
GetFuseCachePath(int32_t userId)294 static string GetFuseCachePath(int32_t userId)
295 {
296     return FUSE_CACHE_PATH_PREFIX + to_string(userId) + FUSE_CACHE_PATH_SUFFIX;
297 }
298 
GetAssetPath(shared_ptr<CloudInode> cInode,struct FuseData * data)299 static string GetAssetPath(shared_ptr<CloudInode> cInode, struct FuseData *data)
300 {
301     string path;
302     filesystem::path parentPath;
303     switch (cInode->mBase->fileType) {
304         case FILE_TYPE_THUMBNAIL:
305         case FILE_TYPE_LCD:
306             path = GetLocalPath(data->userId) + cInode->path;
307             break;
308         default:
309             path = GetFuseCachePath(data->userId) + cInode->path;
310             break;
311     }
312     parentPath = filesystem::path(path).parent_path();
313     ForceCreateDirectory(parentPath.string());
314     LOGD("fileType: %d, create dir: %s, relative path: %s",
315          cInode->mBase->fileType, parentPath.string().c_str(), cInode->path.c_str());
316     return path;
317 }
318 
CloudOpen(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)319 static void CloudOpen(fuse_req_t req, fuse_ino_t ino,
320                       struct fuse_file_info *fi)
321 {
322     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
323     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
324     string recordId = MetaFileMgr::GetInstance().CloudIdToRecordId(cInode->mBase->cloudId);
325     shared_ptr<DriveKit::DKDatabase> database = GetDatabase(data);
326     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
327 
328     LOGD("open %s", CloudPath(data, ino).c_str());
329     if (!database) {
330         fuse_reply_err(req, EPERM);
331         return;
332     }
333     wSesLock.lock();
334     if (!cInode->readSession) {
335         /*
336          * 'recordType' is fixed to "fileType" now
337          * 'assetKey' is one of "content"/"lcd"/"thumbnail"
338          */
339         LOGD("recordId: %s", recordId.c_str());
340         cInode->readSession = database->NewAssetReadSession("fileType",
341                                                             recordId,
342                                                             GetAssetKey(cInode->mBase->fileType),
343                                                             GetAssetPath(cInode, data));
344     }
345 
346     if (!cInode->readSession) {
347         fuse_reply_err(req, EPERM);
348     } else {
349         cInode->sessionRefCount++;
350         LOGD("open success, sessionRefCount: %d", cInode->sessionRefCount.load());
351         fuse_reply_open(req, fi);
352     }
353     wSesLock.unlock();
354 }
355 
CloudRelease(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)356 static void CloudRelease(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
357 {
358     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
359     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
360     std::unique_lock<std::shared_mutex> wSesLock(cInode->sessionLock, std::defer_lock);
361 
362     wSesLock.lock();
363     LOGD("%s, sessionRefCount: %d", CloudPath(data, ino).c_str(), cInode->sessionRefCount.load());
364     cInode->sessionRefCount--;
365     if (cInode->sessionRefCount == 0) {
366         bool needRemain = false;
367         if (cInode->mBase->fileType != FILE_TYPE_CONTENT) {
368             needRemain = true;
369         }
370         bool res = cInode->readSession->Close(needRemain);
371         if (!res) {
372             LOGE("close error, needRemain: %d", needRemain);
373         }
374         if (needRemain && res) {
375             GetCloudInode(data, cInode->parent)->mFile->DoRemove(*(cInode->mBase));
376             LOGD("remove from dentryfile");
377         }
378         cInode->readSession = nullptr;
379         LOGD("readSession released");
380     }
381     wSesLock.unlock();
382 
383     fuse_reply_err(req, 0);
384 }
385 
CloudReadDir(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)386 static void CloudReadDir(fuse_req_t req, fuse_ino_t ino, size_t size,
387                          off_t off, struct fuse_file_info *fi)
388 {
389     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
390     LOGE("readdir %s, not support", CloudPath(data, ino).c_str());
391     fuse_reply_err(req, ENOENT);
392 }
393 
CloudForgetMulti(fuse_req_t req,size_t count,struct fuse_forget_data * forgets)394 static void CloudForgetMulti(fuse_req_t req, size_t count,
395 				struct fuse_forget_data *forgets)
396 {
397     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
398     LOGD("forget_multi");
399     for (size_t i = 0; i < count; i++) {
400         shared_ptr<CloudInode> node = GetCloudInode(data, forgets[i].ino);
401         LOGD("forget (i=%zu) %s, nlookup: %lld", i, node->path.c_str(), (long long)forgets[i].nlookup);
402         PutNode(data, node, forgets[i].nlookup);
403     }
404     fuse_reply_none(req);
405 }
406 
CloudRead(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)407 static void CloudRead(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
408                       struct fuse_file_info *fi)
409 {
410     int64_t readSize;
411     DriveKit::DKError dkError;
412     shared_ptr<char> buf = nullptr;
413     struct FuseData *data = static_cast<struct FuseData *>(fuse_req_userdata(req));
414     shared_ptr<CloudInode> cInode = GetCloudInode(data, ino);
415 
416     LOGD("%s, size=%zd, off=%lu", CloudPath(data, ino).c_str(), size, (unsigned long)off);
417 
418     buf.reset(new char[size], [](char* ptr) {
419         delete[] ptr;
420     });
421     if (!buf) {
422         fuse_reply_err(req, ENOMEM);
423         return;
424     }
425 
426     if (!cInode->readSession) {
427         fuse_reply_err(req, EPERM);
428         return;
429     }
430 
431     readSize = cInode->readSession->PRead(off, size, buf.get(), dkError);
432     if (dkError.HasError()) {
433         LOGE("read error");
434         fuse_reply_err(req, EIO);
435         return;
436     }
437 
438     LOGD("read %s success, %lld bytes", CloudPath(data, ino).c_str(), static_cast<long long>(readSize));
439     fuse_reply_buf(req, buf.get(), readSize);
440 }
441 
442 static const struct fuse_lowlevel_ops cloudFuseOps = {
443     .lookup             = CloudLookup,
444     .forget             = CloudForget,
445     .getattr            = CloudGetAttr,
446     .open               = CloudOpen,
447     .read               = CloudRead,
448     .release            = CloudRelease,
449     .readdir            = CloudReadDir,
450     .forget_multi       = CloudForgetMulti,
451 };
452 
StartFuse(int32_t userId,int32_t devFd,const string & path)453 int32_t FuseManager::StartFuse(int32_t userId, int32_t devFd, const string &path)
454 {
455     struct fuse_loop_config config;
456     struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
457     struct FuseData data;
458     struct fuse_session *se = nullptr;
459     int ret;
460 
461     if (fuse_opt_add_arg(&args, path.c_str())) {
462         LOGE("Mount path invalid");
463         return -EINVAL;
464     }
465 
466     se = fuse_session_new(&args, &cloudFuseOps,
467                           sizeof(cloudFuseOps), &data);
468     if (se == nullptr) {
469         LOGE("fuse_session_new error");
470         return -EINVAL;
471     }
472 
473     data.userId = userId;
474     data.se = se;
475 
476     LOGI("fuse_session_new success, userId: %{public}d", userId);
477     se->fd = devFd;
478     se->mountpoint = strdup(path.c_str());
479     sessions_[userId] = se;
480 
481     fuse_daemonize(true);
482     config.max_idle_threads = 1;
483     ret = fuse_session_loop_mt(se, &config);
484 
485     fuse_session_unmount(se);
486     LOGI("fuse_session_unmount");
487     if (se->mountpoint) {
488         free(se->mountpoint);
489         se->mountpoint = nullptr;
490     }
491 
492     fuse_session_destroy(se);
493     close(devFd);
494     return ret;
495 }
496 
GetInstance()497 FuseManager &FuseManager::GetInstance()
498 {
499     static FuseManager instance_;
500     return instance_;
501 }
502 
503 } // namespace CloudFile
504 } // namespace FileManagement
505 } // namespace OHOS
506