1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2019 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk/stdinc.h"
7
8 #include "spdk/log.h"
9 #include "spdk/env.h"
10 #include "spdk/event.h"
11 #include "spdk/thread.h"
12 #include "spdk/string.h"
13 #include "spdk/blobfs.h"
14
15 #include "blobfs_fuse.h"
16
17 #define FUSE_USE_VERSION 30
18 #include "fuse3/fuse.h"
19 #include "fuse3/fuse_lowlevel.h"
20
21 struct spdk_blobfs_fuse {
22 char *bdev_name;
23 char *mountpoint;
24 struct spdk_fs_thread_ctx *channel;
25 struct spdk_filesystem *fs;
26
27 struct fuse *fuse_handle;
28 pthread_t fuse_tid;
29
30 blobfs_fuse_unmount_cb cb_fn;
31 void *cb_arg;
32 };
33
34 /* Each thread serves one blobfs */
35 static __thread struct spdk_blobfs_fuse *thd_bfuse;
36
37 static void
blobfs_fuse_free(struct spdk_blobfs_fuse * bfuse)38 blobfs_fuse_free(struct spdk_blobfs_fuse *bfuse)
39 {
40 if (bfuse == NULL) {
41 return;
42 }
43
44 free(bfuse->bdev_name);
45 free(bfuse->mountpoint);
46 free(bfuse);
47 }
48
49 void
blobfs_fuse_send_request(fs_request_fn fn,void * arg)50 blobfs_fuse_send_request(fs_request_fn fn, void *arg)
51 {
52 spdk_thread_send_msg(spdk_thread_get_app_thread(), fn, arg);
53 }
54
55 static int
fuse_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)56 fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
57 {
58 struct spdk_file_stat stat;
59 int rc;
60
61 if (!strcmp(path, "/")) {
62 stbuf->st_mode = S_IFDIR | 0755;
63 stbuf->st_nlink = 2;
64 return 0;
65 }
66
67 rc = spdk_fs_file_stat(thd_bfuse->fs, thd_bfuse->channel, path, &stat);
68 if (rc == 0) {
69 stbuf->st_mode = S_IFREG | 0644;
70 stbuf->st_nlink = 1;
71 stbuf->st_size = stat.size;
72 }
73
74 return rc;
75 }
76
77 static int
fuse_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)78 fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
79 off_t offset, struct fuse_file_info *fi,
80 enum fuse_readdir_flags flags)
81 {
82 struct spdk_file *file;
83 const char *filename;
84 spdk_fs_iter iter;
85
86 filler(buf, ".", NULL, 0, 0);
87 filler(buf, "..", NULL, 0, 0);
88
89 iter = spdk_fs_iter_first(thd_bfuse->fs);
90 while (iter != NULL) {
91 file = spdk_fs_iter_get_file(iter);
92 iter = spdk_fs_iter_next(iter);
93 filename = spdk_file_get_name(file);
94 filler(buf, &filename[1], NULL, 0, 0);
95 }
96
97 return 0;
98 }
99
100 static int
fuse_mknod(const char * path,mode_t mode,dev_t rdev)101 fuse_mknod(const char *path, mode_t mode, dev_t rdev)
102 {
103 return spdk_fs_create_file(thd_bfuse->fs, thd_bfuse->channel, path);
104 }
105
106 static int
fuse_unlink(const char * path)107 fuse_unlink(const char *path)
108 {
109 return spdk_fs_delete_file(thd_bfuse->fs, thd_bfuse->channel, path);
110 }
111
112 static int
fuse_truncate(const char * path,off_t size,struct fuse_file_info * fi)113 fuse_truncate(const char *path, off_t size, struct fuse_file_info *fi)
114 {
115 struct spdk_file *file;
116 int rc;
117
118 rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
119 if (rc != 0) {
120 return -rc;
121 }
122
123 rc = spdk_file_truncate(file, thd_bfuse->channel, size);
124 if (rc != 0) {
125 return -rc;
126 }
127
128 spdk_file_close(file, thd_bfuse->channel);
129
130 return 0;
131 }
132
133 static int
fuse_utimens(const char * path,const struct timespec tv[2],struct fuse_file_info * fi)134 fuse_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
135 {
136 return 0;
137 }
138
139 static int
fuse_open(const char * path,struct fuse_file_info * info)140 fuse_open(const char *path, struct fuse_file_info *info)
141 {
142 struct spdk_file *file;
143 int rc;
144
145 rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
146 if (rc != 0) {
147 return -rc;
148 }
149
150 info->fh = (uintptr_t)file;
151 return 0;
152 }
153
154 static int
fuse_release(const char * path,struct fuse_file_info * info)155 fuse_release(const char *path, struct fuse_file_info *info)
156 {
157 struct spdk_file *file = (struct spdk_file *)info->fh;
158
159 return spdk_file_close(file, thd_bfuse->channel);
160 }
161
162 static int
fuse_read(const char * path,char * buf,size_t len,off_t offset,struct fuse_file_info * info)163 fuse_read(const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info)
164 {
165 struct spdk_file *file = (struct spdk_file *)info->fh;
166
167 return spdk_file_read(file, thd_bfuse->channel, buf, offset, len);
168 }
169
170 static int
fuse_write(const char * path,const char * buf,size_t len,off_t offset,struct fuse_file_info * info)171 fuse_write(const char *path, const char *buf, size_t len, off_t offset,
172 struct fuse_file_info *info)
173 {
174 struct spdk_file *file = (struct spdk_file *)info->fh;
175 int rc;
176
177 rc = spdk_file_write(file, thd_bfuse->channel, (void *)buf, offset, len);
178 if (rc == 0) {
179 return len;
180 } else {
181 return rc;
182 }
183 }
184
185 static int
fuse_flush(const char * path,struct fuse_file_info * info)186 fuse_flush(const char *path, struct fuse_file_info *info)
187 {
188 return 0;
189 }
190
191 static int
fuse_fsync(const char * path,int datasync,struct fuse_file_info * info)192 fuse_fsync(const char *path, int datasync, struct fuse_file_info *info)
193 {
194 return 0;
195 }
196
197 static int
fuse_rename(const char * old_path,const char * new_path,unsigned int flags)198 fuse_rename(const char *old_path, const char *new_path, unsigned int flags)
199 {
200 return spdk_fs_rename_file(thd_bfuse->fs, thd_bfuse->channel, old_path, new_path);
201 }
202
203 static struct fuse_operations spdk_fuse_oper = {
204 .getattr = fuse_getattr,
205 .readdir = fuse_readdir,
206 .mknod = fuse_mknod,
207 .unlink = fuse_unlink,
208 .truncate = fuse_truncate,
209 .utimens = fuse_utimens,
210 .open = fuse_open,
211 .release = fuse_release,
212 .read = fuse_read,
213 .write = fuse_write,
214 .flush = fuse_flush,
215 .fsync = fuse_fsync,
216 .rename = fuse_rename,
217 };
218
219 static void *
fuse_loop_new_thread(void * arg)220 fuse_loop_new_thread(void *arg)
221 {
222 struct spdk_blobfs_fuse *bfuse = arg;
223
224 spdk_unaffinitize_thread();
225
226 thd_bfuse = bfuse;
227 SPDK_NOTICELOG("Start to loop blobfs on bdev %s mounted at %s\n", bfuse->bdev_name,
228 bfuse->mountpoint);
229
230 bfuse->channel = spdk_fs_alloc_thread_ctx(bfuse->fs);
231
232 fuse_loop(bfuse->fuse_handle);
233 fuse_unmount(bfuse->fuse_handle);
234 fuse_destroy(bfuse->fuse_handle);
235 SPDK_NOTICELOG("Blobfs on bdev %s unmounted from %s\n", bfuse->bdev_name, bfuse->mountpoint);
236
237 spdk_fs_free_thread_ctx(bfuse->channel);
238
239 bfuse->cb_fn(bfuse->cb_arg);
240
241 blobfs_fuse_free(bfuse);
242
243 pthread_exit(NULL);
244 }
245
246 int
blobfs_fuse_start(const char * bdev_name,const char * mountpoint,struct spdk_filesystem * fs,blobfs_fuse_unmount_cb cb_fn,void * cb_arg,struct spdk_blobfs_fuse ** _bfuse)247 blobfs_fuse_start(const char *bdev_name, const char *mountpoint, struct spdk_filesystem *fs,
248 blobfs_fuse_unmount_cb cb_fn, void *cb_arg, struct spdk_blobfs_fuse **_bfuse)
249 {
250 /* Set argv[1] as bdev_name in order to show bdev_name as the mounting source */
251 char *argv[1] = {(char *)bdev_name};
252 struct fuse_args args = FUSE_ARGS_INIT(1, argv);
253 struct fuse_cmdline_opts opts = {};
254 struct fuse *fuse_handle;
255 struct spdk_blobfs_fuse *bfuse;
256 pthread_t tid;
257 int rc;
258
259 bfuse = (struct spdk_blobfs_fuse *)calloc(1, sizeof(*bfuse));
260 if (bfuse == NULL) {
261 return -ENOMEM;
262 }
263
264 bfuse->bdev_name = strdup(bdev_name);
265 bfuse->mountpoint = strdup(mountpoint);
266 if (!bfuse->bdev_name || !bfuse->mountpoint) {
267 rc = -ENOMEM;
268 goto err;
269 }
270 bfuse->fs = fs;
271 bfuse->cb_fn = cb_fn;
272 bfuse->cb_arg = cb_arg;
273
274 rc = fuse_parse_cmdline(&args, &opts);
275 assert(rc == 0);
276
277 fuse_handle = fuse_new(&args, &spdk_fuse_oper, sizeof(spdk_fuse_oper), NULL);
278 fuse_opt_free_args(&args);
279 if (fuse_handle == NULL) {
280 SPDK_ERRLOG("could not create fuse handle!\n");
281 rc = -1;
282 goto err;
283 }
284 bfuse->fuse_handle = fuse_handle;
285
286 rc = fuse_mount(bfuse->fuse_handle, bfuse->mountpoint);
287 if (rc != 0) {
288 SPDK_ERRLOG("could not mount fuse handle\n");
289 rc = -1;
290 goto err;
291 }
292
293 rc = pthread_create(&tid, NULL, fuse_loop_new_thread, bfuse);
294 if (rc != 0) {
295 SPDK_ERRLOG("could not create thread: %s\n", spdk_strerror(rc));
296 rc = -rc;
297 goto err;
298 }
299 bfuse->fuse_tid = tid;
300
301 rc = pthread_detach(tid);
302 if (rc != 0) {
303 SPDK_ERRLOG("could not detach thread for fuse loop thread: %s\n", spdk_strerror(rc));
304 rc = -rc;
305 goto err;
306 }
307
308 *_bfuse = bfuse;
309 return 0;
310
311 err:
312 blobfs_fuse_free(bfuse);
313
314 return rc;
315 }
316
317 void
blobfs_fuse_stop(struct spdk_blobfs_fuse * bfuse)318 blobfs_fuse_stop(struct spdk_blobfs_fuse *bfuse)
319 {
320 if (bfuse) {
321 fuse_session_exit(fuse_get_session(bfuse->fuse_handle));
322 pthread_kill(bfuse->fuse_tid, SIGINT);
323 }
324 }
325