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
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 if (ctx == nullptr) {
48 MEDIA_ERR_LOG("get file context failed");
49 return -ENOENT;
50 }
51 DfxTimer dfxTimer(
52 DfxType::FUSE_OPEN, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), OPEN_FILE_TIME_OUT, true);
53 dfxTimer.SetCallerUid(ctx->uid);
54
55 int32_t err = MediaFuseManager::GetInstance().DoOpen(path, fi->flags, fd);
56 CHECK_AND_RETURN_RET_LOG(err == 0, err, "Open file failed, path = %{private}s", path);
57
58 fi->fh = static_cast<uint64_t>(fd);
59 return 0;
60 }
61
Read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)62 static int Read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
63 {
64 fuse_context *ctx = fuse_get_context();
65
66 if (ctx == nullptr) {
67 MEDIA_ERR_LOG("get file context failed");
68 return -ENOENT;
69 }
70 DfxTimer dfxTimer(
71 DfxType::FUSE_READ, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
72 dfxTimer.SetCallerUid(ctx->uid);
73
74 int res = pread(fi->fh, buf, size, offset);
75 CHECK_AND_RETURN_RET_LOG(res != -1, -errno, "Read file failed, errno = %{public}d", errno);
76
77 return res;
78 }
79
Write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)80 static int Write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
81 {
82 fuse_context *ctx = fuse_get_context();
83
84 if (ctx == nullptr) {
85 MEDIA_ERR_LOG("get file context failed");
86 return -ENOENT;
87 }
88 DfxTimer dfxTimer(
89 DfxType::FUSE_WRITE, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
90 dfxTimer.SetCallerUid(ctx->uid);
91
92 int res = pwrite(fi->fh, buf, size, offset);
93 CHECK_AND_RETURN_RET_LOG(res != -1, -errno, "Read file failed, errno = %{public}d", errno);
94
95 return res;
96 }
97
Release(const char * path,struct fuse_file_info * fi)98 static int Release(const char *path, struct fuse_file_info *fi)
99 {
100 fuse_context *ctx = fuse_get_context();
101
102 if (ctx == nullptr) {
103 MEDIA_ERR_LOG("get file context failed");
104 return -ENOENT;
105 }
106 DfxTimer dfxTimer(
107 DfxType::FUSE_RELEASE, static_cast<int32_t>(OperationObject::FILESYSTEM_PHOTO), COMMON_TIME_OUT, true);
108 dfxTimer.SetCallerUid(ctx->uid);
109
110 int32_t err = MediaFuseManager::GetInstance().DoRelease(path, fi->fh);
111 return err;
112 }
113
114 static const struct fuse_operations high_ops = {
115 .getattr = GetAttr,
116 .open = Open,
117 .read = Read,
118 .write = Write,
119 .release = Release,
120 };
121
StartFuse()122 int32_t MediaFuseDaemon::StartFuse()
123 {
124 int ret = E_OK;
125
126 bool expect = false;
127 CHECK_AND_RETURN_RET_LOG(isRunning_.compare_exchange_strong(expect, true), E_FAIL,
128 "Fuse daemon is already running");
129
130 std::thread([this]() {
131 DaemonThread();
132 }).detach();
133
134 return ret;
135 }
136
DaemonThread()137 void MediaFuseDaemon::DaemonThread()
138 {
139 struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
140 struct fuse *fuse_default = nullptr;
141 struct fuse_loop_config *loop_config = nullptr;
142 string name("mediaFuseDaemon");
143 pthread_setname_np(pthread_self(), name.c_str());
144 do {
145 if (fuse_opt_add_arg(&args, "-odebug")) {
146 MEDIA_ERR_LOG("fuse_opt_add_arg failed");
147 break;
148 }
149
150 fuse_set_log_func([](enum fuse_log_level level, const char *fmt, va_list ap) {
151 char *str = nullptr;
152 if (vasprintf(&str, fmt, ap) < 0) {
153 MEDIA_ERR_LOG("FUSE: log failed");
154 return;
155 }
156
157 MEDIA_ERR_LOG("FUSE: %{public}s", str);
158 free(str);
159 });
160
161 fuse_default = fuse_new(&args, &high_ops, sizeof(high_ops), nullptr);
162 if (fuse_default == nullptr) {
163 MEDIA_ERR_LOG("fuse_new failed");
164 break;
165 }
166
167 if (fuse_mount(fuse_default, mountpoint_.c_str()) != 0) {
168 MEDIA_ERR_LOG("fuse_mount failed, mountpoint_ = %{private}s", mountpoint_.c_str());
169 break;
170 }
171
172 loop_config = fuse_loop_cfg_create();
173 if (loop_config == nullptr) {
174 MEDIA_ERR_LOG("fuse_loop_cfg_create failed");
175 break;
176 }
177 fuse_loop_cfg_set_max_threads(loop_config, FUSE_CFG_MAX_THREADS);
178
179 MEDIA_INFO_LOG("Starting fuse ...");
180 fuse_loop_mt(fuse_default, loop_config);
181 MEDIA_INFO_LOG("Ending fuse ...");
182 } while (false);
183
184 fuse_opt_free_args(&args);
185 if (loop_config) {
186 fuse_loop_cfg_destroy(loop_config);
187 }
188 if (fuse_default) {
189 fuse_unmount(fuse_default);
190 fuse_destroy(fuse_default);
191 }
192 MEDIA_INFO_LOG("Ended fuse");
193 }
194 } // namespace Media
195 } // namespace OHOS
196
197