/* FUSE: Filesystem in Userspace Copyright (C) 2005-2008 Csaba Henk This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ #include "fuse_i.h" #include "fuse_misc.h" #include "fuse_opt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define FUSERMOUNT_PROG "mount_fusefs" #define FUSE_DEV_TRUNK "/dev/fuse" enum { KEY_ALLOW_ROOT, KEY_RO, KEY_HELP, KEY_VERSION, KEY_KERN }; struct mount_opts { int allow_other; int allow_root; int ishelp; char *kernel_opts; }; #define FUSE_DUAL_OPT_KEY(templ, key) \ FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key) static const struct fuse_opt fuse_mount_opts[] = { { "allow_other", offsetof(struct mount_opts, allow_other), 1 }, { "allow_root", offsetof(struct mount_opts, allow_root), 1 }, FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), FUSE_OPT_KEY("-r", KEY_RO), FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("-V", KEY_VERSION), FUSE_OPT_KEY("--version", KEY_VERSION), /* standard FreeBSD mount options */ FUSE_DUAL_OPT_KEY("dev", KEY_KERN), FUSE_DUAL_OPT_KEY("async", KEY_KERN), FUSE_DUAL_OPT_KEY("atime", KEY_KERN), FUSE_DUAL_OPT_KEY("dev", KEY_KERN), FUSE_DUAL_OPT_KEY("exec", KEY_KERN), FUSE_DUAL_OPT_KEY("suid", KEY_KERN), FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN), FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN), FUSE_DUAL_OPT_KEY("sync", KEY_KERN), FUSE_DUAL_OPT_KEY("union", KEY_KERN), FUSE_DUAL_OPT_KEY("userquota", KEY_KERN), FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN), FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN), FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN), FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN), FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN), FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN), FUSE_DUAL_OPT_KEY("acls", KEY_KERN), FUSE_DUAL_OPT_KEY("force", KEY_KERN), FUSE_DUAL_OPT_KEY("update", KEY_KERN), FUSE_DUAL_OPT_KEY("ro", KEY_KERN), FUSE_DUAL_OPT_KEY("rw", KEY_KERN), FUSE_DUAL_OPT_KEY("auto", KEY_KERN), /* options supported under both Linux and FBSD */ FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN), FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN), FUSE_OPT_KEY("max_read=", KEY_KERN), FUSE_OPT_KEY("subtype=", KEY_KERN), /* FBSD FUSE specific mount options */ FUSE_DUAL_OPT_KEY("private", KEY_KERN), FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN), FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN), FUSE_OPT_KEY("nosync_unmount", KEY_KERN), /* stock FBSD mountopt parsing routine lets anything be negated... */ /* * Linux specific mount options, but let just the mount util * handle them */ FUSE_OPT_KEY("fsname=", KEY_KERN), FUSE_OPT_KEY("nonempty", KEY_KERN), FUSE_OPT_KEY("large_read", KEY_KERN), FUSE_OPT_END }; static void mount_help(void) { fprintf(stderr, " -o allow_root allow access to root\n" ); system(FUSERMOUNT_PROG " --help"); fputc('\n', stderr); } static void mount_version(void) { system(FUSERMOUNT_PROG " --version"); } static int fuse_mount_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { struct mount_opts *mo = data; switch (key) { case KEY_ALLOW_ROOT: if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 || fuse_opt_add_arg(outargs, "-oallow_root") == -1) return -1; return 0; case KEY_RO: arg = "ro"; /* fall through */ case KEY_KERN: return fuse_opt_add_opt(&mo->kernel_opts, arg); case KEY_HELP: mount_help(); mo->ishelp = 1; break; case KEY_VERSION: mount_version(); mo->ishelp = 1; break; } return 1; } void fuse_unmount_compat22(const char *mountpoint) { char dev[128]; char *ssc, *umount_cmd; FILE *sf; int rv; char seekscript[] = /* error message is annoying in help output */ "exec 2>/dev/null; " "/usr/bin/fstat " FUSE_DEV_TRUNK "* | " "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; " " { if ($3 == %d) print $10; }' | " "/usr/bin/sort | " "/usr/bin/uniq | " "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'"; (void) mountpoint; /* * If we don't know the fd, we have to resort to the scripted * solution -- iterating over the fd-s is unpractical, as we * don't know how many of open files we have. (This could be * looked up in procfs -- however, that's optional on FBSD; or * read out from the kmem -- however, that's bound to * privileges (in fact, that's what happens when we call the * setgid kmem fstat(1) utility). */ if (asprintf(&ssc, seekscript, getpid()) == -1) return; errno = 0; sf = popen(ssc, "r"); free(ssc); if (! sf) return; fgets(dev, sizeof(dev), sf); rv = pclose(sf); if (rv) return; if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1) return; system(umount_cmd); free(umount_cmd); } static void do_unmount(char *dev, int fd) { char device_path[SPECNAMELEN + 12]; const char *argv[4]; const char umount_cmd[] = "/sbin/umount"; pid_t pid; snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev); argv[0] = umount_cmd; argv[1] = "-f"; argv[2] = device_path; argv[3] = NULL; pid = fork(); if (pid == -1) return; if (pid == 0) { close(fd); execvp(umount_cmd, (char **)argv); exit(1); } waitpid(pid, NULL, 0); } void fuse_kern_unmount(const char *mountpoint, int fd) { char *ep, dev[128]; struct stat sbuf; (void)mountpoint; if (fstat(fd, &sbuf) == -1) goto out; devname_r(sbuf.st_rdev, S_IFCHR, dev, 128); if (strncmp(dev, "fuse", 4)) goto out; strtol(dev + 4, &ep, 10); if (*ep != '\0') goto out; do_unmount(dev, fd); out: close(fd); } /* Check if kernel is doing init in background */ static int init_backgrounded(void) { unsigned ibg, len; len = sizeof(ibg); if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0)) return 0; return ibg; } static int fuse_mount_core(const char *mountpoint, const char *opts) { const char *mountprog = FUSERMOUNT_PROG; int fd; char *fdnam, *dev; pid_t pid, cpid; int status; fdnam = getenv("FUSE_DEV_FD"); if (fdnam) { char *ep; fd = strtol(fdnam, &ep, 10); if (*ep != '\0') { fprintf(stderr, "invalid value given in FUSE_DEV_FD\n"); return -1; } if (fd < 0) return -1; goto mount; } dev = getenv("FUSE_DEV_NAME"); if (! dev) dev = (char *)FUSE_DEV_TRUNK; if ((fd = open(dev, O_RDWR)) < 0) { perror("fuse: failed to open fuse device"); return -1; } mount: if (getenv("FUSE_NO_MOUNT") || ! mountpoint) goto out; pid = fork(); cpid = pid; if (pid == -1) { perror("fuse: fork() failed"); close(fd); return -1; } if (pid == 0) { if (! init_backgrounded()) { /* * If init is not backgrounded, we have to * call the mount util backgrounded, to avoid * deadlock. */ pid = fork(); if (pid == -1) { perror("fuse: fork() failed"); close(fd); exit(1); } } if (pid == 0) { const char *argv[32]; int a = 0; if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) { perror("fuse: failed to assemble mount arguments"); exit(1); } argv[a++] = mountprog; if (opts) { argv[a++] = "-o"; argv[a++] = opts; } argv[a++] = fdnam; argv[a++] = mountpoint; argv[a++] = NULL; execvp(mountprog, (char **) argv); perror("fuse: failed to exec mount program"); exit(1); } exit(0); } if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) { perror("fuse: failed to mount file system"); close(fd); return -1; } out: return fd; } int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) { struct mount_opts mo; int res = -1; memset(&mo, 0, sizeof(mo)); /* mount util should not try to spawn the daemon */ setenv("MOUNT_FUSEFS_SAFE", "1", 1); /* to notify the mount util it's called from lib */ setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1); if (args && fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) return -1; if (mo.allow_other && mo.allow_root) { fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); goto out; } if (mo.ishelp) return 0; res = fuse_mount_core(mountpoint, mo.kernel_opts); out: free(mo.kernel_opts); return res; } FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");