From: Miklos Szeredi Date: Fri, 30 Mar 2007 21:36:41 +0000 (+0000) Subject: Add support for direct mounting by libfuse X-Git-Tag: fuse_2_7_0_rc1~14 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=f69d432b7a9231ab4e4e92a361eb41e8a2cf7802;p=qemu-gpiodev%2Flibfuse.git Add support for direct mounting by libfuse --- diff --git a/ChangeLog b/ChangeLog index 75b1057..2db1eaa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-03-30 Miklos Szeredi + + * Add support for direct mounting by libfuse. Fall back on + calling fusermount if it doesn't work + 2007-03-14 Miklos Szeredi * Released 2.7.0-pre1 diff --git a/lib/mount.c b/lib/mount.c index cbdedf3..cdfd7d2 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -21,6 +21,7 @@ #include #include #include +#include #define FUSERMOUNT_PROG "fusermount" #define FUSE_COMMFD_ENV "_FUSE_COMMFD" @@ -29,8 +30,15 @@ #define fork() vfork() #endif +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + enum { - KEY_KERN, + KEY_KERN_FLAG, + KEY_KERN_OPT, + KEY_FUSERMOUNT_OPT, + KEY_MTAB_OPT, KEY_ALLOW_ROOT, KEY_RO, KEY_HELP, @@ -41,36 +49,50 @@ struct mount_opts { int allow_other; int allow_root; int ishelp; + int flags; + int nonempty; + int blkdev; + int large_read; + char *fsname; + char *mtab_opts; + char *fusermount_opts; char *kernel_opts; }; +#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } + 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_other", KEY_KERN), + FUSE_MOUNT_OPT("allow_other", allow_other), + FUSE_MOUNT_OPT("allow_root", allow_root), + FUSE_MOUNT_OPT("nonempty", nonempty), + FUSE_MOUNT_OPT("blkdev", blkdev), + FUSE_MOUNT_OPT("fsname=%s", fsname), + FUSE_MOUNT_OPT("large_read", large_read), + FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), - FUSE_OPT_KEY("nonempty", KEY_KERN), - FUSE_OPT_KEY("blkdev", KEY_KERN), - FUSE_OPT_KEY("blksize=", KEY_KERN), - FUSE_OPT_KEY("default_permissions", KEY_KERN), - FUSE_OPT_KEY("fsname=", KEY_KERN), - FUSE_OPT_KEY("large_read", KEY_KERN), - FUSE_OPT_KEY("max_read=", KEY_KERN), + FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("large_read", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), + FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("user=", KEY_MTAB_OPT), FUSE_OPT_KEY("-r", KEY_RO), - FUSE_OPT_KEY("ro", KEY_KERN), - FUSE_OPT_KEY("rw", KEY_KERN), - FUSE_OPT_KEY("suid", KEY_KERN), - FUSE_OPT_KEY("nosuid", KEY_KERN), - FUSE_OPT_KEY("dev", KEY_KERN), - FUSE_OPT_KEY("nodev", KEY_KERN), - FUSE_OPT_KEY("exec", KEY_KERN), - FUSE_OPT_KEY("noexec", KEY_KERN), - FUSE_OPT_KEY("async", KEY_KERN), - FUSE_OPT_KEY("sync", KEY_KERN), - FUSE_OPT_KEY("dirsync", KEY_KERN), - FUSE_OPT_KEY("atime", KEY_KERN), - FUSE_OPT_KEY("noatime", KEY_KERN), + FUSE_OPT_KEY("ro", KEY_KERN_FLAG), + FUSE_OPT_KEY("rw", KEY_KERN_FLAG), + FUSE_OPT_KEY("suid", KEY_KERN_FLAG), + FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), + FUSE_OPT_KEY("dev", KEY_KERN_FLAG), + FUSE_OPT_KEY("nodev", KEY_KERN_FLAG), + FUSE_OPT_KEY("exec", KEY_KERN_FLAG), + FUSE_OPT_KEY("noexec", KEY_KERN_FLAG), + FUSE_OPT_KEY("async", KEY_KERN_FLAG), + FUSE_OPT_KEY("sync", KEY_KERN_FLAG), + FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG), + FUSE_OPT_KEY("atime", KEY_KERN_FLAG), + FUSE_OPT_KEY("noatime", KEY_KERN_FLAG), FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("-V", KEY_VERSION), @@ -109,6 +131,47 @@ static void mount_version(void) waitpid(pid, NULL, 0); } +struct mount_flags { + const char *opt; + unsigned long flag; + int on; +}; + +static struct mount_flags mount_flags[] = { + {"rw", MS_RDONLY, 0}, + {"ro", MS_RDONLY, 1}, + {"suid", MS_NOSUID, 0}, + {"nosuid", MS_NOSUID, 1}, + {"dev", MS_NODEV, 0}, + {"nodev", MS_NODEV, 1}, + {"exec", MS_NOEXEC, 0}, + {"noexec", MS_NOEXEC, 1}, + {"async", MS_SYNCHRONOUS, 0}, + {"sync", MS_SYNCHRONOUS, 1}, + {"atime", MS_NOATIME, 0}, + {"noatime", MS_NOATIME, 1}, + {"dirsync", MS_DIRSYNC, 1}, + {NULL, 0, 0} +}; + +static void set_mount_flag(const char *s, int *flags) +{ + int i; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + const char *opt = mount_flags[i].opt; + if (strcmp(opt, s) == 0) { + if (mount_flags[i].on) + *flags |= mount_flags[i].flag; + else + *flags &= ~mount_flags[i].flag; + return; + } + } + fprintf(stderr, "fuse: internal error, can't find mount flag\n"); + abort(); +} + static int fuse_mount_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { @@ -124,10 +187,19 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key, case KEY_RO: arg = "ro"; /* fall through */ + case KEY_KERN_FLAG: + set_mount_flag(arg, &mo->flags); + return 0; - case KEY_KERN: + case KEY_KERN_OPT: return fuse_opt_add_opt(&mo->kernel_opts, arg); + case KEY_FUSERMOUNT_OPT: + return fuse_opt_add_opt(&mo->fusermount_opts, arg); + + case KEY_MTAB_OPT: + return fuse_opt_add_opt(&mo->mtab_opts, arg); + case KEY_HELP: mount_help(); mo->ishelp = 1; @@ -280,12 +352,109 @@ int fuse_mount_compat22(const char *mountpoint, const char *opts) return rv; } +static int add_mount(const char *fsname, const char *mnt, const char *type, + const char *opts) +{ + int res; + int status; + + res = fork(); + if (res == -1) { + perror("fork"); + return -1; + } + if (res == 0) { + setuid(geteuid()); + execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts, + fsname, mnt, NULL); + perror("execl /bin/mount"); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + perror("waitpid"); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, + const char *mnt_opts) +{ + const char *type = mo->blkdev ? "fuseblk" : "fuse"; + char tmp[128]; + const char *devname = "/dev/fuse"; + struct stat stbuf; + int fd; + int res; + + /* For now silently fall back to fusermount if something doesn't work */ + + /* FIXME: check non-empty mountpoint*/ + + if (mo->large_read) + return -1; + + res = lstat(mnt, &stbuf); + if (res == -1) + return -1; + + fd = open(devname, O_RDWR); + if (fd == -1) + return -1; + + if (mo->fsname) + devname = mo->fsname; + + snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, + stbuf.st_mode & S_IFMT, getuid(), getgid()); + + if (fuse_opt_add_opt(&mo->kernel_opts, tmp) == -1) { + close(fd); + return -1; + } + res = mount(devname, mnt, type, mo->flags, mo->kernel_opts); + if (res == -1) { + close(fd); + return -1; + } + if (geteuid() == 0) { + res = add_mount(devname, mnt, type, mnt_opts); + if (res == -1) { + umount2(mnt, 2); /* lazy umount */ + close(fd); + return -1; + } + } + return fd; +} + +static int get_mnt_flag_opts(char **mnt_optsp, int flags) +{ + int i; + + if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1) + return -1; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + if (mount_flags[i].on && (flags & mount_flags[i].flag) && + fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) + return -1; + } + return 0; +} + int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) { struct mount_opts mo; int res = -1; + char *mnt_opts = NULL; memset(&mo, 0, sizeof(mo)); + mo.flags = MS_NOSUID | MS_NODEV; if (args && fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) @@ -295,12 +464,32 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); goto out; } + res = 0; if (mo.ishelp) - return 0; + goto out; + + res = -1; + if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1) + goto out; + if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1) + goto out; + if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) + goto out; - res = fuse_mount_compat22(mountpoint, mo.kernel_opts); + res = fuse_mount_sys(mountpoint, &mo, mnt_opts); + if (res == -1) { + if (mo.fusermount_opts && + fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) + goto out; + + res = fuse_mount_compat22(mountpoint, mnt_opts); + } out: + free(mnt_opts); + free(mo.fsname); + free(mo.fusermount_opts); free(mo.kernel_opts); + free(mo.mtab_opts); return res; }