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_io_channel *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 spdk_file_truncate(file, g_channel, size); 147 spdk_file_close(file, g_channel); 148 149 return 0; 150 } 151 152 static int 153 spdk_fuse_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) 154 { 155 return 0; 156 } 157 158 static int 159 spdk_fuse_open(const char *path, struct fuse_file_info *info) 160 { 161 struct spdk_file *file; 162 int rc; 163 164 rc = spdk_fs_open_file(g_fs, g_channel, path, 0, &file); 165 if (rc != 0) { 166 return -rc; 167 } 168 169 info->fh = (uintptr_t)file; 170 return 0; 171 } 172 173 static int 174 spdk_fuse_release(const char *path, struct fuse_file_info *info) 175 { 176 struct spdk_file *file = (struct spdk_file *)info->fh; 177 178 return spdk_file_close(file, g_channel); 179 } 180 181 static int 182 spdk_fuse_read(const char *path, char *buf, size_t len, off_t offset, struct fuse_file_info *info) 183 { 184 struct spdk_file *file = (struct spdk_file *)info->fh; 185 186 return spdk_file_read(file, g_channel, buf, offset, len); 187 } 188 189 static int 190 spdk_fuse_write(const char *path, const char *buf, size_t len, off_t offset, 191 struct fuse_file_info *info) 192 { 193 struct spdk_file *file = (struct spdk_file *)info->fh; 194 int rc; 195 196 rc = spdk_file_write(file, g_channel, (void *)buf, offset, len); 197 if (rc == 0) { 198 return len; 199 } else { 200 return rc; 201 } 202 } 203 204 static int 205 spdk_fuse_flush(const char *path, struct fuse_file_info *info) 206 { 207 return 0; 208 } 209 210 static int 211 spdk_fuse_fsync(const char *path, int datasync, struct fuse_file_info *info) 212 { 213 return 0; 214 } 215 216 static int 217 spdk_fuse_rename(const char *old_path, const char *new_path, unsigned int flags) 218 { 219 return spdk_fs_rename_file(g_fs, g_channel, old_path, new_path); 220 } 221 222 static struct fuse_operations spdk_fuse_oper = { 223 .getattr = spdk_fuse_getattr, 224 .readdir = spdk_fuse_readdir, 225 .mknod = spdk_fuse_mknod, 226 .unlink = spdk_fuse_unlink, 227 .truncate = spdk_fuse_truncate, 228 .utimens = spdk_fuse_utimens, 229 .open = spdk_fuse_open, 230 .release = spdk_fuse_release, 231 .read = spdk_fuse_read, 232 .write = spdk_fuse_write, 233 .flush = spdk_fuse_flush, 234 .fsync = spdk_fuse_fsync, 235 .rename = spdk_fuse_rename, 236 }; 237 238 static void 239 construct_targets(void) 240 { 241 struct spdk_bdev *bdev; 242 243 bdev = spdk_bdev_get_by_name(g_bdev_name); 244 if (bdev == NULL) { 245 SPDK_ERRLOG("bdev %s not found\n", g_bdev_name); 246 exit(1); 247 } 248 249 g_bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 250 251 printf("Mounting BlobFS on bdev %s\n", spdk_bdev_get_name(bdev)); 252 } 253 254 static void 255 start_fuse_fn(void *arg1, void *arg2) 256 { 257 struct fuse_args args = FUSE_ARGS_INIT(g_fuse_argc, g_fuse_argv); 258 int rc; 259 struct fuse_cmdline_opts opts = {}; 260 261 g_fuse_thread = pthread_self(); 262 rc = fuse_parse_cmdline(&args, &opts); 263 if (rc != 0) { 264 spdk_app_stop(-1); 265 fuse_opt_free_args(&args); 266 return; 267 } 268 g_fuse = fuse_new(&args, &spdk_fuse_oper, sizeof(spdk_fuse_oper), NULL); 269 fuse_opt_free_args(&args); 270 271 rc = fuse_mount(g_fuse, g_mountpoint); 272 if (rc != 0) { 273 spdk_app_stop(-1); 274 return; 275 } 276 277 fuse_daemonize(true /* true = run in foreground */); 278 279 fuse_loop(g_fuse); 280 281 fuse_unmount(g_fuse); 282 fuse_destroy(g_fuse); 283 } 284 285 static void 286 init_cb(void *ctx, struct spdk_filesystem *fs, int fserrno) 287 { 288 struct spdk_event *event; 289 290 g_fs = fs; 291 g_channel = spdk_fs_alloc_io_channel_sync(g_fs); 292 event = spdk_event_allocate(1, start_fuse_fn, NULL, NULL); 293 spdk_event_call(event); 294 } 295 296 static void 297 spdk_fuse_run(void *arg1, void *arg2) 298 { 299 construct_targets(); 300 spdk_fs_load(g_bs_dev, __send_request, init_cb, NULL); 301 } 302 303 static void 304 shutdown_cb(void *ctx, int fserrno) 305 { 306 fuse_session_exit(fuse_get_session(g_fuse)); 307 pthread_kill(g_fuse_thread, SIGINT); 308 spdk_fs_free_io_channel(g_channel); 309 spdk_app_stop(0); 310 } 311 312 static void 313 spdk_fuse_shutdown(void) 314 { 315 spdk_fs_unload(g_fs, shutdown_cb, NULL); 316 } 317 318 int main(int argc, char **argv) 319 { 320 struct spdk_app_opts opts = {}; 321 int rc = 0; 322 323 if (argc < 4) { 324 fprintf(stderr, "usage: %s <conffile> <bdev name> <mountpoint>\n", argv[0]); 325 exit(1); 326 } 327 328 spdk_app_opts_init(&opts); 329 opts.name = "spdk_fuse"; 330 opts.config_file = argv[1]; 331 opts.reactor_mask = "0x3"; 332 opts.mem_size = 6144; 333 opts.shutdown_cb = spdk_fuse_shutdown; 334 335 g_bdev_name = argv[2]; 336 g_mountpoint = argv[3]; 337 g_fuse_argc = argc - 2; 338 g_fuse_argv = &argv[2]; 339 340 rc = spdk_app_start(&opts, spdk_fuse_run, NULL, NULL); 341 spdk_app_fini(); 342 343 return rc; 344 } 345