Refactor libfuse option processing to better support options in the
authorhelg <helg@openbsd.org>
Mon, 18 Dec 2017 11:41:41 +0000 (11:41 +0000)
committerhelg <helg@openbsd.org>
Mon, 18 Dec 2017 11:41:41 +0000 (11:41 +0000)
future. Immediate benefits are that gid, uid and umask are now
supported and max_read is now honoured for all file systems. Mounting
read_only is now possible but requires more thorough testing.

ok mpi@

lib/libfuse/fuse.c
lib/libfuse/fuse_private.h

index 2b9f9e5..cd7773b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: fuse.c,v 1.41 2017/12/15 16:40:33 jca Exp $ */
+/* $OpenBSD: fuse.c,v 1.42 2017/12/18 11:41:41 helg Exp $ */
 /*
  * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
  *
@@ -23,6 +23,7 @@
 
 #include <errno.h>
 #include <signal.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -34,7 +35,6 @@
 static volatile sig_atomic_t sigraised = 0;
 static volatile sig_atomic_t signum = 0;
 static struct fuse_context *ictx = NULL;
-static int max_read = FUSEBUFMAXSIZE;
 
 enum {
        KEY_DEBUG,
@@ -46,21 +46,72 @@ enum {
        KEY_STUB
 };
 
+/* options supported by fuse_parse_cmdline */
 static struct fuse_opt fuse_core_opts[] = {
+       FUSE_OPT_KEY("-d",                      KEY_DEBUG),
+       FUSE_OPT_KEY("debug",                   KEY_DEBUG),
+       FUSE_OPT_KEY("-f",                      KEY_FOREGROUND),
        FUSE_OPT_KEY("-h",                      KEY_HELP),
        FUSE_OPT_KEY("--help",                  KEY_HELP),
        FUSE_OPT_KEY("-ho",                     KEY_HELP_WITHOUT_HEADER),
+       FUSE_OPT_KEY("-s",                      KEY_STUB),
        FUSE_OPT_KEY("-V",                      KEY_VERSION),
        FUSE_OPT_KEY("--version",               KEY_VERSION),
-       FUSE_OPT_KEY("max_read=",               KEY_MAXREAD),
+       FUSE_OPT_END
+};
+
+/* options supported by fuse_new */
+#define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_config, m), 1}
+static struct fuse_opt fuse_lib_opts[] = {
+       FUSE_OPT_KEY("ac_attr_timeout=",        KEY_STUB),
+       FUSE_OPT_KEY("allow_other",             KEY_STUB),
+       FUSE_OPT_KEY("allow_root",              KEY_STUB),
+       FUSE_OPT_KEY("attr_timeout=",           KEY_STUB),
+       FUSE_OPT_KEY("auto_cache",              KEY_STUB),
+       FUSE_OPT_KEY("noauto_cache",            KEY_STUB),
+       FUSE_OPT_KEY("big_writes",              KEY_STUB),
        FUSE_OPT_KEY("debug",                   KEY_DEBUG),
        FUSE_OPT_KEY("-d",                      KEY_DEBUG),
-       FUSE_OPT_KEY("-f",                      KEY_FOREGROUND),
-       FUSE_OPT_KEY("-s",                      KEY_STUB),
+       FUSE_OPT_KEY("entry_timeout=",          KEY_STUB),
+       FUSE_LIB_OPT("gid=",                    set_gid),
+       FUSE_LIB_OPT("gid=%u",                  gid),
+       FUSE_OPT_KEY("hard_remove",             KEY_STUB),
+       FUSE_OPT_KEY("intr_signal",             KEY_STUB),
+       FUSE_OPT_KEY("kernel_cache",            KEY_STUB),
+       FUSE_OPT_KEY("large_read",              KEY_STUB),
+       FUSE_OPT_KEY("modules=",                KEY_STUB),
+       FUSE_OPT_KEY("negative_timeout=",       KEY_STUB),
+       FUSE_OPT_KEY("readdir_ino",             KEY_STUB),
+       FUSE_OPT_KEY("relatime",                KEY_STUB),
+       FUSE_OPT_KEY("subtype=",                KEY_STUB),
+       FUSE_LIB_OPT("uid=",                    set_uid),
+       FUSE_LIB_OPT("uid=%u",                  uid),
        FUSE_OPT_KEY("use_ino",                 KEY_STUB),
-       FUSE_OPT_KEY("big_writes",              KEY_STUB),
-       FUSE_OPT_KEY("default_permissions",     KEY_STUB),
-       FUSE_OPT_KEY("fsname=",                 KEY_STUB),
+       FUSE_OPT_KEY("dmask=%o",                KEY_STUB),
+       FUSE_OPT_KEY("fmask=%o",                KEY_STUB),
+       FUSE_LIB_OPT("umask=",                  set_mode),
+       FUSE_LIB_OPT("umask=%o",                umask),
+       FUSE_OPT_END
+};
+
+/* options supported by fuse_mount */
+#define FUSE_MOUNT_OPT(o, m) {o, offsetof(struct fuse_mount_opts, m), 1}
+static struct fuse_opt fuse_mount_opts[] = {
+       FUSE_OPT_KEY("async_read",              KEY_STUB),
+       FUSE_OPT_KEY("blkdev",                  KEY_STUB),
+       FUSE_OPT_KEY("blksize=",                KEY_STUB),
+       FUSE_MOUNT_OPT("default_permissions",   def_perms),
+       FUSE_OPT_KEY("direct_io",               KEY_STUB),
+       FUSE_MOUNT_OPT("fsname=%s",             fsname),
+       FUSE_MOUNT_OPT("max_read=%u",           max_read),
+       FUSE_OPT_KEY("max_readahead",           KEY_STUB),
+       FUSE_OPT_KEY("max_write",               KEY_STUB),
+       FUSE_MOUNT_OPT("noatime",               noatime),
+       FUSE_MOUNT_OPT("nonempty",              nonempty),
+       FUSE_MOUNT_OPT("-r",                    rdonly),
+       FUSE_MOUNT_OPT("ro",                    rdonly),
+       FUSE_OPT_KEY("ro_fallback",             KEY_STUB),
+       FUSE_OPT_KEY("sync_read",               KEY_STUB),
        FUSE_OPT_END
 };
 
