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