Add support for direct mounting by libfuse
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 30 Mar 2007 21:36:41 +0000 (21:36 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 30 Mar 2007 21:36:41 +0000 (21:36 +0000)
ChangeLog
lib/mount.c

index 75b10579ad2ac651f59d2333b492cc4646c667ff..2db1eaa27768d52c2431ac4b8be31ecd6a4fc19c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2007-03-30  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Add support for direct mounting by libfuse.  Fall back on
+       calling fusermount if it doesn't work
+
 2007-03-14  Miklos Szeredi <miklos@szeredi.hu>
 
        * Released 2.7.0-pre1
index cbdedf36851d01692cdbe2a3be175ef2a53f71a2..cdfd7d232d0da07cbbbf869d24a92451ee4011f0 100644 (file)
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/wait.h>
+#include <sys/mount.h>
 
 #define FUSERMOUNT_PROG         "fusermount"
 #define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
 #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;
 }