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 #define FUSE_USE_VERSION 30 37 #include "fuse3/fuse.h" 38 #include "fuse3/fuse_lowlevel.h" 39 40 #include "spdk/blobfs.h" 41 #include "spdk/bdev.h" 42 #include "spdk/event.h" 43 #include "spdk/thread.h" 44 #include "spdk/blob_bdev.h" 45 #include "spdk/log.h" 46 47 struct fuse *g_fuse; 48 char *g_bdev_name; 49 char *g_mountpoint; 50 pthread_t g_fuse_thread; 51 52 struct spdk_bs_dev *g_bs_dev; 53 struct spdk_filesystem *g_fs; 54 struct spdk_fs_thread_ctx *g_channel; 55 struct spdk_file *g_file; 56 int g_fserrno; 57 int g_fuse_argc = 0; 58 char **g_fuse_argv = NULL; 59 60 static void 61 __call_fn(void *arg1, void *arg2) 62 { 63 fs_request_fn fn; 64 65 fn = (fs_request_fn)arg1; 66 fn(arg2); 67 } 68 69 static void 70 __send_request(fs_request_fn fn, void *arg) 71 { 72 struct spdk_event *event; 73 74 event = spdk_event_allocate(0, __call_fn, (void *)fn, arg); 75 spdk_event_call(event); 76 } 77 78 static int 79 spdk_fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) 80 { 81 struct spdk_file_stat stat; 82 int rc; 83 84 if (!strcmp(path, "/")) { 85 stbuf->st_mode = S_IFDIR | 0755; 86 stbuf->st_nlink = 2; 87 return 0; 88 } 89 90 rc = spdk_fs_file_stat(g_fs, g_channel, path, &stat); 91 if (rc == 0) { 92 stbuf->st_mode = S_IFREG | 0644; 93 stbuf->st_nlink = 1; 94 stbuf->st_size = stat.size; 95 } 96 97 return rc; 98 } 99 100 static int 101 spdk_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 102 off_t offset, struct fuse_file_info *fi, 103 enum fuse_readdir_flags flags) 104 { 105 struct spdk_file *file; 106 const char *filename; 107 spdk_fs_iter iter; 108 109 filler(buf, ".", NULL, 0, 0); 110 filler(buf, "..", NULL, 0, 0); 111 112 iter = spdk_fs_iter_first(g_fs); 113 while (iter != NULL) { 114 file = spdk_fs_iter_get_file(iter); 115 iter = spdk_fs_iter_next(iter); 116 filename = spdk_file_get_name(file); 117 filler(buf, &filename[1], NULL, 0, 0); 118 } 119 120 return 0; 121 } 122 123 static int 124 spdk_fuse_mknod(const char *path, mode_t mode, dev_t rdev) 125 { 126 return spdk_fs_create_file(g_fs, g_channel, path); 127 } 128 129 static int 130 spdk_fuse_unlink(const char *path) 131 { 132 return spdk_fs_delete_file(g_fs, g_channel, path); 133 } 134 135 static int 136 spdk_fuse_truncate(const char *path, off_t size, struct fuse_file_info *fi) 137 { 138 struct spdk_file *file; 139 int rc; 140 141 rc = spdk_fs_open_file(g_fs, g_channel, path, 0, &file); 142 if (rc != 0) { 143 return -rc; 144 } 145 146 rc = spdk_file_truncate(file, g_channel, size); 147 if (rc != 0) { 148 return -rc; 149 } 150 151 spdk_file_close(file, g_channel); 152 153 return 0; 154 } 155 156 static int 157 spdk_fuse_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) 158 { 159 return 0; 160 } 161 162 static int 163 spdk_fuse_open(const char *path, struct fuse_file_info *info) 164 { 165 struct spdk_file *file; 166 int rc; 167 168 rc = spdk_fs_open_file(g_fs, g_channel, path, 0, &file); 169 if (rc != 0) { 170 return -rc; 171 } 172 173 info->fh = (uintptr_t)file; 174 return 0; 175 } 176 177 static int 178 spdk_fuse_release(const char *path, struct fuse_file_info *info) 179 { 180 struct spdk_file *file = (struct spdk_file *)info->fh; 181 182 return spdk_file_close(file, g_channel); 183 } 184 185 static int 186 spdk_fuse_read(const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info) 187 { 188 struct spdk_file *file = (struct spdk_file *)info->fh; 189 190 return spdk_file_read(file, g_channel, buf, offset, len); 191 } 192 193 static int 194 spdk_fuse_write(const char *path, const char *buf, size_t len, off_t offset, 195 struct fuse_file_info *info) 196 { 197 struct spdk_file *file = (struct spdk_file *)info->fh; 198 int rc; 199 200 rc = spdk_file_write(file, g_channel, (void *)buf, offset, len); 201 if (rc == 0) { 202 return len; 203 } else { 204 return rc; 205 } 206 } 207 208 static int 209 spdk_fuse_flush(const char *path, struct fuse_file_info *info) 210 { 211 return 0; 212 } 213 214 static int 215 spdk_fuse_fsync(const char *path, int datasync, struct fuse_file_info *info) 216 { 217 return 0; 218 } 219 220 static int 221 spdk_fuse_rename(const char *old_path, const char *new_path, unsigned int flags) 222 { 223 return spdk_fs_rename_file(g_fs, g_channel, old_path, new_path); 224 } 225 226 static struct fuse_operations spdk_fuse_oper = { 227 .getattr = spdk_fuse_getattr, 228 .readdir = spdk_fuse_readdir, 229 .mknod = spdk_fuse_mknod, 230 .unlink = spdk_fuse_unlink, 231 .truncate = spdk_fuse_truncate, 232 .utimens = spdk_fuse_utimens, 233 .open = spdk_fuse_open, 234 .release = spdk_fuse_release, 235 .read = spdk_fuse_read, 236 .write = spdk_fuse_write, 237 .flush = spdk_fuse_flush, 238 .fsync = spdk_fuse_fsync, 239 .rename = spdk_fuse_rename, 240 }; 241 242 static void 243 construct_targets(void) 244 { 245 struct spdk_bdev *bdev; 246 247 bdev = spdk_bdev_get_by_name(g_bdev_name); 248 if (bdev == NULL) { 249 SPDK_ERRLOG("bdev %s not found\n", g_bdev_name); 250 exit(1); 251 } 252 253 g_bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 254 255 printf("Mounting BlobFS on bdev %s\n", spdk_bdev_get_name(bdev)); 256 } 257 258 static void 259 start_fuse_fn(void *arg1, void *arg2) 260 { 261 struct fuse_args args = FUSE_ARGS_INIT(g_fuse_argc, g_fuse_argv); 262 int rc; 263 struct fuse_cmdline_opts opts = {}; 264 265 g_fuse_thread = pthread_self(); 266 rc = fuse_parse_cmdline(&args, &opts); 267 if (rc != 0) { 268 spdk_app_stop(-1); 269 fuse_opt_free_args(&args); 270 return; 271 } 272 g_fuse = fuse_new(&args, &spdk_fuse_oper, sizeof(spdk_fuse_oper), NULL); 273 fuse_opt_free_args(&args); 274 275 rc = fuse_mount(g_fuse, g_mountpoint); 276 if (rc != 0) { 277 spdk_app_stop(-1); 278 return; 279 } 280 281 fuse_daemonize(true /* true = run in foreground */); 282 283 fuse_loop(g_fuse); 284 285 fuse_unmount(g_fuse); 286 fuse_destroy(g_fuse); 287 } 288 289 static void 290 init_cb(void *ctx, struct spdk_filesystem *fs, int fserrno) 291 { 292 struct spdk_event *event; 293 294 g_fs = fs; 295 g_channel = spdk_fs_alloc_thread_ctx(g_fs); 296 event = spdk_event_allocate(1, start_fuse_fn, NULL, NULL); 297 spdk_event_call(event); 298 } 299 300 static void 301 spdk_fuse_run(void *arg1) 302 { 303 construct_targets(); 304 spdk_fs_load(g_bs_dev, __send_request, init_cb, NULL); 305 } 306 307 static void 308 shutdown_cb(void *ctx, int fserrno) 309 { 310 fuse_session_exit(fuse_get_session(g_fuse)); 311 pthread_kill(g_fuse_thread, SIGINT); 312 spdk_fs_free_thread_ctx(g_channel); 313 spdk_app_stop(0); 314 } 315 316 static void 317 spdk_fuse_shutdown(void) 318 { 319 spdk_fs_unload(g_fs, shutdown_cb, NULL); 320 } 321 322 int main(int argc, char **argv) 323 { 324 struct spdk_app_opts opts = {}; 325 int rc = 0; 326 327 if (argc < 4) { 328 fprintf(stderr, "usage: %s <conffile> <bdev name> <mountpoint>\n", argv[0]); 329 exit(1); 330 } 331 332 spdk_app_opts_init(&opts); 333 opts.name = "spdk_fuse"; 334 opts.config_file = argv[1]; 335 opts.reactor_mask = "0x3"; 336 opts.shutdown_cb = spdk_fuse_shutdown; 337 338 g_bdev_name = argv[2]; 339 g_mountpoint = argv[3]; 340 g_fuse_argc = argc - 2; 341 g_fuse_argv = &argv[2]; 342 343 rc = spdk_app_start(&opts, spdk_fuse_run, NULL); 344 spdk_app_fini(); 345 346 return rc; 347 } 348