-/* $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>
*
#include <errno.h>
#include <signal.h>
+#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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,
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
};
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);
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";
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
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)
{
/* 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;
if (foreground)
return (0);
- return (daemon(0,0));
+ return (daemon(0, 0));
}
DEF(fuse_daemonize);
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");
}
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)
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);
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);
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));