xref: /spdk/module/blobfs/bdev/blobfs_fuse.c (revision 9934e41498980fc63c8963d25f32dab7b6192e9c)
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