1/* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 Helper functions to create (simple) standalone programs. With the 6 aid of these functions it should be possible to create full FUSE 7 file system by implementing nothing but the request handlers. 8 9 This program can be distributed under the terms of the GNU LGPLv2. 10 See the file COPYING.LIB. 11*/ 12 13#include "fuse_config.h" 14#include "fuse_i.h" 15#include "fuse_misc.h" 16#include "fuse_opt.h" 17#include "fuse_lowlevel.h" 18#include "mount_util.h" 19 20#include <stdio.h> 21#include <stdlib.h> 22#include <stddef.h> 23#include <unistd.h> 24#include <string.h> 25#include <limits.h> 26#include <errno.h> 27#include <sys/param.h> 28 29#define FUSE_HELPER_OPT(t, p) \ 30 { t, offsetof(struct fuse_cmdline_opts, p), 1 } 31 32static const struct fuse_opt fuse_helper_opts[] = { 33 FUSE_HELPER_OPT("-h", show_help), 34 FUSE_HELPER_OPT("--help", show_help), 35 FUSE_HELPER_OPT("-V", show_version), 36 FUSE_HELPER_OPT("--version", show_version), 37 FUSE_HELPER_OPT("-d", debug), 38 FUSE_HELPER_OPT("debug", debug), 39 FUSE_HELPER_OPT("-d", foreground), 40 FUSE_HELPER_OPT("debug", foreground), 41 FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), 42 FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), 43 FUSE_HELPER_OPT("-f", foreground), 44 FUSE_HELPER_OPT("-s", singlethread), 45 FUSE_HELPER_OPT("fsname=", nodefault_subtype), 46 FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), 47#ifndef __FreeBSD__ 48 FUSE_HELPER_OPT("subtype=", nodefault_subtype), 49 FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), 50#endif 51 FUSE_HELPER_OPT("clone_fd", clone_fd), 52 FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), 53 FUSE_HELPER_OPT("max_threads=%u", max_threads), 54 FUSE_OPT_END 55}; 56 57struct fuse_conn_info_opts { 58 int atomic_o_trunc; 59 int no_remote_posix_lock; 60 int no_remote_flock; 61 int splice_write; 62 int splice_move; 63 int splice_read; 64 int no_splice_write; 65 int no_splice_move; 66 int no_splice_read; 67 int auto_inval_data; 68 int no_auto_inval_data; 69 int no_readdirplus; 70 int no_readdirplus_auto; 71 int async_dio; 72 int no_async_dio; 73 int writeback_cache; 74 int no_writeback_cache; 75 int async_read; 76 int sync_read; 77 unsigned max_write; 78 unsigned max_readahead; 79 unsigned max_background; 80 unsigned congestion_threshold; 81 unsigned time_gran; 82 int set_max_write; 83 int set_max_readahead; 84 int set_max_background; 85 int set_congestion_threshold; 86 int set_time_gran; 87}; 88 89#define CONN_OPTION(t, p, v) \ 90 { t, offsetof(struct fuse_conn_info_opts, p), v } 91static const struct fuse_opt conn_info_opt_spec[] = { 92 CONN_OPTION("max_write=%u", max_write, 0), 93 CONN_OPTION("max_write=", set_max_write, 1), 94 CONN_OPTION("max_readahead=%u", max_readahead, 0), 95 CONN_OPTION("max_readahead=", set_max_readahead, 1), 96 CONN_OPTION("max_background=%u", max_background, 0), 97 CONN_OPTION("max_background=", set_max_background, 1), 98 CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), 99 CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), 100 CONN_OPTION("sync_read", sync_read, 1), 101 CONN_OPTION("async_read", async_read, 1), 102 CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), 103 CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), 104 CONN_OPTION("no_remote_lock", no_remote_flock, 1), 105 CONN_OPTION("no_remote_flock", no_remote_flock, 1), 106 CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), 107 CONN_OPTION("splice_write", splice_write, 1), 108 CONN_OPTION("no_splice_write", no_splice_write, 1), 109 CONN_OPTION("splice_move", splice_move, 1), 110 CONN_OPTION("no_splice_move", no_splice_move, 1), 111 CONN_OPTION("splice_read", splice_read, 1), 112 CONN_OPTION("no_splice_read", no_splice_read, 1), 113 CONN_OPTION("auto_inval_data", auto_inval_data, 1), 114 CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), 115 CONN_OPTION("readdirplus=no", no_readdirplus, 1), 116 CONN_OPTION("readdirplus=yes", no_readdirplus, 0), 117 CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), 118 CONN_OPTION("readdirplus=auto", no_readdirplus, 0), 119 CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), 120 CONN_OPTION("async_dio", async_dio, 1), 121 CONN_OPTION("no_async_dio", no_async_dio, 1), 122 CONN_OPTION("writeback_cache", writeback_cache, 1), 123 CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), 124 CONN_OPTION("time_gran=%u", time_gran, 0), 125 CONN_OPTION("time_gran=", set_time_gran, 1), 126 FUSE_OPT_END 127}; 128 129 130void fuse_cmdline_help(void) 131{ 132 printf(" -h --help print help\n" 133 " -V --version print version\n" 134 " -d -o debug enable debug output (implies -f)\n" 135 " -f foreground operation\n" 136 " -s disable multi-threaded operation\n" 137 " -o clone_fd use separate fuse device fd for each thread\n" 138 " (may improve performance)\n" 139 " -o max_idle_threads the maximum number of idle worker threads\n" 140 " allowed (default: -1)\n" 141 " -o max_threads the maximum number of worker threads\n" 142 " allowed (default: 10)\n"); 143} 144 145static int fuse_helper_opt_proc(void *data, const char *arg, int key, 146 struct fuse_args *outargs) 147{ 148 (void) outargs; 149 struct fuse_cmdline_opts *opts = data; 150 151 switch (key) { 152 case FUSE_OPT_KEY_NONOPT: 153 if (!opts->mountpoint) { 154 if (fuse_mnt_parse_fuse_fd(arg) != -1) { 155 return fuse_opt_add_opt(&opts->mountpoint, arg); 156 } 157 158 char mountpoint[PATH_MAX] = ""; 159 if (realpath(arg, mountpoint) == NULL) { 160 fuse_log(FUSE_LOG_ERR, 161 "fuse: bad mount point `%s': %s\n", 162 arg, strerror(errno)); 163 return -1; 164 } 165 return fuse_opt_add_opt(&opts->mountpoint, mountpoint); 166 } else { 167 fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); 168 return -1; 169 } 170 171 default: 172 /* Pass through unknown options */ 173 return 1; 174 } 175} 176 177/* Under FreeBSD, there is no subtype option so this 178 function actually sets the fsname */ 179static int add_default_subtype(const char *progname, struct fuse_args *args) 180{ 181 int res; 182 char *subtype_opt; 183 184 const char *basename = strrchr(progname, '/'); 185 if (basename == NULL) 186 basename = progname; 187 else if (basename[1] != '\0') 188 basename++; 189 190 subtype_opt = (char *) malloc(strlen(basename) + 64); 191 if (subtype_opt == NULL) { 192 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); 193 return -1; 194 } 195#ifdef __FreeBSD__ 196 sprintf(subtype_opt, "-ofsname=%s", basename); 197#else 198 sprintf(subtype_opt, "-osubtype=%s", basename); 199#endif 200 res = fuse_opt_add_arg(args, subtype_opt); 201 free(subtype_opt); 202 return res; 203} 204 205int fuse_parse_cmdline_312(struct fuse_args *args, 206 struct fuse_cmdline_opts *opts); 207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12") 208int fuse_parse_cmdline_312(struct fuse_args *args, 209 struct fuse_cmdline_opts *opts) 210{ 211 memset(opts, 0, sizeof(struct fuse_cmdline_opts)); 212 213 opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */ 214 opts->max_threads = 10; 215 216 if (fuse_opt_parse(args, opts, fuse_helper_opts, 217 fuse_helper_opt_proc) == -1) 218 return -1; 219 220 /* *Linux*: if neither -o subtype nor -o fsname are specified, 221 set subtype to program's basename. 222 *FreeBSD*: if fsname is not specified, set to program's 223 basename. */ 224 if (!opts->nodefault_subtype) 225 if (add_default_subtype(args->argv[0], args) == -1) 226 return -1; 227 228 return 0; 229} 230 231/** 232 * struct fuse_cmdline_opts got extended in libfuse-3.12 233 */ 234int fuse_parse_cmdline_30(struct fuse_args *args, 235 struct fuse_cmdline_opts *opts); 236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0") 237int fuse_parse_cmdline_30(struct fuse_args *args, 238 struct fuse_cmdline_opts *out_opts) 239{ 240 struct fuse_cmdline_opts opts; 241 242 int rc = fuse_parse_cmdline_312(args, &opts); 243 if (rc == 0) { 244 /* copy up to the size of the old pre 3.12 struct */ 245 memcpy(out_opts, &opts, 246 offsetof(struct fuse_cmdline_opts, max_idle_threads) + 247 sizeof(opts.max_idle_threads)); 248 } 249 250 return rc; 251} 252 253int fuse_daemonize(int foreground) 254{ 255 if (!foreground) { 256 int nullfd; 257 int waiter[2]; 258 char completed; 259 260 if (pipe(waiter)) { 261 perror("fuse_daemonize: pipe"); 262 return -1; 263 } 264 265 /* 266 * demonize current process by forking it and killing the 267 * parent. This makes current process as a child of 'init'. 268 */ 269 switch(fork()) { 270 case -1: 271 perror("fuse_daemonize: fork"); 272 return -1; 273 case 0: 274 break; 275 default: 276 (void) read(waiter[0], &completed, sizeof(completed)); 277 _exit(0); 278 } 279 280 if (setsid() == -1) { 281 perror("fuse_daemonize: setsid"); 282 return -1; 283 } 284 285 (void) chdir("/"); 286 287 nullfd = open("/dev/null", O_RDWR, 0); 288 if (nullfd != -1) { 289 (void) dup2(nullfd, 0); 290 (void) dup2(nullfd, 1); 291 (void) dup2(nullfd, 2); 292 if (nullfd > 2) 293 close(nullfd); 294 } 295 296 /* Propagate completion of daemon initialization */ 297 completed = 1; 298 (void) write(waiter[1], &completed, sizeof(completed)); 299 close(waiter[0]); 300 close(waiter[1]); 301 } else { 302 (void) chdir("/"); 303 } 304 return 0; 305} 306 307int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, 308 size_t op_size, void *user_data) 309{ 310 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 311 struct fuse *fuse; 312 struct fuse_cmdline_opts opts; 313 int res; 314 struct fuse_loop_config *loop_config = NULL; 315 316 if (fuse_parse_cmdline(&args, &opts) != 0) 317 return 1; 318 319 if (opts.show_version) { 320 printf("FUSE library version %s\n", PACKAGE_VERSION); 321 fuse_lowlevel_version(); 322 res = 0; 323 goto out1; 324 } 325 326 if (opts.show_help) { 327 if(args.argv[0][0] != '\0') 328 printf("usage: %s [options] <mountpoint>\n\n", 329 args.argv[0]); 330 printf("FUSE options:\n"); 331 fuse_cmdline_help(); 332 fuse_lib_help(&args); 333 res = 0; 334 goto out1; 335 } 336 337 if (!opts.show_help && 338 !opts.mountpoint) { 339 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); 340 res = 2; 341 goto out1; 342 } 343 344 345 fuse = fuse_new_31(&args, op, op_size, user_data); 346 if (fuse == NULL) { 347 res = 3; 348 goto out1; 349 } 350 351 if (fuse_mount(fuse,opts.mountpoint) != 0) { 352 res = 4; 353 goto out2; 354 } 355 356 if (fuse_daemonize(opts.foreground) != 0) { 357 res = 5; 358 goto out3; 359 } 360 361 struct fuse_session *se = fuse_get_session(fuse); 362 if (fuse_set_signal_handlers(se) != 0) { 363 res = 6; 364 goto out3; 365 } 366 367 if (opts.singlethread) 368 res = fuse_loop(fuse); 369 else { 370 loop_config = fuse_loop_cfg_create(); 371 if (loop_config == NULL) { 372 res = 7; 373 goto out3; 374 } 375 376 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd); 377 378 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads); 379 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads); 380 res = fuse_loop_mt(fuse, loop_config); 381 } 382 if (res) 383 res = 8; 384 385 fuse_remove_signal_handlers(se); 386out3: 387 fuse_unmount(fuse); 388out2: 389 fuse_destroy(fuse); 390out1: 391 fuse_loop_cfg_destroy(loop_config); 392 free(opts.mountpoint); 393 fuse_opt_free_args(&args); 394 return res; 395} 396 397 398void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, 399 struct fuse_conn_info *conn) 400{ 401 if(opts->set_max_write) 402 conn->max_write = opts->max_write; 403 if(opts->set_max_background) 404 conn->max_background = opts->max_background; 405 if(opts->set_congestion_threshold) 406 conn->congestion_threshold = opts->congestion_threshold; 407 if(opts->set_time_gran) 408 conn->time_gran = opts->time_gran; 409 if(opts->set_max_readahead) 410 conn->max_readahead = opts->max_readahead; 411 412#define LL_ENABLE(cond,cap) \ 413 if (cond) conn->want |= (cap) 414#define LL_DISABLE(cond,cap) \ 415 if (cond) conn->want &= ~(cap) 416 417 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); 418 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); 419 420 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); 421 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); 422 423 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); 424 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); 425 426 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); 427 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); 428 429 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); 430 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); 431 432 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); 433 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); 434 435 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); 436 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); 437 438 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); 439 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); 440 441 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); 442 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); 443} 444 445struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) 446{ 447 struct fuse_conn_info_opts *opts; 448 449 opts = calloc(1, sizeof(struct fuse_conn_info_opts)); 450 if(opts == NULL) { 451 fuse_log(FUSE_LOG_ERR, "calloc failed\n"); 452 return NULL; 453 } 454 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { 455 free(opts); 456 return NULL; 457 } 458 return opts; 459} 460 461int fuse_open_channel(const char *mountpoint, const char* options) 462{ 463 struct mount_opts *opts = NULL; 464 int fd = -1; 465 const char *argv[] = { "", "-o", options }; 466 int argc = sizeof(argv) / sizeof(argv[0]); 467 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); 468 469 opts = parse_mount_opts(&args); 470 if (opts == NULL) 471 return -1; 472 473 fd = fuse_kern_mount(mountpoint, opts); 474 destroy_mount_opts(opts); 475 476 return fd; 477} 478