xref: /spdk/module/blobfs/bdev/blobfs_fuse.c (revision 2f5c602574a98ede645991abe279a96e19c50196)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/log.h"
37 #include "spdk/env.h"
38 #include "spdk/event.h"
39 #include "spdk/thread.h"
40 #include "spdk/string.h"
41 #include "spdk/blobfs.h"
42 
43 #include "blobfs_fuse.h"
44 
45 #define FUSE_USE_VERSION 30
46 #include "fuse3/fuse.h"
47 #include "fuse3/fuse_lowlevel.h"
48 
49 struct spdk_blobfs_fuse {
50 	char *bdev_name;
51 	char *mountpoint;
52 	struct spdk_fs_thread_ctx *channel;
53 	struct spdk_filesystem *fs;
54 
55 	struct fuse *fuse_handle;
56 	pthread_t	fuse_tid;
57 
58 	blobfs_fuse_unmount_cb cb_fn;
59 	void *cb_arg;
60 };
61 
62 /* Each thread serves one blobfs */
63 static __thread struct spdk_blobfs_fuse *thd_bfuse;
64 
65 static void
66 blobfs_fuse_free(struct spdk_blobfs_fuse *bfuse)
67 {
68 	if (bfuse == NULL) {
69 		return;
70 	}
71 
72 	free(bfuse->bdev_name);
73 	free(bfuse->mountpoint);
74 	free(bfuse);
75 }
76 
77 static void
78 __call_fn(void *arg1, void *arg2)
79 {
80 	fs_request_fn fn;
81 
82 	fn = (fs_request_fn)arg1;
83 	fn(arg2);
84 }
85 
86 void
87 blobfs_fuse_send_request(fs_request_fn fn, void *arg)
88 {
89 	struct spdk_event *event;
90 
91 	event = spdk_event_allocate(0, __call_fn, (void *)fn, arg);
92 	spdk_event_call(event);
93 }
94 
95 static int
96 fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
97 {
98 	struct spdk_file_stat stat;
99 	int rc;
100 
101 	if (!strcmp(path, "/")) {
102 		stbuf->st_mode = S_IFDIR | 0755;
103 		stbuf->st_nlink = 2;
104 		return 0;
105 	}
106 
107 	rc = spdk_fs_file_stat(thd_bfuse->fs, thd_bfuse->channel, path, &stat);
108 	if (rc == 0) {
109 		stbuf->st_mode = S_IFREG | 0644;
110 		stbuf->st_nlink = 1;
111 		stbuf->st_size = stat.size;
112 	}
113 
114 	return rc;
115 }
116 
117 static int
118 fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
119 	     off_t offset, struct fuse_file_info *fi,
120 	     enum fuse_readdir_flags flags)
121 {
122 	struct spdk_file *file;
123 	const char *filename;
124 	spdk_fs_iter iter;
125 
126 	filler(buf, ".", NULL, 0, 0);
127 	filler(buf, "..", NULL, 0, 0);
128 
129 	iter = spdk_fs_iter_first(thd_bfuse->fs);
130 	while (iter != NULL) {
131 		file = spdk_fs_iter_get_file(iter);
132 		iter = spdk_fs_iter_next(iter);
133 		filename = spdk_file_get_name(file);
134 		filler(buf, &filename[1], NULL, 0, 0);
135 	}
136 
137 	return 0;
138 }
139 
140 static int
141 fuse_mknod(const char *path, mode_t mode, dev_t rdev)
142 {
143 	return spdk_fs_create_file(thd_bfuse->fs, thd_bfuse->channel, path);
144 }
145 
146 static int
147 fuse_unlink(const char *path)
148 {
149 	return spdk_fs_delete_file(thd_bfuse->fs, thd_bfuse->channel, path);
150 }
151 
152 static int
153 fuse_truncate(const char *path, off_t size, struct fuse_file_info *fi)
154 {
155 	struct spdk_file *file;
156 	int rc;
157 
158 	rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
159 	if (rc != 0) {
160 		return -rc;
161 	}
162 
163 	rc = spdk_file_truncate(file, thd_bfuse->channel, size);
164 	if (rc != 0) {
165 		return -rc;
166 	}
167 
168 	spdk_file_close(file, thd_bfuse->channel);
169 
170 	return 0;
171 }
172 
173 static int
174 fuse_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
175 {
176 	return 0;
177 }
178 
179 static int
180 fuse_open(const char *path, struct fuse_file_info *info)
181 {
182 	struct spdk_file *file;
183 	int rc;
184 
185 	rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
186 	if (rc != 0) {
187 		return -rc;
188 	}
189 
190 	info->fh = (uintptr_t)file;
191 	return 0;
192 }
193 
194 static int
195 fuse_release(const char *path, struct fuse_file_info *info)
196 {
197 	struct spdk_file *file = (struct spdk_file *)info->fh;
198 
199 	return spdk_file_close(file, thd_bfuse->channel);
200 }
201 
202 static int
203 fuse_read(const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info)
204 {
205 	struct spdk_file *file = (struct spdk_file *)info->fh;
206 
207 	return spdk_file_read(file, thd_bfuse->channel, buf, offset, len);
208 }
209 
210 static int
211 fuse_write(const char *path, const char *buf, size_t len, off_t offset,
212 	   struct fuse_file_info *info)
213 {
214 	struct spdk_file *file = (struct spdk_file *)info->fh;
215 	int rc;
216 
217 	rc = spdk_file_write(file, thd_bfuse->channel, (void *)buf, offset, len);
218 	if (rc == 0) {
219 		return len;
220 	} else {
221 		return rc;
222 	}
223 }
224 
225 static int
226 fuse_flush(const char *path, struct fuse_file_info *info)
227 {
228 	return 0;
229 }
230 
231 static int
232 fuse_fsync(const char *path, int datasync, struct fuse_file_info *info)
233 {
234 	return 0;
235 }
236 
237 static int
238 fuse_rename(const char *old_path, const char *new_path, unsigned int flags)
239 {
240 	return spdk_fs_rename_file(thd_bfuse->fs, thd_bfuse->channel, old_path, new_path);
241 }
242 
243 static struct fuse_operations spdk_fuse_oper = {
244 	.getattr	= fuse_getattr,
245 	.readdir	= fuse_readdir,
246 	.mknod		= fuse_mknod,
247 	.unlink		= fuse_unlink,
248 	.truncate	= fuse_truncate,
249 	.utimens	= fuse_utimens,
250 	.open		= fuse_open,
251 	.release	= fuse_release,
252 	.read		= fuse_read,
253 	.write		= fuse_write,
254 	.flush		= fuse_flush,
255 	.fsync		= fuse_fsync,
256 	.rename		= fuse_rename,
257 };
258 
259 static void *
260 fuse_loop_new_thread(void *arg)
261 {
262 	struct spdk_blobfs_fuse *bfuse = arg;
263 
264 	spdk_unaffinitize_thread();
265 
266 	thd_bfuse = bfuse;
267 	SPDK_NOTICELOG("Start to loop blobfs on bdev %s mounted at %s\n", bfuse->bdev_name,
268 		       bfuse->mountpoint);
269 
270 	bfuse->channel = spdk_fs_alloc_thread_ctx(bfuse->fs);
271 
272 	fuse_loop(bfuse->fuse_handle);
273 	fuse_unmount(bfuse->fuse_handle);
274 	fuse_destroy(bfuse->fuse_handle);
275 	SPDK_NOTICELOG("Blobfs on bdev %s unmounted from %s\n", bfuse->bdev_name, bfuse->mountpoint);
276 
277 	spdk_fs_free_thread_ctx(bfuse->channel);
278 
279 	bfuse->cb_fn(bfuse->cb_arg);
280 
281 	blobfs_fuse_free(bfuse);
282 
283 	pthread_exit(NULL);
284 }
285 
286 int
287 blobfs_fuse_start(const char *bdev_name, const char *mountpoint, struct spdk_filesystem *fs,
288 		  blobfs_fuse_unmount_cb cb_fn, void *cb_arg, struct spdk_blobfs_fuse **_bfuse)
289 {
290 	/* Set argv[1] as bdev_name in order to show bdev_name as the mounting source */
291 	char *argv[1] = {(char *)bdev_name};
292 	struct fuse_args args = FUSE_ARGS_INIT(1, argv);
293 	struct fuse_cmdline_opts opts = {};
294 	struct fuse *fuse_handle;
295 	struct spdk_blobfs_fuse *bfuse;
296 	pthread_t tid;
297 	int rc;
298 
299 	bfuse = (struct spdk_blobfs_fuse *)calloc(1, sizeof(*bfuse));
300 	if (bfuse == NULL) {
301 		return -ENOMEM;
302 	}
303 
304 	bfuse->bdev_name = strdup(bdev_name);
305 	bfuse->mountpoint = strdup(mountpoint);
306 	if (!bfuse->bdev_name || !bfuse->mountpoint) {
307 		rc = -ENOMEM;
308 		goto err;
309 	}
310 	bfuse->fs = fs;
311 	bfuse->cb_fn = cb_fn;
312 	bfuse->cb_arg = cb_arg;
313 
314 	rc = fuse_parse_cmdline(&args, &opts);
315 	assert(rc == 0);
316 
317 	fuse_handle = fuse_new(&args, &spdk_fuse_oper, sizeof(spdk_fuse_oper), NULL);
318 	fuse_opt_free_args(&args);
319 	if (fuse_handle == NULL) {
320 		SPDK_ERRLOG("could not create fuse handle!\n");
321 		rc = -1;
322 		goto err;
323 	}
324 	bfuse->fuse_handle = fuse_handle;
325 
326 	rc = fuse_mount(bfuse->fuse_handle, bfuse->mountpoint);
327 	if (rc != 0) {
328 		SPDK_ERRLOG("could not mount fuse handle\n");
329 		rc = -1;
330 		goto err;
331 	}
332 
333 	rc = pthread_create(&tid, NULL, fuse_loop_new_thread, bfuse);
334 	if (rc != 0) {
335 		SPDK_ERRLOG("could not create thread: %s\n", spdk_strerror(rc));
336 		rc = -rc;
337 		goto err;
338 	}
339 	bfuse->fuse_tid = tid;
340 
341 	rc = pthread_detach(tid);
342 	if (rc != 0) {
343 		SPDK_ERRLOG("could not detach thread for fuse loop thread: %s\n", spdk_strerror(rc));
344 		rc = -rc;
345 		goto err;
346 	}
347 
348 	*_bfuse = bfuse;
349 	return 0;
350 
351 err:
352 	blobfs_fuse_free(bfuse);
353 
354 	return rc;
355 }
356 
357 void
358 blobfs_fuse_stop(struct spdk_blobfs_fuse *bfuse)
359 {
360 	if (bfuse) {
361 		fuse_session_exit(fuse_get_session(bfuse->fuse_handle));
362 		pthread_kill(bfuse->fuse_tid, SIGINT);
363 	}
364 }
365