@@ -221,11 +272,13 @@ fuse_loop(struct fuse *fuse)
 DEF(fuse_loop);
 
 struct fuse_chan *
-fuse_mount(const char *dir, unused struct fuse_args *args)
+fuse_mount(const char *dir, struct fuse_args *args)
 {
        struct fusefs_args fargs;
+       struct fuse_mount_opts opts;
        struct fuse_chan *fc;
        const char *errcause;
+       int mnt_flags;
 
        if (dir == NULL)
                return (NULL);
@@ -243,9 +296,27 @@ fuse_mount(const char *dir, unused struct fuse_args *args)
                goto bad;
        }
 
+       bzero(&opts, sizeof(opts));
+       if (fuse_opt_parse(args, &opts, fuse_mount_opts, NULL) == -1)
+               goto bad;
+
+       mnt_flags = 0;
+       if (opts.rdonly)
+               mnt_flags |= MNT_RDONLY;
+       if (opts.noatime)
+               mnt_flags |= MNT_NOATIME;
+
+       if (opts.max_read > FUSEBUFMAXSIZE) {
+               fprintf(stderr, "fuse: invalid max_read (%d > %d)\n",
+                   opts.max_read, FUSEBUFMAXSIZE);
+               goto bad;
+       }
+
+       bzero(&fargs, sizeof(fargs));
        fargs.fd = fc->fd;
