jcs's openbsd hax
openbsd
at jcs 604 lines 13 kB view raw
1/* $OpenBSD: fuse.c,v 1.58 2026/01/29 06:04:27 helg Exp $ */ 2/* 3 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/uio.h> 19 20#include <errno.h> 21#include <signal.h> 22#include <stddef.h> 23#include <stdlib.h> 24#include <string.h> 25#include <unistd.h> 26 27#include "fuse_private.h" 28#include "debug.h" 29 30static struct fuse_context *ictx = NULL; 31 32enum { 33 KEY_DEBUG, 34 KEY_FOREGROUND, 35 KEY_HELP, 36 KEY_HELP_WITHOUT_HEADER, 37 KEY_VERSION, 38 KEY_MAXREAD, 39 KEY_STUB 40}; 41 42/* options supported by fuse_parse_cmdline */ 43static struct fuse_opt fuse_core_opts[] = { 44 FUSE_OPT_KEY("-d", KEY_DEBUG), 45 FUSE_OPT_KEY("debug", KEY_DEBUG), 46 FUSE_OPT_KEY("-f", KEY_FOREGROUND), 47 FUSE_OPT_KEY("-h", KEY_HELP), 48 FUSE_OPT_KEY("--help", KEY_HELP), 49 FUSE_OPT_KEY("-ho", KEY_HELP_WITHOUT_HEADER), 50 FUSE_OPT_KEY("-s", KEY_STUB), 51 FUSE_OPT_KEY("-V", KEY_VERSION), 52 FUSE_OPT_KEY("--version", KEY_VERSION), 53 FUSE_OPT_END 54}; 55 56/* options supported by fuse_new */ 57#define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_config, m), 1} 58static struct fuse_opt fuse_lib_opts[] = { 59 FUSE_OPT_KEY("ac_attr_timeout=", KEY_STUB), 60 FUSE_OPT_KEY("attr_timeout=", KEY_STUB), 61 FUSE_OPT_KEY("auto_cache", KEY_STUB), 62 FUSE_OPT_KEY("noauto_cache", KEY_STUB), 63 FUSE_OPT_KEY("big_writes", KEY_STUB), 64 FUSE_OPT_KEY("debug", KEY_DEBUG), 65 FUSE_OPT_KEY("-d", KEY_DEBUG), 66 FUSE_OPT_KEY("entry_timeout=", KEY_STUB), 67 FUSE_LIB_OPT("gid=", set_gid), 68 FUSE_LIB_OPT("gid=%u", gid), 69 FUSE_OPT_KEY("hard_remove", KEY_STUB), 70 FUSE_OPT_KEY("intr_signal", KEY_STUB), 71 FUSE_OPT_KEY("kernel_cache", KEY_STUB), 72 FUSE_OPT_KEY("large_read", KEY_STUB), 73 FUSE_OPT_KEY("modules=", KEY_STUB), 74 FUSE_OPT_KEY("negative_timeout=", KEY_STUB), 75 FUSE_OPT_KEY("readdir_ino", KEY_STUB), 76 FUSE_OPT_KEY("relatime", KEY_STUB), 77 FUSE_OPT_KEY("subtype=", KEY_STUB), 78 FUSE_LIB_OPT("uid=", set_uid), 79 FUSE_LIB_OPT("uid=%u", uid), 80 FUSE_LIB_OPT("use_ino", use_ino), 81 FUSE_OPT_KEY("dmask=%o", KEY_STUB), 82 FUSE_OPT_KEY("fmask=%o", KEY_STUB), 83 FUSE_LIB_OPT("umask=", set_mode), 84 FUSE_LIB_OPT("umask=%o", umask), 85 FUSE_OPT_END 86}; 87 88/* options supported by fuse_mount */ 89#define FUSE_MOUNT_OPT(o, m) {o, offsetof(struct fuse_mount_opts, m), 1} 90static struct fuse_opt fuse_mount_opts[] = { 91 FUSE_MOUNT_OPT("allow_other", allow_other), 92 FUSE_OPT_KEY("allow_root", KEY_STUB), 93 FUSE_OPT_KEY("async_read", KEY_STUB), 94 FUSE_OPT_KEY("blkdev", KEY_STUB), 95 FUSE_OPT_KEY("blksize=", KEY_STUB), 96 FUSE_MOUNT_OPT("default_permissions", def_perms), 97 FUSE_OPT_KEY("direct_io", KEY_STUB), 98 FUSE_MOUNT_OPT("fsname=%s", fsname), 99 FUSE_MOUNT_OPT("max_read=%u", max_read), 100 FUSE_OPT_KEY("max_readahead", KEY_STUB), 101 FUSE_OPT_KEY("max_write", KEY_STUB), 102 FUSE_MOUNT_OPT("noatime", noatime), 103 FUSE_MOUNT_OPT("nonempty", nonempty), 104 FUSE_MOUNT_OPT("-r", rdonly), 105 FUSE_MOUNT_OPT("ro", rdonly), 106 FUSE_OPT_KEY("ro_fallback", KEY_STUB), 107 FUSE_OPT_KEY("sync_read", KEY_STUB), 108 FUSE_OPT_END 109}; 110 111extern struct fuse_lowlevel_ops llops; 112 113int 114fuse_loop(struct fuse *fuse) 115{ 116 return fuse_session_loop(fuse_get_session(fuse)); 117} 118DEF(fuse_loop); 119 120struct fuse_chan * 121fuse_mount(const char *dir, struct fuse_args *args) 122{ 123 struct fusefs_args fargs; 124 struct fuse_mount_opts opts; 125 struct fuse_chan *fc; 126 const char *errcause; 127 char *mnt_dir; 128 int mnt_flags; 129 130 if (dir == NULL) 131 return (NULL); 132 133 fc = calloc(1, sizeof(*fc)); 134 if (fc == NULL) 135 return (NULL); 136 137 mnt_dir = realpath(dir, NULL); 138 if (mnt_dir == NULL) 139 goto bad; 140 141 if ((fc->fd = open("/dev/fuse0", O_RDWR)) == -1) { 142 perror("/dev/fuse0"); 143 goto bad; 144 } 145 146 memset(&opts, 0, sizeof(opts)); 147 if (fuse_opt_parse(args, &opts, fuse_mount_opts, NULL) == -1) 148 goto bad; 149 150 mnt_flags = 0; 151 if (opts.rdonly) 152 mnt_flags |= MNT_RDONLY; 153 if (opts.noatime) 154 mnt_flags |= MNT_NOATIME; 155 156 if (opts.max_read > FUSEBUFMAXSIZE) { 157 fprintf(stderr, "fuse: invalid max_read (%d > %d)\n", 158 opts.max_read, FUSEBUFMAXSIZE); 159 goto bad; 160 } 161 162 memset(&fargs, 0, sizeof(fargs)); 163 fargs.fd = fc->fd; 164 fargs.max_read = opts.max_read; 165 fargs.allow_other = opts.allow_other; 166 167 if (mount(MOUNT_FUSEFS, mnt_dir, mnt_flags, &fargs)) { 168 switch (errno) { 169 case EMFILE: 170 errcause = "mount table full"; 171 break; 172 case EOPNOTSUPP: 173 errcause = "filesystem not supported by kernel"; 174 break; 175 default: 176 errcause = strerror(errno); 177 break; 178 } 179 fprintf(stderr, "%s on %s: %s\n", __func__, dir, errcause); 180 goto bad; 181 } 182 183 return (fc); 184bad: 185 if (fc->fd != -1) 186 close(fc->fd); 187 free(mnt_dir); 188 free(fc); 189 return (NULL); 190} 191DEF(fuse_mount); 192 193void 194fuse_unmount(const char *dir, struct fuse_chan *ch) 195{ 196 if (ch == NULL) 197 return; 198 199 /* 200 * Close the device before unmounting to prevent deadlocks with 201 * FBT_DESTROY if fuse_loop() has already terminated. 202 */ 203 if (close(ch->fd) == -1) 204 DPERROR(__func__); 205 206 if (!ch->dead) 207 if (unmount(dir, MNT_FORCE) == -1) 208 DPERROR(__func__); 209} 210DEF(fuse_unmount); 211 212int 213fuse_is_lib_option(const char *opt) 214{ 215 return (fuse_opt_match(fuse_lib_opts, opt)); 216} 217DEF(fuse_is_lib_option); 218 219struct fuse_session * 220fuse_get_session(struct fuse *f) 221{ 222 return (f->se); 223} 224DEF(fuse_get_session); 225 226int 227fuse_loop_mt(unused struct fuse *fuse) 228{ 229 return (-1); 230} 231DEF(fuse_loop_mt); 232 233static int 234ifuse_lib_opt_proc(void *data, const char *arg, int key, 235 unused struct fuse_args *args) 236{ 237 switch (key) { 238 case KEY_STUB: 239 return (0); 240 case KEY_DEBUG: 241 ifuse_debug_init(); 242 break; 243 default: 244 fprintf(stderr, "fuse: unrecognised option %s\n", arg); 245 return (-1); 246 } 247 248 /* Keep unknown options. */ 249 return (1); 250} 251 252struct fuse * 253fuse_new(struct fuse_chan *fc, struct fuse_args *args, 254 const struct fuse_operations *ops, unused size_t size, 255 void *userdata) 256{ 257 struct fuse *fuse; 258 struct fuse_vnode *root; 259 260 if (fc == NULL || ops == NULL) 261 return (NULL); 262 263 if ((fuse = calloc(1, sizeof(*fuse))) == NULL) 264 return (NULL); 265 266 /* copy fuse ops to their own structure */ 267 memcpy(&fuse->op, ops, sizeof(fuse->op)); 268 269 if (fuse_opt_parse(args, &fuse->conf, fuse_lib_opts, 270 ifuse_lib_opt_proc) == -1) { 271 free(fuse); 272 return (NULL); 273 } 274 275 fuse->max_ino = FUSE_ROOT_INO; 276 fuse->private_data = userdata; 277 fuse->se = fuse_lowlevel_new(args, &llops, sizeof(llops), fuse); 278 if (fuse->se == NULL) { 279 free(fuse); 280 return (NULL); 281 } 282 283 fuse_session_add_chan(fuse->se, fc); 284 285 if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) { 286 free(fuse); 287 return (NULL); 288 } 289 290 tree_init(&fuse->vnode_tree); 291 tree_init(&fuse->name_tree); 292 if (!set_vn(fuse, root)) { 293 free(fuse); 294 return (NULL); 295 } 296 297 /* 298 * Prepare the context that is available to file system operations via 299 * fuse_get_context(3). The pid, gid, uid and umask fields are set 300 * on demand when this is called in a requeset handle. 301 */ 302 ictx = calloc(1, sizeof(*ictx)); 303 if (ictx == NULL) { 304 free(fuse); 305 return (NULL); 306 } 307 308 ictx->fuse = fuse; 309 ictx->private_data = userdata; 310 311 return (fuse); 312} 313DEF(fuse_new); 314 315int 316fuse_daemonize(int foreground) 317{ 318 if (foreground) 319 return (0); 320 321 return (daemon(0, 0)); 322} 323DEF(fuse_daemonize); 324 325void 326fuse_destroy(struct fuse *fuse) 327{ 328 fuse_session_destroy(fuse_get_session(fuse)); 329 free(fuse); 330 free(ictx); 331 ictx = NULL; 332} 333DEF(fuse_destroy); 334 335static void 336ifuse_sig_handler(int signum) 337{ 338 /* empty handler to dinstinguish between SIG_IGN */ 339} 340 341void 342fuse_remove_signal_handlers(unused struct fuse_session *se) 343{ 344 struct sigaction old_sa; 345 346 if (sigaction(SIGHUP, NULL, &old_sa) == 0) 347 if (old_sa.sa_handler == ifuse_sig_handler) 348 signal(SIGHUP, SIG_DFL); 349 350 if (sigaction(SIGINT, NULL, &old_sa) == 0) 351 if (old_sa.sa_handler == ifuse_sig_handler) 352 signal(SIGINT, SIG_DFL); 353 354 if (sigaction(SIGTERM, NULL, &old_sa) == 0) 355 if (old_sa.sa_handler == ifuse_sig_handler) 356 signal(SIGTERM, SIG_DFL); 357 358 if (sigaction(SIGPIPE, NULL, &old_sa) == 0) 359 if (old_sa.sa_handler == SIG_IGN) 360 signal(SIGPIPE, SIG_DFL); 361} 362DEF(fuse_remove_signal_handlers); 363 364int 365fuse_set_signal_handlers(unused struct fuse_session *se) 366{ 367 struct sigaction old_sa; 368 369 if (sigaction(SIGHUP, NULL, &old_sa) == -1) 370 return (-1); 371 if (old_sa.sa_handler == SIG_DFL) 372 signal(SIGHUP, ifuse_sig_handler); 373 374 if (sigaction(SIGINT, NULL, &old_sa) == -1) 375 return (-1); 376 if (old_sa.sa_handler == SIG_DFL) 377 signal(SIGINT, ifuse_sig_handler); 378 379 if (sigaction(SIGTERM, NULL, &old_sa) == -1) 380 return (-1); 381 if (old_sa.sa_handler == SIG_DFL) 382 signal(SIGTERM, ifuse_sig_handler); 383 384 if (sigaction(SIGPIPE, NULL, &old_sa) == -1) 385 return (-1); 386 if (old_sa.sa_handler == SIG_DFL) 387 signal(SIGPIPE, SIG_IGN); 388 389 return (0); 390} 391DEF(fuse_set_signal_handlers); 392 393static void 394dump_help(void) 395{ 396 fprintf(stderr, "FUSE options:\n" 397 " -d -o debug enable debug output (implies -f)\n" 398 " -f run in foreground\n" 399 " -V --version print fuse version\n" 400 "\n"); 401} 402 403static void 404dump_version(void) 405{ 406 fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION, 407 FUSE_MINOR_VERSION); 408} 409 410static int 411ifuse_process_opt(void *data, const char *arg, int key, 412 unused struct fuse_args *args) 413{ 414 struct fuse_core_opts *opt = data; 415 struct stat st; 416 int res; 417 418 switch (key) { 419 case KEY_STUB: 420 return (0); 421 case KEY_DEBUG: 422 ifuse_debug_init(); 423 /* falls through */ 424 case KEY_FOREGROUND: 425 opt->foreground = 1; 426 return (0); 427 case KEY_HELP: 428 case KEY_HELP_WITHOUT_HEADER: 429 dump_help(); 430 return (-1); 431 case KEY_VERSION: 432 dump_version(); 433 return (-1); 434 case FUSE_OPT_KEY_NONOPT: 435 if (opt->mp == NULL) { 436 opt->mp = realpath(arg, opt->mp); 437 if (opt->mp == NULL) { 438 fprintf(stderr, "fuse: realpath: " 439 "%s : %s\n", arg, strerror(errno)); 440 return (-1); 441 } 442 443 res = stat(opt->mp, &st); 444 if (res == -1) { 445 fprintf(stderr, "fuse: bad mount point " 446 "%s : %s\n", arg, strerror(errno)); 447 return (-1); 448 } 449 450 if (!S_ISDIR(st.st_mode)) { 451 fprintf(stderr, "fuse: bad mount point " 452 "%s : %s\n", arg, strerror(ENOTDIR)); 453 return (-1); 454 } 455 } 456 return (0); 457 } 458 459 /* Pass through unknown options. */ 460 return (1); 461} 462 463int 464fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg) 465{ 466 struct fuse_core_opts opt; 467 468 memset(&opt, 0, sizeof(opt)); 469 if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1) 470 return (-1); 471 472 if (opt.mp == NULL) { 473 fprintf(stderr, "fuse: missing mountpoint parameter\n"); 474 return (-1); 475 } 476 477 if (mp != NULL) { 478 *mp = strdup(opt.mp); 479 if (*mp == NULL) 480 return (-1); 481 } 482 483 if (mt != NULL) 484 *mt = 0; 485 486 if (fg != NULL) 487 *fg = opt.foreground; 488 489 return (0); 490} 491DEF(fuse_parse_cmdline); 492 493struct fuse_context * 494fuse_get_context(void) 495{ 496 const fuse_req_t req = ifuse_req(); 497 const struct fuse_ctx *req_ctx = fuse_req_ctx(req); 498 499 if (req_ctx == NULL) { 500 ictx->uid = 0; 501 ictx->gid = 0; 502 ictx->pid = 0; 503 ictx->umask = 0; 504 } else { 505 ictx->uid = req_ctx->uid; 506 ictx->gid = req_ctx->gid; 507 ictx->pid = req_ctx->pid; 508 ictx->umask = req_ctx->umask; 509 } 510 511 return (ictx); 512} 513DEF(fuse_get_context); 514 515int 516fuse_version(void) 517{ 518 return (FUSE_VERSION); 519} 520DEF(fuse_version); 521 522void 523fuse_teardown(struct fuse *fuse, char *mp) 524{ 525 if (fuse == NULL || mp == NULL) 526 return; 527 528 fuse_remove_signal_handlers(fuse->se); 529 fuse_unmount(mp, fuse->se->chan); 530 fuse_destroy(fuse); 531} 532DEF(fuse_teardown); 533 534int 535fuse_invalidate(unused struct fuse *f, unused const char *path) 536{ 537 return (EINVAL); 538} 539DEF(fuse_invalidate); 540 541struct fuse * 542fuse_setup(int argc, char **argv, const struct fuse_operations *ops, 543 size_t size, char **mp, int *mt, void *data) 544{ 545 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 546 struct fuse_chan *fc; 547 struct fuse *fuse; 548 char *dir; 549 int fg; 550 551 dir = NULL; 552 if (fuse_parse_cmdline(&args, &dir, mt, &fg)) 553 goto err; 554 555 if ((fc = fuse_mount(dir, &args)) == NULL) 556 goto err; 557 558 fuse_daemonize(fg); 559 560 if ((fuse = fuse_new(fc, &args, ops, size, data)) == NULL) { 561 fuse_unmount(dir, fc); 562 free(fc); 563 goto err; 564 } 565 566 /* args are no longer needed */ 567 fuse_opt_free_args(&args); 568 569 if (fuse_set_signal_handlers(fuse_get_session(fuse)) == -1) { 570 fuse_unmount(dir, fc); 571 fuse_destroy(fuse); 572 goto err; 573 } 574 575 /* the caller frees dir, but we do it if the caller doesn't want it */ 576 if (mp == NULL) 577 free(dir); 578 else 579 *mp = dir; 580 581 return (fuse); 582err: 583 free(dir); 584 return (NULL); 585} 586DEF(fuse_setup); 587 588int 589fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data) 590{ 591 struct fuse *fuse; 592 char *mp; 593 int ret; 594 595 fuse = fuse_setup(argc, argv, ops, sizeof(*ops), &mp, NULL, data); 596 if (fuse == NULL) 597 return (-1); 598 599 ret = fuse_loop(fuse); 600 fuse_teardown(fuse, mp); 601 602 return (ret == -1 ? 1 : 0); 603} 604DEF(fuse_main);