1 /*
2 * Copyright (C) 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 #define MLOG_TAG "MediaFuseDaemon"
17 #include "media_fuse_daemon.h"
18
19 #include <fcntl.h>
20 #define FUSE_USE_VERSION 34
21 #include <fuse.h>
22 #include <thread>
23 #include <unistd.h>
24
25 #include "dfx_const.h"
26 #include "dfx_timer.h"
27 #include "media_log.h"
28 #include "medialibrary_errno.h"
29 #include "medialibrary_operation.h"
30 #include "media_fuse_manager.h"
31
32 namespace OHOS {
33 namespace Media {
34 using namespace std;
35
36 static constexpr int32_t FUSE_CFG_MAX_THREADS = 5;
37 // LCOV_EXCL_START
GetAttr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)38 static int GetAttr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
39 {
40 return MediaFuseManager::GetInstance().DoGetAttr(path, stbuf);
41 }
42
Open(const char * path,struct fuse_file_info * fi)43 static int Open(const char *path, struct fuse_file_info *fi)
44 {
45 int fd = -1;
46 fuse_context *ctx = fuse_get_context();
47 CHECK_AND_RETURN_RET_LOG(ctx != nullptr, -ENOENT, "get file context failed");
48 DfxTimer dfxTimer(
49 DfxType::FUSE_OPEN, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), OPEN_FILE_TIME_OUT, true);
50 dfxTimer.SetCallerUid(ctx->uid);
51
52 int32_t err = MediaFuseManager::GetInstance().DoOpen(path, fi->flags, fd);
53 CHECK_AND_RETURN_RET_LOG(err == 0, err, "Open file failed, path = %{private}s", path);
54
55 fi->fh = static_cast<uint64_t>(fd);
56 return 0;
57 }
58
Read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)59 static int Read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
60 {
61 fuse_context *ctx = fuse_get_context();
62 CHECK_AND_RETURN_RET_LOG(ctx != nullptr, -ENOENT, "get file context failed");
63 DfxTimer dfxTimer(
64 DfxType::FUSE_READ, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
65 dfxTimer.SetCallerUid(ctx->uid);
66
67 int res = pread(fi->fh, buf, size, offset);
68 CHECK_AND_RETURN_RET_LOG(res != -1, -errno, "Read file failed, errno = %{public}d", errno);
69
70 return res;
71 }
72
Write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)73 static int Write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
74 {
75 fuse_context *ctx = fuse_get_context();
76 CHECK_AND_RETURN_RET_LOG(ctx != nullptr, -ENOENT, "get file context failed");
77 DfxTimer dfxTimer(
78 DfxType::FUSE_WRITE, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
79 dfxTimer.SetCallerUid(ctx->uid);
80
81 int res = pwrite(fi->fh, buf, size, offset);
82 CHECK_AND_RETURN_RET_LOG(res != -1, -errno, "Read file failed, errno = %{public}d", errno);
83
84 return res;
85 }
86
Release(const char * path,struct fuse_file_info * fi)87 static int Release(const char *path, struct fuse_file_info *fi)
88 {
89 fuse_context *ctx = fuse_get_context();
90 CHECK_AND_RETURN_RET_LOG(ctx != nullptr, -ENOENT, "get file context failed");
91 DfxTimer dfxTimer(
92 DfxType::FUSE_RELEASE, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
93 dfxTimer.SetCallerUid(ctx->uid);
94
95 int32_t err = MediaFuseManager::GetInstance().DoRelease(path, fi->fh);
96 return err;
97 }
98
99 static const struct fuse_operations high_ops = {
100 .getattr = GetAttr,
101 .open = Open,
102 .read = Read,
103 .write = Write,
104 .release = Release,
105 };
106
StartFuse()107 int32_t MediaFuseDaemon::StartFuse()
108 {
109 int ret = E_OK;
110
111 bool expect = false;
112 CHECK_AND_RETURN_RET_LOG(isRunning_.compare_exchange_strong(expect, true), E_FAIL,
113 "Fuse daemon is already running");
114
115 std::thread([this]() {
116 DaemonThread();
117 }).detach();
118
119 return ret;
120 }
121
DaemonThread()122 void MediaFuseDaemon::DaemonThread()
123 {
124 struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
125 struct fuse *fuse_default = nullptr;
126 struct fuse_loop_config *loop_config = nullptr;
127 string name("mediaFuseDaemon");
128 pthread_setname_np(pthread_self(), name.c_str());
129 do {
130 CHECK_AND_BREAK_ERR_LOG(!fuse_opt_add_arg(&args, "-odebug"), "fuse_opt_add_arg failed");
131 fuse_set_log_func([](enum fuse_log_level level, const char *fmt, va_list ap) {
132 char *str = nullptr;
133 CHECK_AND_RETURN_LOG(vasprintf(&str, fmt, ap) >= 0, "FUSE: log failed");
134 MEDIA_ERR_LOG("FUSE: %{public}s", str);
135 free(str);
136 });
137
138 fuse_default = fuse_new(&args, &high_ops, sizeof(high_ops), nullptr);
139 CHECK_AND_BREAK_ERR_LOG(fuse_default != nullptr, "fuse_new failed");
140 CHECK_AND_BREAK_ERR_LOG(fuse_mount(fuse_default, mountpoint_.c_str()) == 0,
141 "fuse_mount failed, mountpoint_ = %{private}s", mountpoint_.c_str());
142
143 loop_config = fuse_loop_cfg_create();
144 CHECK_AND_BREAK_ERR_LOG(loop_config != nullptr, "fuse_loop_cfg_create failed");
145 fuse_loop_cfg_set_max_threads(loop_config, FUSE_CFG_MAX_THREADS);
146 MEDIA_INFO_LOG("Starting fuse ...");
147 fuse_loop_mt(fuse_default, loop_config);
148 MEDIA_INFO_LOG("Ending fuse ...");
149 } while (false);
150
151 fuse_opt_free_args(&args);
152 if (loop_config) {
153 fuse_loop_cfg_destroy(loop_config);
154 }
155 if (fuse_default) {
156 fuse_unmount(fuse_default);
157 fuse_destroy(fuse_default);
158 }
159 MEDIA_INFO_LOG("Ended fuse");
160 }
161 // LCOV_EXCL_STOP
162 } // namespace Media
163 } // namespace OHOS
164
165