-       fargs.max_read = max_read;
-       if (mount(MOUNT_FUSEFS, fc->dir, 0, &fargs)) {
+       fargs.max_read = opts.max_read;
+
+       if (mount(MOUNT_FUSEFS, fc->dir, mnt_flags, &fargs)) {
                switch (errno) {
                case EMFILE:
                        errcause = "mount table full";
@@ -285,7 +356,7 @@ DEF(fuse_unmount);
 int
 fuse_is_lib_option(const char *opt)
 {
-       return (fuse_opt_match(fuse_core_opts, opt));
+       return (fuse_opt_match(fuse_lib_opts, opt));
 }
 
 int
@@ -310,8 +381,27 @@ fuse_loop_mt(unused struct fuse *fuse)
        return (-1);
 }
 
+static int
+ifuse_lib_opt_proc(void *data, const char *arg, int key,
+    unused struct fuse_args *args)
+{
+       switch (key) {
+       case KEY_STUB:
+               return (0);
+       case KEY_DEBUG:
+               ifuse_debug_init();
+               break;
+       default:
+               fprintf(stderr, "fuse: unrecognised option %s\n", arg);
+               return (-1);
+       }
+
+       /* Keep unknown options. */
+       return (1);
+}
+
 struct fuse *
-fuse_new(struct fuse_chan *fc, unused struct fuse_args *args,
+fuse_new(struct fuse_chan *fc, struct fuse_args *args,
     const struct fuse_operations *ops, unused size_t size,
     void *userdata)
 {
@@ -327,6 +417,12 @@ fuse_new(struct fuse_chan *fc, unused struct fuse_args *args,
        /* copy fuse ops to their own structure */
        memcpy(&fuse->op, ops, sizeof(fuse->op));
 
+       if (fuse_opt_parse(args, &fuse->conf, fuse_lib_opts,
+           ifuse_lib_opt_proc) == -1) {
+               free(fuse);
+               return (NULL);
+       }
+
        fuse->fc = fc;
        fuse->max_ino = FUSE_ROOT_INO;
        fuse->se.args = fuse;
@@ -354,7 +450,7 @@ fuse_daemonize(int foreground)
        if (foreground)
                return (0);
 
-       return (daemon(0,0));
+       return (daemon(0, 0));
 }
 DEF(fuse_daemonize);
 
@@ -402,7 +498,7 @@ dump_help(void)
        fprintf(stderr, "FUSE options:\n"
            "    -d   -o debug          enable debug output (implies -f)\n"
            "    -f                     run in foreground\n"
-           "    -V                     print fuse version\n"
+           "    -V   --version         print fuse version\n"
            "\n");
 }
 
@@ -417,74 +513,59 @@ static int
 ifuse_process_opt(void *data, const char *arg, int key,
     unused struct fuse_args *args)
 {
-       struct fuse_core_opt *opt = data;
+       struct fuse_core_opts *opt = data;
        struct stat st;
-       const char *err;
        int res;
 
        switch (key) {
-               case KEY_STUB:
-                       return (0);
-               case KEY_DEBUG:
-                       ifuse_debug_init();
-                       /* falls through */
-               case KEY_FOREGROUND:
-                       opt->foreground = 1;
-                       return (0);
-               case KEY_HELP:
-               case KEY_HELP_WITHOUT_HEADER:
-                       dump_help();
-                       return (-1);
-               case KEY_VERSION:
-                       dump_version();
-                       return (-1);
-               case KEY_MAXREAD:
-                       res = strtonum(arg, 0, FUSEBUFMAXSIZE, &err);
-                       if (err) {
-                               fprintf(stderr, "fuse: max_read %s\n", err);
+       case KEY_STUB:
+               return (0);
+       case KEY_DEBUG:
+               ifuse_debug_init();
+               /* falls through */
+       case KEY_FOREGROUND:
+               opt->foreground = 1;
+               return (0);
+       case KEY_HELP:
+       case KEY_HELP_WITHOUT_HEADER:
+               dump_help();
+               return (-1);
+       case KEY_VERSION:
+               dump_version();
+               return (-1);
+       case FUSE_OPT_KEY_NONOPT:
+               if (opt->mp == NULL) {
+                       opt->mp = realpath(arg, opt->mp);
+                       if (opt->mp == NULL) {
+                               fprintf(stderr, "fuse: realpath: "
+                                   "%s : %s\n", arg, strerror(errno));
                                return (-1);
                        }
-                       max_read = res;
-                       break;
-               case FUSE_OPT_KEY_NONOPT:
-                       if (opt->mp == NULL) {
-                               opt->mp = realpath(arg, opt->mp);
-                               if (opt->mp == NULL) {
-                                       fprintf(stderr, "fuse: realpath: "
-                                           "%s : %s\n", arg, strerror(errno));
-                                       return (-1);
-                               }
 
-                               res = stat(opt->mp, &st);
-                               if (res == -1) {
-                                       fprintf(stderr, "fuse: bad mount point "
-                                           "%s : %s\n", arg, strerror(errno));
-                                       return (-1);
-                               }
+                       res = stat(opt->mp, &st);
+                       if (res == -1) {
+                               fprintf(stderr, "fuse: bad mount point "
+                                   "%s : %s\n", arg, strerror(errno));
+                               return (-1);
+                       }
 
-                               if (!S_ISDIR(st.st_mode)) {
-                                       fprintf(stderr, "fuse: bad mount point "
-                                           "%s : %s\n", arg,
-                                           strerror(ENOTDIR));
-                                       return (-1);
-                               }
-                       } else {
-                               fprintf(stderr, "fuse: invalid argument %s\n",
-                                   arg);
+                       if (!S_ISDIR(st.st_mode)) {
+                               fprintf(stderr, "fuse: bad mount point "
+                                   "%s : %s\n", arg, strerror(ENOTDIR));
                                return (-1);
                        }
-                       break;
-               default:
-                       fprintf(stderr, "fuse: unknown option %s\n", arg);
-                       return (-1);
+               }
+               return (0);
        }
-       return (0);
+
+       /* Pass through unknown options. */
+       return (1);
 }
 
 int
 fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg)
 {
-       struct fuse_core_opt opt;
+       struct fuse_core_opts opt;
 
        bzero(&opt, sizeof(opt));
        if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1)
@@ -556,10 +637,10 @@ fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
 
        fuse_daemonize(fg);
 
-       if ((fc = fuse_mount(dir, NULL)) == NULL)
+       if ((fc = fuse_mount(dir, &args)) == NULL)
                goto err;
 
-       if ((fuse = fuse_new(fc, NULL, ops, size, data)) == NULL) {
+       if ((fuse = fuse_new(fc, &args, ops, size, data)) == NULL) {
                fuse_unmount(dir, fc);
                close(fc->fd);
                free(fc->dir);
@@ -567,13 +648,19 @@ fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
                goto err;
        }
 
+       /* args are no longer needed */
+       fuse_opt_free_args(&args);
+
        if (fuse_set_signal_handlers(fuse_get_session(fuse)) == -1) {
                fuse_unmount(dir, fc);
                fuse_destroy(fuse);
                goto err;
        }
 
-       if (mp != NULL)
+       /* the caller frees dir, but we do it if the caller doesn't want it */
+       if (mp == NULL)
+               free(dir);
+       else
                *mp = dir;
 
        return (fuse);
@@ -589,7 +676,7 @@ fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data)
        struct fuse *fuse;
 
        fuse = fuse_setup(argc, argv, ops, sizeof(*ops), NULL, NULL, data);
-       if (!fuse)
+       if (fuse == NULL)
                return (-1);
 
        return (fuse_loop(fuse));
index 4fac375..53f1400 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: fuse_private.h,v 1.16 2017/12/15 16:40:33 jca Exp $ */
+/* $OpenBSD: fuse_private.h,v 1.17 2017/12/18 11:41:41 helg Exp $ */
 /*
  * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
  *
@@ -73,11 +73,20 @@ struct fuse_config {
        int                     set_gid;
 };
 
-struct fuse_core_opt {
+struct fuse_core_opts {
        char                    *mp;
        int                     foreground;
 };
 
+struct fuse_mount_opts {
+       char                    *fsname;
+       int                     def_perms;
+       int                     max_read;
+       int                     noatime;
+       int                     nonempty;
+       int                     rdonly;
+};
+
 struct fuse {
        struct fuse_chan        *fc;
        struct fuse_operations  op;