xref: /spdk/module/blobfs/bdev/blobfs_fuse.c (revision 9934e41498980fc63c8963d25f32dab7b6192e9c)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2019 Intel Corporation.
34c10e0bbSXiaodong Liu  *   All rights reserved.
44c10e0bbSXiaodong Liu  */
54c10e0bbSXiaodong Liu 
64c10e0bbSXiaodong Liu #include "spdk/stdinc.h"
74c10e0bbSXiaodong Liu 
84c10e0bbSXiaodong Liu #include "spdk/log.h"
94c10e0bbSXiaodong Liu #include "spdk/env.h"
104c10e0bbSXiaodong Liu #include "spdk/event.h"
114c10e0bbSXiaodong Liu #include "spdk/thread.h"
124c10e0bbSXiaodong Liu #include "spdk/string.h"
134c10e0bbSXiaodong Liu #include "spdk/blobfs.h"
144c10e0bbSXiaodong Liu 
154c10e0bbSXiaodong Liu #include "blobfs_fuse.h"
164c10e0bbSXiaodong Liu 
174c10e0bbSXiaodong Liu #define FUSE_USE_VERSION 30
184c10e0bbSXiaodong Liu #include "fuse3/fuse.h"
194c10e0bbSXiaodong Liu #include "fuse3/fuse_lowlevel.h"
204c10e0bbSXiaodong Liu 
214c10e0bbSXiaodong Liu struct spdk_blobfs_fuse {
224c10e0bbSXiaodong Liu 	char *bdev_name;
234c10e0bbSXiaodong Liu 	char *mountpoint;
244c10e0bbSXiaodong Liu 	struct spdk_fs_thread_ctx *channel;
254c10e0bbSXiaodong Liu 	struct spdk_filesystem *fs;
264c10e0bbSXiaodong Liu 
274c10e0bbSXiaodong Liu 	struct fuse *fuse_handle;
284c10e0bbSXiaodong Liu 	pthread_t	fuse_tid;
294c10e0bbSXiaodong Liu 
304c10e0bbSXiaodong Liu 	blobfs_fuse_unmount_cb cb_fn;
314c10e0bbSXiaodong Liu 	void *cb_arg;
324c10e0bbSXiaodong Liu };
334c10e0bbSXiaodong Liu 
344c10e0bbSXiaodong Liu /* Each thread serves one blobfs */
354c10e0bbSXiaodong Liu static __thread struct spdk_blobfs_fuse *thd_bfuse;
364c10e0bbSXiaodong Liu 
374c10e0bbSXiaodong Liu static void
blobfs_fuse_free(struct spdk_blobfs_fuse * bfuse)384c10e0bbSXiaodong Liu blobfs_fuse_free(struct spdk_blobfs_fuse *bfuse)
394c10e0bbSXiaodong Liu {
404c10e0bbSXiaodong Liu 	if (bfuse == NULL) {
414c10e0bbSXiaodong Liu 		return;
424c10e0bbSXiaodong Liu 	}
434c10e0bbSXiaodong Liu 
444c10e0bbSXiaodong Liu 	free(bfuse->bdev_name);
454c10e0bbSXiaodong Liu 	free(bfuse->mountpoint);
464c10e0bbSXiaodong Liu 	free(bfuse);
474c10e0bbSXiaodong Liu }
484c10e0bbSXiaodong Liu 
494c10e0bbSXiaodong Liu void
blobfs_fuse_send_request(fs_request_fn fn,void * arg)5030e3f4d9SSeth Howell blobfs_fuse_send_request(fs_request_fn fn, void *arg)
514c10e0bbSXiaodong Liu {
52*9934e414SJim Harris 	spdk_thread_send_msg(spdk_thread_get_app_thread(), fn, arg);
534c10e0bbSXiaodong Liu }
544c10e0bbSXiaodong Liu 
554c10e0bbSXiaodong Liu static int
fuse_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)5630e3f4d9SSeth Howell fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
574c10e0bbSXiaodong Liu {
584c10e0bbSXiaodong Liu 	struct spdk_file_stat stat;
594c10e0bbSXiaodong Liu 	int rc;
604c10e0bbSXiaodong Liu 
614c10e0bbSXiaodong Liu 	if (!strcmp(path, "/")) {
624c10e0bbSXiaodong Liu 		stbuf->st_mode = S_IFDIR | 0755;
634c10e0bbSXiaodong Liu 		stbuf->st_nlink = 2;
644c10e0bbSXiaodong Liu 		return 0;
654c10e0bbSXiaodong Liu 	}
664c10e0bbSXiaodong Liu 
674c10e0bbSXiaodong Liu 	rc = spdk_fs_file_stat(thd_bfuse->fs, thd_bfuse->channel, path, &stat);
684c10e0bbSXiaodong Liu 	if (rc == 0) {
694c10e0bbSXiaodong Liu 		stbuf->st_mode = S_IFREG | 0644;
704c10e0bbSXiaodong Liu 		stbuf->st_nlink = 1;
714c10e0bbSXiaodong Liu 		stbuf->st_size = stat.size;
724c10e0bbSXiaodong Liu 	}
734c10e0bbSXiaodong Liu 
744c10e0bbSXiaodong Liu 	return rc;
754c10e0bbSXiaodong Liu }
764c10e0bbSXiaodong Liu 
774c10e0bbSXiaodong Liu 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)7830e3f4d9SSeth Howell fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
794c10e0bbSXiaodong Liu 	     off_t offset, struct fuse_file_info *fi,
804c10e0bbSXiaodong Liu 	     enum fuse_readdir_flags flags)
814c10e0bbSXiaodong Liu {
824c10e0bbSXiaodong Liu 	struct spdk_file *file;
834c10e0bbSXiaodong Liu 	const char *filename;
844c10e0bbSXiaodong Liu 	spdk_fs_iter iter;
854c10e0bbSXiaodong Liu 
864c10e0bbSXiaodong Liu 	filler(buf, ".", NULL, 0, 0);
874c10e0bbSXiaodong Liu 	filler(buf, "..", NULL, 0, 0);
884c10e0bbSXiaodong Liu 
894c10e0bbSXiaodong Liu 	iter = spdk_fs_iter_first(thd_bfuse->fs);
904c10e0bbSXiaodong Liu 	while (iter != NULL) {
914c10e0bbSXiaodong Liu 		file = spdk_fs_iter_get_file(iter);
924c10e0bbSXiaodong Liu 		iter = spdk_fs_iter_next(iter);
934c10e0bbSXiaodong Liu 		filename = spdk_file_get_name(file);
944c10e0bbSXiaodong Liu 		filler(buf, &filename[1], NULL, 0, 0);
954c10e0bbSXiaodong Liu 	}
964c10e0bbSXiaodong Liu 
974c10e0bbSXiaodong Liu 	return 0;
984c10e0bbSXiaodong Liu }
994c10e0bbSXiaodong Liu 
1004c10e0bbSXiaodong Liu static int
fuse_mknod(const char * path,mode_t mode,dev_t rdev)10130e3f4d9SSeth Howell fuse_mknod(const char *path, mode_t mode, dev_t rdev)
1024c10e0bbSXiaodong Liu {
1034c10e0bbSXiaodong Liu 	return spdk_fs_create_file(thd_bfuse->fs, thd_bfuse->channel, path);
1044c10e0bbSXiaodong Liu }
1054c10e0bbSXiaodong Liu 
1064c10e0bbSXiaodong Liu static int
fuse_unlink(const char * path)10730e3f4d9SSeth Howell fuse_unlink(const char *path)
1084c10e0bbSXiaodong Liu {
1094c10e0bbSXiaodong Liu 	return spdk_fs_delete_file(thd_bfuse->fs, thd_bfuse->channel, path);
1104c10e0bbSXiaodong Liu }
1114c10e0bbSXiaodong Liu 
1124c10e0bbSXiaodong Liu static int
fuse_truncate(const char * path,off_t size,struct fuse_file_info * fi)11330e3f4d9SSeth Howell fuse_truncate(const char *path, off_t size, struct fuse_file_info *fi)
1144c10e0bbSXiaodong Liu {
1154c10e0bbSXiaodong Liu 	struct spdk_file *file;
1164c10e0bbSXiaodong Liu 	int rc;
1174c10e0bbSXiaodong Liu 
1184c10e0bbSXiaodong Liu 	rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
1194c10e0bbSXiaodong Liu 	if (rc != 0) {
1204c10e0bbSXiaodong Liu 		return -rc;
1214c10e0bbSXiaodong Liu 	}
1224c10e0bbSXiaodong Liu 
1234c10e0bbSXiaodong Liu 	rc = spdk_file_truncate(file, thd_bfuse->channel, size);
1244c10e0bbSXiaodong Liu 	if (rc != 0) {
1254c10e0bbSXiaodong Liu 		return -rc;
1264c10e0bbSXiaodong Liu 	}
1274c10e0bbSXiaodong Liu 
1284c10e0bbSXiaodong Liu 	spdk_file_close(file, thd_bfuse->channel);
1294c10e0bbSXiaodong Liu 
1304c10e0bbSXiaodong Liu 	return 0;
1314c10e0bbSXiaodong Liu }
1324c10e0bbSXiaodong Liu 
1334c10e0bbSXiaodong Liu static int
fuse_utimens(const char * path,const struct timespec tv[2],struct fuse_file_info * fi)13430e3f4d9SSeth Howell fuse_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
1354c10e0bbSXiaodong Liu {
1364c10e0bbSXiaodong Liu 	return 0;
1374c10e0bbSXiaodong Liu }
1384c10e0bbSXiaodong Liu 
1394c10e0bbSXiaodong Liu static int
fuse_open(const char * path,struct fuse_file_info * info)14030e3f4d9SSeth Howell fuse_open(const char *path, struct fuse_file_info *info)
1414c10e0bbSXiaodong Liu {
1424c10e0bbSXiaodong Liu 	struct spdk_file *file;
1434c10e0bbSXiaodong Liu 	int rc;
1444c10e0bbSXiaodong Liu 
1454c10e0bbSXiaodong Liu 	rc = spdk_fs_open_file(thd_bfuse->fs, thd_bfuse->channel, path, 0, &file);
1464c10e0bbSXiaodong Liu 	if (rc != 0) {
1474c10e0bbSXiaodong Liu 		return -rc;
1484c10e0bbSXiaodong Liu 	}
1494c10e0bbSXiaodong Liu 
1504c10e0bbSXiaodong Liu 	info->fh = (uintptr_t)file;
1514c10e0bbSXiaodong Liu 	return 0;
1524c10e0bbSXiaodong Liu }
1534c10e0bbSXiaodong Liu 
1544c10e0bbSXiaodong Liu static int
fuse_release(const char * path,struct fuse_file_info * info)15530e3f4d9SSeth Howell fuse_release(const char *path, struct fuse_file_info *info)
1564c10e0bbSXiaodong Liu {
1574c10e0bbSXiaodong Liu 	struct spdk_file *file = (struct spdk_file *)info->fh;
1584c10e0bbSXiaodong Liu 
1594c10e0bbSXiaodong Liu 	return spdk_file_close(file, thd_bfuse->channel);
1604c10e0bbSXiaodong Liu }
1614c10e0bbSXiaodong Liu 
1624c10e0bbSXiaodong Liu static int
fuse_read(const char * path,char * buf,size_t len,off_t offset,struct fuse_file_info * info)16330e3f4d9SSeth Howell fuse_read(const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info)
1644c10e0bbSXiaodong Liu {
1654c10e0bbSXiaodong Liu 	struct spdk_file *file = (struct spdk_file *)info->fh;
1664c10e0bbSXiaodong Liu 
1674c10e0bbSXiaodong Liu 	return spdk_file_read(file, thd_bfuse->channel, buf, offset, len);
1684c10e0bbSXiaodong Liu }
1694c10e0bbSXiaodong Liu 
1704c10e0bbSXiaodong Liu static int
fuse_write(const char * path,const char * buf,size_t len,off_t offset,struct fuse_file_info * info)17130e3f4d9SSeth Howell fuse_write(const char *path, const char *buf, size_t len, off_t offset,
1724c10e0bbSXiaodong Liu 	   struct fuse_file_info *info)
1734c10e0bbSXiaodong Liu {
1744c10e0bbSXiaodong Liu 	struct spdk_file *file = (struct spdk_file *)info->fh;
1754c10e0bbSXiaodong Liu 	int rc;
1764c10e0bbSXiaodong Liu 
1774c10e0bbSXiaodong Liu 	rc = spdk_file_write(file, thd_bfuse->channel, (void *)buf, offset, len);
1784c10e0bbSXiaodong Liu 	if (rc == 0) {
1794c10e0bbSXiaodong Liu 		return len;
1804c10e0bbSXiaodong Liu 	} else {
1814c10e0bbSXiaodong Liu 		return rc;
1824c10e0bbSXiaodong Liu 	}
1834c10e0bbSXiaodong Liu }
1844c10e0bbSXiaodong Liu 
1854c10e0bbSXiaodong Liu static int
fuse_flush(const char * path,struct fuse_file_info * info)18630e3f4d9SSeth Howell fuse_flush(const char *path, struct fuse_file_info *info)
1874c10e0bbSXiaodong Liu {
1884c10e0bbSXiaodong Liu 	return 0;
1894c10e0bbSXiaodong Liu }
1904c10e0bbSXiaodong Liu 
1914c10e0bbSXiaodong Liu static int
fuse_fsync(const char * path,int datasync,struct fuse_file_info * info)19230e3f4d9SSeth Howell fuse_fsync(const char *path, int datasync, struct fuse_file_info *info)
1934c10e0bbSXiaodong Liu {
1944c10e0bbSXiaodong Liu 	return 0;
1954c10e0bbSXiaodong Liu }
1964c10e0bbSXiaodong Liu 
1974c10e0bbSXiaodong Liu static int
fuse_rename(const char * old_path,const char * new_path,unsigned int flags)19830e3f4d9SSeth Howell fuse_rename(const char *old_path, const char *new_path, unsigned int flags)
1994c10e0bbSXiaodong Liu {
2004c10e0bbSXiaodong Liu 	return spdk_fs_rename_file(thd_bfuse->fs, thd_bfuse->channel, old_path, new_path);
2014c10e0bbSXiaodong Liu }
2024c10e0bbSXiaodong Liu 
2034c10e0bbSXiaodong Liu static struct fuse_operations spdk_fuse_oper = {
20430e3f4d9SSeth Howell 	.getattr	= fuse_getattr,
20530e3f4d9SSeth Howell 	.readdir	= fuse_readdir,
20630e3f4d9SSeth Howell 	.mknod		= fuse_mknod,
20730e3f4d9SSeth Howell 	.unlink		= fuse_unlink,
20830e3f4d9SSeth Howell 	.truncate	= fuse_truncate,
20930e3f4d9SSeth Howell 	.utimens	= fuse_utimens,
21030e3f4d9SSeth Howell 	.open		= fuse_open,
21130e3f4d9SSeth Howell 	.release	= fuse_release,
21230e3f4d9SSeth Howell 	.read		= fuse_read,
21330e3f4d9SSeth Howell 	.write		= fuse_write,
21430e3f4d9SSeth Howell 	.flush		= fuse_flush,
21530e3f4d9SSeth Howell 	.fsync		= fuse_fsync,
21630e3f4d9SSeth Howell 	.rename		= fuse_rename,
2174c10e0bbSXiaodong Liu };
2184c10e0bbSXiaodong Liu 
2194c10e0bbSXiaodong Liu static void *
fuse_loop_new_thread(void * arg)2204c10e0bbSXiaodong Liu fuse_loop_new_thread(void *arg)
2214c10e0bbSXiaodong Liu {
2224c10e0bbSXiaodong Liu 	struct spdk_blobfs_fuse *bfuse = arg;
2234c10e0bbSXiaodong Liu 
2244c10e0bbSXiaodong Liu 	spdk_unaffinitize_thread();
2254c10e0bbSXiaodong Liu 
2264c10e0bbSXiaodong Liu 	thd_bfuse = bfuse;
2274c10e0bbSXiaodong Liu 	SPDK_NOTICELOG("Start to loop blobfs on bdev %s mounted at %s\n", bfuse->bdev_name,
2284c10e0bbSXiaodong Liu 		       bfuse->mountpoint);
2294c10e0bbSXiaodong Liu 
2304c10e0bbSXiaodong Liu 	bfuse->channel = spdk_fs_alloc_thread_ctx(bfuse->fs);
2314c10e0bbSXiaodong Liu 
2324c10e0bbSXiaodong Liu 	fuse_loop(bfuse->fuse_handle);
2334c10e0bbSXiaodong Liu 	fuse_unmount(bfuse->fuse_handle);
2344c10e0bbSXiaodong Liu 	fuse_destroy(bfuse->fuse_handle);
2354c10e0bbSXiaodong Liu 	SPDK_NOTICELOG("Blobfs on bdev %s unmounted from %s\n", bfuse->bdev_name, bfuse->mountpoint);
2364c10e0bbSXiaodong Liu 
2374c10e0bbSXiaodong Liu 	spdk_fs_free_thread_ctx(bfuse->channel);
2384c10e0bbSXiaodong Liu 
2394c10e0bbSXiaodong Liu 	bfuse->cb_fn(bfuse->cb_arg);
2404c10e0bbSXiaodong Liu 
2414c10e0bbSXiaodong Liu 	blobfs_fuse_free(bfuse);
2424c10e0bbSXiaodong Liu 
2434c10e0bbSXiaodong Liu 	pthread_exit(NULL);
2444c10e0bbSXiaodong Liu }
2454c10e0bbSXiaodong Liu 
2464c10e0bbSXiaodong Liu 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)24730e3f4d9SSeth Howell blobfs_fuse_start(const char *bdev_name, const char *mountpoint, struct spdk_filesystem *fs,
2484c10e0bbSXiaodong Liu 		  blobfs_fuse_unmount_cb cb_fn, void *cb_arg, struct spdk_blobfs_fuse **_bfuse)
2494c10e0bbSXiaodong Liu {
2504c10e0bbSXiaodong Liu 	/* Set argv[1] as bdev_name in order to show bdev_name as the mounting source */
2514c10e0bbSXiaodong Liu 	char *argv[1] = {(char *)bdev_name};
2524c10e0bbSXiaodong Liu 	struct fuse_args args = FUSE_ARGS_INIT(1, argv);
2534c10e0bbSXiaodong Liu 	struct fuse_cmdline_opts opts = {};
2544c10e0bbSXiaodong Liu 	struct fuse *fuse_handle;
2554c10e0bbSXiaodong Liu 	struct spdk_blobfs_fuse *bfuse;
2564c10e0bbSXiaodong Liu 	pthread_t tid;
2574c10e0bbSXiaodong Liu 	int rc;
2584c10e0bbSXiaodong Liu 
2594c10e0bbSXiaodong Liu 	bfuse = (struct spdk_blobfs_fuse *)calloc(1, sizeof(*bfuse));
2604c10e0bbSXiaodong Liu 	if (bfuse == NULL) {
2614c10e0bbSXiaodong Liu 		return -ENOMEM;
2624c10e0bbSXiaodong Liu 	}
2634c10e0bbSXiaodong Liu 
2644c10e0bbSXiaodong Liu 	bfuse->bdev_name = strdup(bdev_name);
2654c10e0bbSXiaodong Liu 	bfuse->mountpoint = strdup(mountpoint);
266d491e7eaSZhiqiang Liu 	if (!bfuse->bdev_name || !bfuse->mountpoint) {
267d491e7eaSZhiqiang Liu 		rc = -ENOMEM;
268d491e7eaSZhiqiang Liu 		goto err;
269d491e7eaSZhiqiang Liu 	}
2704c10e0bbSXiaodong Liu 	bfuse->fs = fs;
2714c10e0bbSXiaodong Liu 	bfuse->cb_fn = cb_fn;
2724c10e0bbSXiaodong Liu 	bfuse->cb_arg = cb_arg;
2734c10e0bbSXiaodong Liu 
274d491e7eaSZhiqiang Liu 	rc = fuse_parse_cmdline(&args, &opts);
275d491e7eaSZhiqiang Liu 	assert(rc == 0);
276d491e7eaSZhiqiang Liu 
2774c10e0bbSXiaodong Liu 	fuse_handle = fuse_new(&args, &spdk_fuse_oper, sizeof(spdk_fuse_oper), NULL);
2784c10e0bbSXiaodong Liu 	fuse_opt_free_args(&args);
2794c10e0bbSXiaodong Liu 	if (fuse_handle == NULL) {
2804c10e0bbSXiaodong Liu 		SPDK_ERRLOG("could not create fuse handle!\n");
2814c10e0bbSXiaodong Liu 		rc = -1;
2824c10e0bbSXiaodong Liu 		goto err;
2834c10e0bbSXiaodong Liu 	}
2844c10e0bbSXiaodong Liu 	bfuse->fuse_handle = fuse_handle;
2854c10e0bbSXiaodong Liu 
2864c10e0bbSXiaodong Liu 	rc = fuse_mount(bfuse->fuse_handle, bfuse->mountpoint);
2874c10e0bbSXiaodong Liu 	if (rc != 0) {
2884c10e0bbSXiaodong Liu 		SPDK_ERRLOG("could not mount fuse handle\n");
2894c10e0bbSXiaodong Liu 		rc = -1;
2904c10e0bbSXiaodong Liu 		goto err;
2914c10e0bbSXiaodong Liu 	}
2924c10e0bbSXiaodong Liu 
2934c10e0bbSXiaodong Liu 	rc = pthread_create(&tid, NULL, fuse_loop_new_thread, bfuse);
2944c10e0bbSXiaodong Liu 	if (rc != 0) {
2954c10e0bbSXiaodong Liu 		SPDK_ERRLOG("could not create thread: %s\n", spdk_strerror(rc));
2964c10e0bbSXiaodong Liu 		rc = -rc;
2974c10e0bbSXiaodong Liu 		goto err;
2984c10e0bbSXiaodong Liu 	}
2994c10e0bbSXiaodong Liu 	bfuse->fuse_tid = tid;
3004c10e0bbSXiaodong Liu 
3014c10e0bbSXiaodong Liu 	rc = pthread_detach(tid);
3024c10e0bbSXiaodong Liu 	if (rc != 0) {
3034c10e0bbSXiaodong Liu 		SPDK_ERRLOG("could not detach thread for fuse loop thread: %s\n", spdk_strerror(rc));
3044c10e0bbSXiaodong Liu 		rc = -rc;
3054c10e0bbSXiaodong Liu 		goto err;
3064c10e0bbSXiaodong Liu 	}
3074c10e0bbSXiaodong Liu 
3084c10e0bbSXiaodong Liu 	*_bfuse = bfuse;
3094c10e0bbSXiaodong Liu 	return 0;
3104c10e0bbSXiaodong Liu 
3114c10e0bbSXiaodong Liu err:
3124c10e0bbSXiaodong Liu 	blobfs_fuse_free(bfuse);
3134c10e0bbSXiaodong Liu 
3144c10e0bbSXiaodong Liu 	return rc;
3154c10e0bbSXiaodong Liu }
3164c10e0bbSXiaodong Liu 
3174c10e0bbSXiaodong Liu void
blobfs_fuse_stop(struct spdk_blobfs_fuse * bfuse)31830e3f4d9SSeth Howell blobfs_fuse_stop(struct spdk_blobfs_fuse *bfuse)
3194c10e0bbSXiaodong Liu {
32078ba12ecSWang Shilong 	if (bfuse) {
3214c10e0bbSXiaodong Liu 		fuse_session_exit(fuse_get_session(bfuse->fuse_handle));
3224c10e0bbSXiaodong Liu 		pthread_kill(bfuse->fuse_tid, SIGINT);
3234c10e0bbSXiaodong Liu 	}
32478ba12ecSWang Shilong }
325