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 void 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 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 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 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 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 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 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 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 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 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 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 186 fuse_flush(const char *path, struct fuse_file_info *info) 187 { 188 return 0; 189 } 190 191 static int 192 fuse_fsync(const char *path, int datasync, struct fuse_file_info *info) 193 { 194 return 0; 195 } 196 197 static int 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 * 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 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 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