1/* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 Architecture specific file system mounting (Linux). 6 7 This program can be distributed under the terms of the GNU LGPLv2. 8 See the file COPYING.LIB. 9*/ 10 11#include "fuse_config.h" 12#include "fuse_i.h" 13#include "fuse_misc.h" 14#include "fuse_opt.h" 15#include "mount_util.h" 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <unistd.h> 20#include <stddef.h> 21#include <string.h> 22#include <fcntl.h> 23#include <errno.h> 24#include <poll.h> 25#include <sys/socket.h> 26#include <sys/un.h> 27#include <sys/wait.h> 28 29#include "fuse_mount_compat.h" 30 31#ifdef __NetBSD__ 32#include <perfuse.h> 33 34#define MS_RDONLY MNT_RDONLY 35#define MS_NOSUID MNT_NOSUID 36#define MS_NODEV MNT_NODEV 37#define MS_NOEXEC MNT_NOEXEC 38#define MS_SYNCHRONOUS MNT_SYNCHRONOUS 39#define MS_NOATIME MNT_NOATIME 40 41#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0) 42#endif 43 44#define FUSERMOUNT_PROG "fusermount3" 45#define FUSE_COMMFD_ENV "_FUSE_COMMFD" 46 47#ifndef HAVE_FORK 48#define fork() vfork() 49#endif 50 51#ifndef MS_DIRSYNC 52#define MS_DIRSYNC 128 53#endif 54 55enum { 56 KEY_KERN_FLAG, 57 KEY_KERN_OPT, 58 KEY_FUSERMOUNT_OPT, 59 KEY_SUBTYPE_OPT, 60 KEY_MTAB_OPT, 61 KEY_ALLOW_OTHER, 62 KEY_RO, 63}; 64 65struct mount_opts { 66 int allow_other; 67 int flags; 68 int auto_unmount; 69 int blkdev; 70 char *fsname; 71 char *subtype; 72 char *subtype_opt; 73 char *mtab_opts; 74 char *fusermount_opts; 75 char *kernel_opts; 76 unsigned max_read; 77}; 78 79#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } 80 81static const struct fuse_opt fuse_mount_opts[] = { 82 FUSE_MOUNT_OPT("allow_other", allow_other), 83 FUSE_MOUNT_OPT("blkdev", blkdev), 84 FUSE_MOUNT_OPT("auto_unmount", auto_unmount), 85 FUSE_MOUNT_OPT("fsname=%s", fsname), 86 FUSE_MOUNT_OPT("max_read=%u", max_read), 87 FUSE_MOUNT_OPT("subtype=%s", subtype), 88 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), 89 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT), 90 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), 91 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), 92 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), 93 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), 94 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), 95 FUSE_OPT_KEY("context=", KEY_KERN_OPT), 96 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT), 97 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT), 98 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT), 99 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), 100 FUSE_OPT_KEY("user=", KEY_MTAB_OPT), 101 FUSE_OPT_KEY("-n", KEY_MTAB_OPT), 102 FUSE_OPT_KEY("-r", KEY_RO), 103 FUSE_OPT_KEY("ro", KEY_KERN_FLAG), 104 FUSE_OPT_KEY("rw", KEY_KERN_FLAG), 105 FUSE_OPT_KEY("suid", KEY_KERN_FLAG), 106 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), 107 FUSE_OPT_KEY("dev", KEY_KERN_FLAG), 108 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG), 109 FUSE_OPT_KEY("exec", KEY_KERN_FLAG), 110 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG), 111 FUSE_OPT_KEY("async", KEY_KERN_FLAG), 112 FUSE_OPT_KEY("sync", KEY_KERN_FLAG), 113 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG), 114 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG), 115 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG), 116 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG), 117 FUSE_OPT_END 118}; 119 120static void exec_fusermount(const char *argv[]) 121{ 122 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); 123 execvp(FUSERMOUNT_PROG, (char **) argv); 124} 125 126void fuse_mount_version(void) 127{ 128 int pid = fork(); 129 if (!pid) { 130 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL }; 131 exec_fusermount(argv); 132 _exit(1); 133 } else if (pid != -1) 134 waitpid(pid, NULL, 0); 135} 136 137struct mount_flags { 138 const char *opt; 139 unsigned long flag; 140 int on; 141}; 142 143static const struct mount_flags mount_flags[] = { 144 {"rw", MS_RDONLY, 0}, 145 {"ro", MS_RDONLY, 1}, 146 {"suid", MS_NOSUID, 0}, 147 {"nosuid", MS_NOSUID, 1}, 148 {"dev", MS_NODEV, 0}, 149 {"nodev", MS_NODEV, 1}, 150 {"exec", MS_NOEXEC, 0}, 151 {"noexec", MS_NOEXEC, 1}, 152 {"async", MS_SYNCHRONOUS, 0}, 153 {"sync", MS_SYNCHRONOUS, 1}, 154 {"noatime", MS_NOATIME, 1}, 155 {"nodiratime", MS_NODIRATIME, 1}, 156 {"norelatime", MS_RELATIME, 0}, 157 {"nostrictatime", MS_STRICTATIME, 0}, 158#ifndef __NetBSD__ 159 {"dirsync", MS_DIRSYNC, 1}, 160#endif 161 {NULL, 0, 0} 162}; 163 164unsigned get_max_read(struct mount_opts *o) 165{ 166 return o->max_read; 167} 168 169static void set_mount_flag(const char *s, int *flags) 170{ 171 int i; 172 173 for (i = 0; mount_flags[i].opt != NULL; i++) { 174 const char *opt = mount_flags[i].opt; 175 if (strcmp(opt, s) == 0) { 176 if (mount_flags[i].on) 177 *flags |= mount_flags[i].flag; 178 else 179 *flags &= ~mount_flags[i].flag; 180 return; 181 } 182 } 183 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n"); 184 abort(); 185} 186 187static int fuse_mount_opt_proc(void *data, const char *arg, int key, 188 struct fuse_args *outargs) 189{ 190 (void) outargs; 191 struct mount_opts *mo = data; 192 193 switch (key) { 194 case KEY_RO: 195 arg = "ro"; 196 /* fall through */ 197 case KEY_KERN_FLAG: 198 set_mount_flag(arg, &mo->flags); 199 return 0; 200 201 case KEY_KERN_OPT: 202 return fuse_opt_add_opt(&mo->kernel_opts, arg); 203 204 case KEY_FUSERMOUNT_OPT: 205 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg); 206 207 case KEY_SUBTYPE_OPT: 208 return fuse_opt_add_opt(&mo->subtype_opt, arg); 209 210 case KEY_MTAB_OPT: 211 return fuse_opt_add_opt(&mo->mtab_opts, arg); 212 } 213 214 /* Pass through unknown options */ 215 return 1; 216} 217 218/* return value: 219 * >= 0 => fd 220 * -1 => error 221 */ 222static int receive_fd(int fd) 223{ 224 struct msghdr msg; 225 struct iovec iov; 226 char buf[1]; 227 int rv; 228 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; 229 struct cmsghdr *cmsg; 230 231 iov.iov_base = buf; 232 iov.iov_len = 1; 233 234 memset(&msg, 0, sizeof(msg)); 235 msg.msg_name = 0; 236 msg.msg_namelen = 0; 237 msg.msg_iov = &iov; 238 msg.msg_iovlen = 1; 239 /* old BSD implementations should use msg_accrights instead of 240 * msg_control; the interface is different. */ 241 msg.msg_control = ccmsg; 242 msg.msg_controllen = sizeof(ccmsg); 243 244 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); 245 if (rv == -1) { 246 perror("recvmsg"); 247 return -1; 248 } 249 if(!rv) { 250 /* EOF */ 251 return -1; 252 } 253 254 cmsg = CMSG_FIRSTHDR(&msg); 255 if (cmsg->cmsg_type != SCM_RIGHTS) { 256 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n", 257 cmsg->cmsg_type); 258 return -1; 259 } 260 return *(int*)CMSG_DATA(cmsg); 261} 262 263void fuse_kern_unmount(const char *mountpoint, int fd) 264{ 265 int res; 266 int pid; 267 268 if (fd != -1) { 269 struct pollfd pfd; 270 271 pfd.fd = fd; 272 pfd.events = 0; 273 res = poll(&pfd, 1, 0); 274 275 /* Need to close file descriptor, otherwise synchronous umount 276 would recurse into filesystem, and deadlock. 277 278 Caller expects fuse_kern_unmount to close the fd, so close it 279 anyway. */ 280 close(fd); 281 282 /* If file poll returns POLLERR on the device file descriptor, 283 then the filesystem is already unmounted or the connection 284 was severed via /sys/fs/fuse/connections/NNN/abort */ 285 if (res == 1 && (pfd.revents & POLLERR)) 286 return; 287 } 288 289 if (geteuid() == 0) { 290 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1); 291 return; 292 } 293 294 res = umount2(mountpoint, 2); 295 if (res == 0) 296 return; 297 298 pid = fork(); 299 if(pid == -1) 300 return; 301 302 if(pid == 0) { 303 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z", 304 "--", mountpoint, NULL }; 305 306 exec_fusermount(argv); 307 _exit(1); 308 } 309 waitpid(pid, NULL, 0); 310} 311 312static int setup_auto_unmount(const char *mountpoint, int quiet) 313{ 314 int fds[2], pid; 315 int res; 316 317 if (!mountpoint) { 318 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n"); 319 return -1; 320 } 321 322 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); 323 if(res == -1) { 324 perror("fuse: socketpair() failed"); 325 return -1; 326 } 327 328 pid = fork(); 329 if(pid == -1) { 330 perror("fuse: fork() failed"); 331 close(fds[0]); 332 close(fds[1]); 333 return -1; 334 } 335 336 if(pid == 0) { 337 char env[10]; 338 const char *argv[32]; 339 int a = 0; 340 341 if (quiet) { 342 int fd = open("/dev/null", O_RDONLY); 343 if (fd != -1) { 344 dup2(fd, 1); 345 dup2(fd, 2); 346 } 347 } 348 349 argv[a++] = FUSERMOUNT_PROG; 350 argv[a++] = "--auto-unmount"; 351 argv[a++] = "--"; 352 argv[a++] = mountpoint; 353 argv[a++] = NULL; 354 355 close(fds[1]); 356 fcntl(fds[0], F_SETFD, 0); 357 snprintf(env, sizeof(env), "%i", fds[0]); 358 setenv(FUSE_COMMFD_ENV, env, 1); 359 exec_fusermount(argv); 360 perror("fuse: failed to exec fusermount3"); 361 _exit(1); 362 } 363 364 close(fds[0]); 365 366 // Now fusermount3 will only exit when fds[1] closes automatically when our 367 // process exits. 368 return 0; 369} 370 371static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo, 372 const char *opts, int quiet) 373{ 374 int fds[2], pid; 375 int res; 376 int rv; 377 378 if (!mountpoint) { 379 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n"); 380 return -1; 381 } 382 383 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); 384 if(res == -1) { 385 perror("fuse: socketpair() failed"); 386 return -1; 387 } 388 389 pid = fork(); 390 if(pid == -1) { 391 perror("fuse: fork() failed"); 392 close(fds[0]); 393 close(fds[1]); 394 return -1; 395 } 396 397 if(pid == 0) { 398 char env[10]; 399 const char *argv[32]; 400 int a = 0; 401 402 if (quiet) { 403 int fd = open("/dev/null", O_RDONLY); 404 if (fd != -1) { 405 dup2(fd, 1); 406 dup2(fd, 2); 407 } 408 } 409 410 argv[a++] = FUSERMOUNT_PROG; 411 if (opts) { 412 argv[a++] = "-o"; 413 argv[a++] = opts; 414 } 415 argv[a++] = "--"; 416 argv[a++] = mountpoint; 417 argv[a++] = NULL; 418 419 close(fds[1]); 420 fcntl(fds[0], F_SETFD, 0); 421 snprintf(env, sizeof(env), "%i", fds[0]); 422 setenv(FUSE_COMMFD_ENV, env, 1); 423 exec_fusermount(argv); 424 perror("fuse: failed to exec fusermount3"); 425 _exit(1); 426 } 427 428 close(fds[0]); 429 rv = receive_fd(fds[1]); 430 431 if (!mo->auto_unmount) { 432 /* with auto_unmount option fusermount3 will not exit until 433 this socket is closed */ 434 close(fds[1]); 435 waitpid(pid, NULL, 0); /* bury zombie */ 436 } 437 438 if (rv >= 0) 439 fcntl(rv, F_SETFD, FD_CLOEXEC); 440 441 return rv; 442} 443 444#ifndef O_CLOEXEC 445#define O_CLOEXEC 0 446#endif 447 448static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, 449 const char *mnt_opts) 450{ 451 char tmp[128]; 452 const char *devname = "/dev/fuse"; 453 char *source = NULL; 454 char *type = NULL; 455 struct stat stbuf; 456 int fd; 457 int res; 458 459 if (!mnt) { 460 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n"); 461 return -1; 462 } 463 464 res = stat(mnt, &stbuf); 465 if (res == -1) { 466 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n", 467 mnt, strerror(errno)); 468 return -1; 469 } 470 471 fd = open(devname, O_RDWR | O_CLOEXEC); 472 if (fd == -1) { 473 if (errno == ENODEV || errno == ENOENT) 474 fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n"); 475 else 476 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", 477 devname, strerror(errno)); 478 return -1; 479 } 480 if (!O_CLOEXEC) 481 fcntl(fd, F_SETFD, FD_CLOEXEC); 482 483 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u", 484 fd, stbuf.st_mode & S_IFMT, getuid(), getgid()); 485 486 res = fuse_opt_add_opt(&mo->kernel_opts, tmp); 487 if (res == -1) 488 goto out_close; 489 490 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + 491 (mo->subtype ? strlen(mo->subtype) : 0) + 492 strlen(devname) + 32); 493 494 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); 495 if (!type || !source) { 496 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n"); 497 goto out_close; 498 } 499 500 strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); 501 if (mo->subtype) { 502 strcat(type, "."); 503 strcat(type, mo->subtype); 504 } 505 strcpy(source, 506 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); 507 508 res = mount(source, mnt, type, mo->flags, mo->kernel_opts); 509 if (res == -1 && errno == ENODEV && mo->subtype) { 510 /* Probably missing subtype support */ 511 strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); 512 if (mo->fsname) { 513 if (!mo->blkdev) 514 sprintf(source, "%s#%s", mo->subtype, 515 mo->fsname); 516 } else { 517 strcpy(source, type); 518 } 519 res = mount(source, mnt, type, mo->flags, mo->kernel_opts); 520 } 521 if (res == -1) { 522 /* 523 * Maybe kernel doesn't support unprivileged mounts, in this 524 * case try falling back to fusermount3 525 */ 526 if (errno == EPERM) { 527 res = -2; 528 } else { 529 int errno_save = errno; 530 if (mo->blkdev && errno == ENODEV && 531 !fuse_mnt_check_fuseblk()) 532 fuse_log(FUSE_LOG_ERR, 533 "fuse: 'fuseblk' support missing\n"); 534 else 535 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n", 536 strerror(errno_save)); 537 } 538 539 goto out_close; 540 } 541 542#ifndef IGNORE_MTAB 543 if (geteuid() == 0) { 544 char *newmnt = fuse_mnt_resolve_path("fuse", mnt); 545 res = -1; 546 if (!newmnt) 547 goto out_umount; 548 549 res = fuse_mnt_add_mount("fuse", source, newmnt, type, 550 mnt_opts); 551 free(newmnt); 552 if (res == -1) 553 goto out_umount; 554 } 555#endif /* IGNORE_MTAB */ 556 free(type); 557 free(source); 558 559 return fd; 560 561out_umount: 562 umount2(mnt, 2); /* lazy umount */ 563out_close: 564 free(type); 565 free(source); 566 close(fd); 567 return res; 568} 569 570static int get_mnt_flag_opts(char **mnt_optsp, int flags) 571{ 572 int i; 573 574 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1) 575 return -1; 576 577 for (i = 0; mount_flags[i].opt != NULL; i++) { 578 if (mount_flags[i].on && (flags & mount_flags[i].flag) && 579 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) 580 return -1; 581 } 582 return 0; 583} 584 585struct mount_opts *parse_mount_opts(struct fuse_args *args) 586{ 587 struct mount_opts *mo; 588 589 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts)); 590 if (mo == NULL) 591 return NULL; 592 593 memset(mo, 0, sizeof(struct mount_opts)); 594 mo->flags = MS_NOSUID | MS_NODEV; 595 596 if (args && 597 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) 598 goto err_out; 599 600 return mo; 601 602err_out: 603 destroy_mount_opts(mo); 604 return NULL; 605} 606 607void destroy_mount_opts(struct mount_opts *mo) 608{ 609 free(mo->fsname); 610 free(mo->subtype); 611 free(mo->fusermount_opts); 612 free(mo->subtype_opt); 613 free(mo->kernel_opts); 614 free(mo->mtab_opts); 615 free(mo); 616} 617 618 619int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo) 620{ 621 int res = -1; 622 char *mnt_opts = NULL; 623 624 res = -1; 625 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1) 626 goto out; 627 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1) 628 goto out; 629 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1) 630 goto out; 631 632 res = fuse_mount_sys(mountpoint, mo, mnt_opts); 633 if (res >= 0 && mo->auto_unmount) { 634 if(0 > setup_auto_unmount(mountpoint, 0)) { 635 // Something went wrong, let's umount like in fuse_mount_sys. 636 umount2(mountpoint, MNT_DETACH); /* lazy umount */ 637 res = -1; 638 } 639 } else if (res == -2) { 640 if (mo->fusermount_opts && 641 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1) 642 goto out; 643 644 if (mo->subtype) { 645 char *tmp_opts = NULL; 646 647 res = -1; 648 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || 649 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) { 650 free(tmp_opts); 651 goto out; 652 } 653 654 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1); 655 free(tmp_opts); 656 if (res == -1) 657 res = fuse_mount_fusermount(mountpoint, mo, 658 mnt_opts, 0); 659 } else { 660 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0); 661 } 662 } 663out: 664 free(mnt_opts); 665 return res; 666} 667