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