• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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