From 659743bc8870afd42bcacecbd6fa571befd5579f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi <miklos@szeredi.hu> Date: Fri, 9 Dec 2005 17:41:42 +0000 Subject: [PATCH] added option parsing --- ChangeLog | 5 + include/Makefile.am | 3 +- include/fuse_opt.h | 212 ++++++++++++++++++++++++ lib/Makefile.am | 1 + lib/fuse.c | 293 +++++++++++++-------------------- lib/fuse_lowlevel.c | 53 ++---- lib/fuse_opt.c | 363 +++++++++++++++++++++++++++++++++++++++++ lib/fuse_versionscript | 5 + lib/helper.c | 320 +++++++++++++++--------------------- 9 files changed, 854 insertions(+), 401 deletions(-) create mode 100644 include/fuse_opt.h create mode 100644 lib/fuse_opt.c diff --git a/ChangeLog b/ChangeLog index 86a4365..f71b18d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-12-09 Miklos Szeredi <miklos@szeredi.hu> + + * libfuse: added option parsing interface, defined in + <fuse_opt.h>. + 2005-12-07 Miklos Szeredi <miklos@szeredi.hu> * Return EIO for file operations (read, write, fsync, flush) on diff --git a/include/Makefile.am b/include/Makefile.am index 41d9a81..81d2661 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -7,7 +7,8 @@ fuseinclude_HEADERS = \ fuse_compat.h \ fuse_common.h \ fuse_lowlevel.h \ - fuse_lowlevel_compat.h + fuse_lowlevel_compat.h \ + fuse_opt.h include_HEADERS = old/fuse.h diff --git a/include/fuse_opt.h b/include/fuse_opt.h new file mode 100644 index 0000000..528ec9d --- /dev/null +++ b/include/fuse_opt.h @@ -0,0 +1,212 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2005 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +#ifndef _FUSE_OPT_H_ +#define _FUSE_OPT_H_ + +/* This file defines the option parsing interface of FUSE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Special 'offset' value. In case of a match, the processing + * function will be called with 'value' as the key + */ +#define FUSE_OPT_OFFSET_KEY -1U + +/** + * Option description + * + * This structure describes a single option, and and action associated + * with it, in case it matches. + * + * More than one such match may occur, in which case the action for + * each match is executed. + * + * There are three possible actions in case of a match: + * + * i) An integer (int or unsigned) variable determined by 'offset' is + * set to 'value' + * + * ii) The processing function is called, with 'value' as the key + * + * iii) An integer (any) or string (char *) variable determined by + * 'offset' is set to the value of an option parameter + * + * 'offset' should normally be either set to + * + * - 'offsetof(struct foo, member)' actions i) and iii) + * + * - FUSE_OPT_OFFSET_KEY action ii) + * + * The 'offsetof()' macro is defined in the <stddef.h> header. + * + * The template determines which options match, and also have an + * effect on the action. Normally the action is either i) or ii), but + * if a format is present in the template, then action iii) is + * performed. + * + * The types of templates are: + * + * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * themselves. Invalid values are "--" and anything beginning + * with "-o" + * + * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or + * the relevant option in a comma separated option list + * + * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) + * which have a parameter + * + * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform + * action iii). + * + * 5) "-x ", etc. Matches either "-xparam" or "-x param" as + * two separate arguments + * + * 6) "-x %s", etc. Combination of 4) and 5) + * + * If the format is "%s", memory is allocated for the string unlike + * with scanf(). + */ +struct fuse_opt { + /** Matching template and optional parameter formatting */ + const char *template; + + /** + * Offset of variable within 'data' parameter of fuse_opt_parse() + * or FUSE_OPT_OFFSET_KEY + */ + unsigned long offset; + + /** + * Value to set the variable to, or to be passed as 'key' to the + * processing function. Ignored if template a format + */ + int value; +}; + +/** + * Last option. An array of 'struct fuse_opt' must end with a NULL + * template value + */ +#define FUSE_OPT_END { .template = NULL } + + +/** + * Key value passed to the processing function if an option did not + * match any templated + */ +#define FUSE_OPT_KEY_OPT -1 + +/** + * Key value passed to the processing function for all non-options + * + * Non-options are the arguments beginning with a charater other than + * '-' or all arguments after the special '--' option + */ +#define FUSE_OPT_KEY_NONOPT -2 + +/** + * Processing function + * + * This function is called if + * - option did not match any 'struct fuse_opt' + * - argument is a non-option + * - option did match and offset was set to FUSE_OPT_OFFSET_KEY + * + * The 'arg' parameter will always contain the whole argument or + * option including the parameter if exists. A two-argument option + * ("-x foo") is always converted to single arguemnt option of the + * form "-xfoo" before this function is called. + * + * Options of the form '-ofoo' are passed to this function without the + * '-o' prefix. + * + * The return value of this function determines whether this argument + * is to be inserted into the output argument vector, or discarded. + * + * @param data is the user data passed to the fuse_opt_parse() function + * @param arg is the whole argument or option + * @param key determines why the processing function was called + * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept + */ +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key); + +/** + * Option parsing function + * + * If 'argv' is NULL, the values pointed by argcout and argvout will + * be used as input + * + * A NULL 'opts' is the same as an 'opts' array containing a single + * end marker + * + * If 'proc' is NULL, then any non-matching options will cause an + * error to be returned + * + * If argvout is NULL, then any output arguments are discarded + * + * If argcout is NULL, then the output argument count is not stored + * + * @param argc is the input argument count + * @param argv is the input argument vector, may be NULL + * @param data is the user data + * @param opts is the option description array, may be NULL + * @param proc is the processing function, may be NULL + * @param argcout is pointer to output argument count, may be NULL + * @param argvout is pointer to output argument vector, may be NULL + * @return -1 on error, 0 on success + */ +int fuse_opt_parse(int argc, char *argv[], void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc, + int *argcout, char **argvout[]); + +/** + * Add an option to a comma separated option list + * + * @param opts is a pointer to an option list, may point to a NULL value + * @param opt is the option to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_opt(char **opts, const char *opt); + +/** + * Add an argument to a NULL terminated argument vector + * + * @param argcp is a pointer to argument count + * @param argvp is a pointer to argument vector + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_arg(int *argcp, char **argvp[], const char *arg); + +/** + * Free argument vector + * + * @param args is the argument vector + */ +void fuse_opt_free_args(char *args[]); + + +/** + * Check if an option matches + * + * @param opts is the option description array + * @param opt is the option to match + * @return 1 if a match is found, 0 if not + */ +int fuse_opt_match(const struct fuse_opt opts[], const char *opt); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_OPT_H_ */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 0bd952e..2af57b9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,6 +16,7 @@ libfuse_la_SOURCES = \ fuse_loop_mt.c \ fuse_lowlevel.c \ fuse_mt.c \ + fuse_opt.c \ fuse_session.c \ helper.c \ $(mount_source) diff --git a/lib/fuse.c b/lib/fuse.c index 8733164..cd5307c 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -12,10 +12,12 @@ #include "fuse_i.h" #include "fuse_lowlevel.h" +#include "fuse_opt.h" #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <stddef.h> #include <unistd.h> #include <fcntl.h> #include <limits.h> @@ -25,45 +27,29 @@ #include <sys/param.h> #include <sys/uio.h> -/* FUSE flags: */ - -/** Enable debugging output */ -#define FUSE_DEBUG (1 << 1) - -/** If a file is removed but it's still open, don't hide the file but - remove it immediately */ -#define FUSE_HARD_REMOVE (1 << 2) - -/** Use st_ino field in getattr instead of generating inode numbers */ -#define FUSE_USE_INO (1 << 3) - -/** Make a best effort to fill in inode number in a readdir **/ -#define FUSE_READDIR_INO (1 << 5) - -/** Ignore file mode supplied by the filesystem, and create one based - on the 'umask' option */ -#define FUSE_SET_MODE (1 << 6) - -/** Ignore st_uid supplied by the filesystem and set it based on the - 'uid' option*/ -#define FUSE_SET_UID (1 << 7) - -/** Ignore st_gid supplied by the filesystem and set it based on the - 'gid' option*/ -#define FUSE_SET_GID (1 << 8) - -/** Bypass the page cache for read and write operations */ -#define FUSE_DIRECT_IO (1 << 9) - -/** If the FUSE_KERNEL_CACHE flag is given, then cached data will not - be flushed on open */ -#define FUSE_KERNEL_CACHE (1 << 10) - #define FUSE_MAX_PATH 4096 +struct fuse_config { + char *llopts; + unsigned int uid; + unsigned int gid; + unsigned int umask; + double entry_timeout; + double negative_timeout; + double attr_timeout; + int debug; + int hard_remove; + int use_ino; + int readdir_ino; + int set_mode; + int set_uid; + int set_gid; + int direct_io; + int kernel_cache; +}; + struct fuse { struct fuse_session *se; - int flags; struct fuse_operations op; int compat; struct node **name_table; @@ -76,12 +62,7 @@ struct fuse { pthread_mutex_t lock; pthread_rwlock_t tree_lock; void *user_data; - unsigned int uid; - unsigned int gid; - unsigned int umask; - double entry_timeout; - double negative_timeout; - double attr_timeout; + struct fuse_config conf; }; struct node { @@ -231,7 +212,7 @@ static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parent, static void delete_node(struct fuse *f, struct node *node) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("delete: %llu\n", (unsigned long long) node->nodeid); fflush(stdout); } @@ -422,14 +403,14 @@ static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) { - if (!(f->flags & FUSE_USE_INO)) + if (!f->conf.use_ino) stbuf->st_ino = nodeid; - if (f->flags & FUSE_SET_MODE) - stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->umask); - if (f->flags & FUSE_SET_UID) - stbuf->st_uid = f->uid; - if (f->flags & FUSE_SET_GID) - stbuf->st_gid = f->gid; + if (f->conf.set_mode) + stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask); + if (f->conf.set_uid) + stbuf->st_uid = f->conf.uid; + if (f->conf.set_gid) + stbuf->st_gid = f->conf.gid; } static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) @@ -525,10 +506,10 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, else { e->ino = node->nodeid; e->generation = node->generation; - e->entry_timeout = f->entry_timeout; - e->attr_timeout = f->attr_timeout; + e->entry_timeout = f->conf.entry_timeout; + e->attr_timeout = f->conf.attr_timeout; set_stat(f, e->ino, &e->attr); - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf(" NODEID: %lu\n", (unsigned long) e->ino); fflush(stdout); } @@ -607,16 +588,16 @@ static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("LOOKUP %s\n", path); fflush(stdout); } err = -ENOSYS; if (f->op.getattr) { err = lookup_path(f, parent, name, path, &e, NULL); - if (err == -ENOENT && f->negative_timeout != 0.0) { + if (err == -ENOENT && f->conf.negative_timeout != 0.0) { e.ino = 0; - e.entry_timeout = f->negative_timeout; + e.entry_timeout = f->conf.negative_timeout; err = 0; } } @@ -629,7 +610,7 @@ static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) static void fuse_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) { struct fuse *f = req_fuse(req); - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("FORGET %llu/%lu\n", (unsigned long long) ino, nlookup); fflush(stdout); } @@ -659,7 +640,7 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_unlock(&f->tree_lock); if (!err) { set_stat(f, ino, &buf); - fuse_reply_attr(req, &buf, f->attr_timeout); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else reply_err(req, err); } @@ -747,7 +728,7 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, pthread_rwlock_unlock(&f->tree_lock); if (!err) { set_stat(f, ino, &buf); - fuse_reply_attr(req, &buf, f->attr_timeout); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else reply_err(req, err); } @@ -762,7 +743,7 @@ static void fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("ACCESS %s 0%o\n", path, mask); fflush(stdout); } @@ -811,7 +792,7 @@ static void fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("MKNOD %s\n", path); fflush(stdout); } @@ -850,7 +831,7 @@ static void fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("MKDIR %s\n", path); fflush(stdout); } @@ -876,13 +857,13 @@ static void fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_wrlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("UNLINK %s\n", path); fflush(stdout); } err = -ENOSYS; if (f->op.unlink) { - if (!(f->flags & FUSE_HARD_REMOVE) && is_open(f, parent, name)) + if (!f->conf.hard_remove && is_open(f, parent, name)) err = hide_node(f, path, parent, name); else { err = f->op.unlink(path); @@ -906,7 +887,7 @@ static void fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) pthread_rwlock_wrlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("RMDIR %s\n", path); fflush(stdout); } @@ -934,7 +915,7 @@ static void fuse_symlink(fuse_req_t req, const char *linkname, pthread_rwlock_rdlock(&f->tree_lock); path = get_path_name(f, parent, name); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("SYMLINK %s\n", path); fflush(stdout); } @@ -964,14 +945,14 @@ static void fuse_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, if (oldpath != NULL) { newpath = get_path_name(f, newdir, newname); if (newpath != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("RENAME %s -> %s\n", oldpath, newpath); fflush(stdout); } err = -ENOSYS; if (f->op.rename) { err = 0; - if (!(f->flags & FUSE_HARD_REMOVE) && + if (!f->conf.hard_remove && is_open(f, newdir, newname)) err = hide_node(f, newpath, newdir, newname); if (!err) { @@ -1003,7 +984,7 @@ static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, if (oldpath != NULL) { newpath = get_path_name(f, newparent, newname); if (newpath != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("LINK %s\n", newpath); fflush(stdout); } @@ -1037,7 +1018,7 @@ static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, if (f->op.create && f->op.getattr) { err = f->op.create(path, mode, fi); if (!err) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("CREATE[%llu] flags: 0x%x %s\n", (unsigned long long) fi->fh, fi->flags, path); fflush(stdout); @@ -1057,9 +1038,9 @@ static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, } if (!err) { - if (f->flags & FUSE_DIRECT_IO) + if (f->conf.direct_io) fi->direct_io = 1; - if (f->flags & FUSE_KERNEL_CACHE) + if (f->conf.kernel_cache) fi->keep_cache = 1; pthread_mutex_lock(&f->lock); @@ -1096,15 +1077,15 @@ static void fuse_open(fuse_req_t req, fuse_ino_t ino, err = fuse_do_open(f, path, fi); } if (!err) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("OPEN[%llu] flags: 0x%x\n", (unsigned long long) fi->fh, fi->flags); fflush(stdout); } - if (f->flags & FUSE_DIRECT_IO) + if (f->conf.direct_io) fi->direct_io = 1; - if (f->flags & FUSE_KERNEL_CACHE) + if (f->conf.kernel_cache) fi->keep_cache = 1; pthread_mutex_lock(&f->lock); @@ -1143,7 +1124,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("READ[%llu] %u bytes from %llu\n", (unsigned long long) fi->fh, size, off); fflush(stdout); @@ -1157,7 +1138,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, pthread_rwlock_unlock(&f->tree_lock); if (res >= 0) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf(" READ[%llu] %u bytes\n", (unsigned long long) fi->fh, res); fflush(stdout); @@ -1182,7 +1163,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("WRITE%s[%llu] %u bytes to %llu\n", fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, size, off); @@ -1197,7 +1178,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, pthread_rwlock_unlock(&f->tree_lock); if (res >= 0) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf(" WRITE%s[%llu] %u bytes\n", fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, res); @@ -1221,7 +1202,7 @@ static void fuse_flush(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("FLUSH[%llu]\n", (unsigned long long) fi->fh); fflush(stdout); } @@ -1251,7 +1232,7 @@ static void fuse_release(fuse_req_t req, fuse_ino_t ino, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("RELEASE[%llu] flags: 0x%x\n", (unsigned long long) fi->fh, fi->flags); fflush(stdout); @@ -1280,7 +1261,7 @@ static void fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); if (path != NULL) { - if (f->flags & FUSE_DEBUG) { + if (f->conf.debug) { printf("FSYNC[%llu]\n", (unsigned long long) fi->fh); fflush(stdout); } @@ -1375,9 +1356,9 @@ static int fill_dir_common(struct fuse_dirhandle *dh, const char *name, stbuf.st_ino = (ino_t) -1; } - if (!(dh->fuse->flags & FUSE_USE_INO)) { + if (!dh->fuse->conf.use_ino) { stbuf.st_ino = (ino_t) -1; - if (dh->fuse->flags & FUSE_READDIR_INO) { + if (dh->fuse->conf.readdir_ino) { struct node *node; pthread_mutex_lock(&dh->fuse->lock); node = lookup_node(dh->fuse, dh->nodeid, name); @@ -1824,97 +1805,39 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void)) fuse_getcontext = func; } -static int begins_with(const char *s, const char *beg) -{ - if (strncmp(s, beg, strlen(beg)) == 0) - return 1; - else - return 0; -} +static int fuse_lib_opt_proc(void *data, const char *arg, int key) +{ + struct fuse_config *conf = data; + (void) key; + return fuse_opt_add_opt(&conf->llopts, arg); +} + +#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v } + +static const struct fuse_opt fuse_lib_opts[] = { + { "debug", FUSE_OPT_OFFSET_KEY, 0 }, + FUSE_LIB_OPT("debug", debug, 1), + FUSE_LIB_OPT("hard_remove", hard_remove, 1), + FUSE_LIB_OPT("use_ino", use_ino, 1), + FUSE_LIB_OPT("readdir_ino", readdir_ino, 1), + FUSE_LIB_OPT("direct_io", direct_io, 1), + FUSE_LIB_OPT("kernel_cache", kernel_cache, 1), + FUSE_LIB_OPT("umask=", set_mode, 1), + FUSE_LIB_OPT("umask=%o", umask, 0), + FUSE_LIB_OPT("uid=", set_uid, 1), + FUSE_LIB_OPT("uid=%d", uid, 0), + FUSE_LIB_OPT("gid=", set_gid, 1), + FUSE_LIB_OPT("gid=%d", gid, 0), + FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0), + FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0), + FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), + FUSE_OPT_END +}; int fuse_is_lib_option(const char *opt) { - if (fuse_lowlevel_is_lib_option(opt) || - strcmp(opt, "debug") == 0 || - strcmp(opt, "hard_remove") == 0 || - strcmp(opt, "use_ino") == 0 || - strcmp(opt, "allow_root") == 0 || - strcmp(opt, "readdir_ino") == 0 || - strcmp(opt, "direct_io") == 0 || - strcmp(opt, "kernel_cache") == 0 || - begins_with(opt, "umask=") || - begins_with(opt, "uid=") || - begins_with(opt, "gid=") || - begins_with(opt, "entry_timeout=") || - begins_with(opt, "attr_timeout=") || - begins_with(opt, "negative_timeout=")) - return 1; - else - return 0; -} - -static int parse_lib_opts(struct fuse *f, const char *opts, char **llopts) -{ - if (opts) { - char *xopts = strdup(opts); - char *s = xopts; - char *opt; - char *d = xopts; - - if (xopts == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - - while((opt = strsep(&s, ","))) { - if (fuse_lowlevel_is_lib_option(opt)) { - size_t optlen = strlen(opt); - if (strcmp(opt, "debug") == 0) - f->flags |= FUSE_DEBUG; - memmove(d, opt, optlen); - d += optlen; - *d++ = ','; - } else if (strcmp(opt, "hard_remove") == 0) - f->flags |= FUSE_HARD_REMOVE; - else if (strcmp(opt, "use_ino") == 0) - f->flags |= FUSE_USE_INO; - else if (strcmp(opt, "readdir_ino") == 0) - f->flags |= FUSE_READDIR_INO; - else if (strcmp(opt, "direct_io") == 0) - f->flags |= FUSE_DIRECT_IO; - else if (strcmp(opt, "kernel_cache") == 0) - f->flags |= FUSE_KERNEL_CACHE; - else if (sscanf(opt, "umask=%o", &f->umask) == 1) - f->flags |= FUSE_SET_MODE; - else if (sscanf(opt, "uid=%u", &f->uid) == 1) - f->flags |= FUSE_SET_UID; - else if(sscanf(opt, "gid=%u", &f->gid) == 1) - f->flags |= FUSE_SET_GID; - else if (sscanf(opt, "entry_timeout=%lf", &f->entry_timeout) == 1) - /* nop */; - else if (sscanf(opt, "attr_timeout=%lf", &f->attr_timeout) == 1) - /* nop */; - else if (sscanf(opt, "negative_timeout=%lf", - &f->negative_timeout) == 1) - /* nop */; - else - fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt); - } - if (d != xopts) { - d[-1] = '\0'; - *llopts = xopts; - } - else - free(xopts); - } -#ifdef __FreeBSD__ - /* - * In FreeBSD, we always use these settings as inode numbers are needed to - * make getcwd(3) work. - */ - f->flags |= FUSE_READDIR_INO; -#endif - return 0; + return fuse_lowlevel_is_lib_option(opt) || + fuse_opt_match(fuse_lib_opts, opt); } struct fuse *fuse_new_common(int fd, const char *opts, @@ -1924,7 +1847,6 @@ struct fuse *fuse_new_common(int fd, const char *opts, struct fuse_chan *ch; struct fuse *f; struct node *root; - char *llopts = NULL; if (sizeof(struct fuse_operations) < op_size) { fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); @@ -1937,15 +1859,28 @@ struct fuse *fuse_new_common(int fd, const char *opts, goto out; } - f->entry_timeout = 1.0; - f->attr_timeout = 1.0; - f->negative_timeout = 0.0; + f->conf.entry_timeout = 1.0; + f->conf.attr_timeout = 1.0; + f->conf.negative_timeout = 0.0; - if (parse_lib_opts(f, opts, &llopts) == -1) - goto out_free; + if (opts) { + const char *argv[] = { "", "-o", opts, NULL }; + if (fuse_opt_parse(3, (char **) argv, &f->conf, + fuse_lib_opts, fuse_lib_opt_proc, NULL, NULL) == -1) + goto out_free; + } + +#ifdef __FreeBSD__ + /* + * In FreeBSD, we always use these settings as inode numbers are needed to + * make getcwd(3) work. + */ + f->flags |= FUSE_READDIR_INO; +#endif - f->se = fuse_lowlevel_new(llopts, &fuse_path_ops, sizeof(fuse_path_ops), f); - free(llopts); + f->se = fuse_lowlevel_new(f->conf.llopts, &fuse_path_ops, + sizeof(fuse_path_ops), f); + free(f->conf.llopts); if (f->se == NULL) goto out_free; diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 0787368..370e320 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -9,10 +9,12 @@ #include <config.h> #include "fuse_lowlevel.h" #include "fuse_kernel.h" +#include "fuse_opt.h" #include <stdio.h> -#include <string.h> #include <stdlib.h> +#include <stddef.h> +#include <string.h> #include <unistd.h> #include <limits.h> #include <errno.h> @@ -31,8 +33,8 @@ #define MIN_BUFFER_SIZE (MIN_SYMLINK + HEADER_OVERHEAD) struct fuse_ll { - unsigned int debug : 1; - unsigned int allow_root : 1; + int debug; + int allow_root; struct fuse_lowlevel_ops op; int got_init; void *userdata; @@ -917,38 +919,15 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, } } -int fuse_lowlevel_is_lib_option(const char *opt) -{ - if (strcmp(opt, "debug") == 0 || - strcmp(opt, "allow_root") == 0) - return 1; - else - return 0; -} +static struct fuse_opt fuse_ll_opts[] = { + { "debug", offsetof(struct fuse_ll, debug), 1 }, + { "allow_root", offsetof(struct fuse_ll, allow_root), 1 }, + FUSE_OPT_END +}; -static int parse_ll_opts(struct fuse_ll *f, const char *opts) +int fuse_lowlevel_is_lib_option(const char *opt) { - if (opts) { - char *xopts = strdup(opts); - char *s = xopts; - char *opt; - - if (xopts == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - - while((opt = strsep(&s, ","))) { - if (strcmp(opt, "debug") == 0) - f->debug = 1; - else if (strcmp(opt, "allow_root") == 0) - f->allow_root = 1; - else - fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt); - } - free(xopts); - } - return 0; + return fuse_opt_match(fuse_ll_opts, opt); } static void fuse_ll_destroy(void *data) @@ -983,8 +962,12 @@ struct fuse_session *fuse_lowlevel_new(const char *opts, goto out; } - if (parse_ll_opts(f, opts) == -1) - goto out_free; + if (opts) { + const char *argv[] = { "", "-o", opts, NULL }; + if (fuse_opt_parse(3, (char **) argv, f, fuse_ll_opts, NULL, + NULL, NULL) == -1) + goto out_free; + } memcpy(&f->op, op, op_size); f->owner = getuid(); diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c new file mode 100644 index 0000000..f49e85d --- /dev/null +++ b/lib/fuse_opt.c @@ -0,0 +1,363 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPL. + See the file COPYING.LIB +*/ + +#include "fuse_opt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +struct fuse_opt_context { + void *data; + const struct fuse_opt *opt; + fuse_opt_proc_t proc; + int argctr; + int argc; + char **argv; + int argcout; + char **argvout; + char *opts; + int nonopt; +}; + +void fuse_opt_free_args(char *args[]) +{ + int i; + + if (args) { + for (i = 0; args[i]; i++) + free(args[i]); + free(args); + } +} + +static int alloc_failed(void) +{ + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; +} + +int fuse_opt_add_arg(int *argcp, char **argvp[], const char *arg) +{ + char **newargv = realloc(*argvp, (*argcp + 2) * sizeof(char *)); + char *newarg = newargv ? strdup(arg) : NULL; + if (!newargv || !newarg) + return alloc_failed(); + + newargv[(*argcp)++] = newarg; + newargv[*argcp] = NULL; + *argvp = newargv; + return 0; +} + +static int next_arg(struct fuse_opt_context *ctx, const char *opt) +{ + if (ctx->argctr + 1 >= ctx->argc) { + fprintf(stderr, "fuse: missing argument after `%s'\n", opt); + return -1; + } + ctx->argctr++; + return 0; +} + +static int add_arg(struct fuse_opt_context *ctx, const char *arg) +{ + return fuse_opt_add_arg(&ctx->argcout, &ctx->argvout, arg); +} + +int fuse_opt_add_opt(char **opts, const char *opt) +{ + char *newopts; + if (!*opts) + newopts = strdup(opt); + else { + unsigned oldlen = strlen(*opts); + newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); + if (newopts) { + newopts[oldlen] = ','; + strcpy(newopts + oldlen + 1, opt); + } + } + if (!newopts) + return alloc_failed(); + + *opts = newopts; + return 0; +} + +static int add_opt(struct fuse_opt_context *ctx, const char *opt) +{ + return fuse_opt_add_opt(&ctx->opts, opt); +} + +static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg) +{ + assert(pos <= ctx->argcout); + if (add_arg(ctx, arg) == -1) + return -1; + + if (pos != ctx->argcout - 1) { + char *newarg = ctx->argvout[ctx->argcout - 1]; + memmove(&ctx->argvout[pos+1], &ctx->argvout[pos], + sizeof(char *) * (ctx->argcout - pos - 1)); + ctx->argvout[pos] = newarg; + } + return 0; +} + +static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, + int iso) +{ + int res; + + if (!ctx->proc) { + fprintf(stderr, "fuse: unknown option `%s'\n", arg); + return -1; + } + + res = ctx->proc(ctx->data, arg, key); + if (res == -1 || !res) + return res; + + if (iso) + return add_opt(ctx, arg); + else + return add_arg(ctx, arg); +} + +static int match_template(const char *t, const char *arg, unsigned *sepp) +{ + int arglen = strlen(arg); + const char *sep = strchr(t, '='); + sep = sep ? sep : strchr(t, ' '); + if (sep && (!sep[1] || sep[1] == '%')) { + int tlen = sep - t; + if (sep[0] == '=') + tlen ++; + if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { + *sepp = sep - t; + return 1; + } + } + if (strcmp(t, arg) == 0) { + *sepp = 0; + return 1; + } + return 0; +} + +static const struct fuse_opt *find_opt(const struct fuse_opt *opt, + const char *arg, unsigned *sepp) +{ + for (; opt && opt->template; opt++) + if (match_template(opt->template, arg, sepp)) + return opt; + return NULL; +} + +int fuse_opt_match(const struct fuse_opt *opts, const char *opt) +{ + unsigned dummy; + return find_opt(opts, opt, &dummy) ? 1 : 0; +} + +static int process_opt_param(void *var, const char *format, const char *param, + const char *arg) +{ + assert(format[0] == '%'); + if (format[1] == 's') { + char *copy = strdup(param); + if (!copy) + return alloc_failed(); + + *(char **) var = copy; + } else { + if (sscanf(param, format, var) != 1) { + fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); + return -1; + } + } + return 0; +} + +static int process_opt(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + if (opt->offset == FUSE_OPT_OFFSET_KEY) { + if (call_proc(ctx, arg, opt->value, iso) == -1) + return -1; + } else { + void *var = ctx->data + opt->offset; + if (sep && opt->template[sep + 1]) { + const char *param = arg + sep; + if (opt->template[sep] == '=') + param ++; + if (process_opt_param(var, opt->template + sep + 1, + param, arg) == -1) + return -1; + } else + *(int *)var = opt->value; + } + return 0; +} + +static int process_opt_sep_arg(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + int res; + char *newarg; + char *param; + + if (next_arg(ctx, arg) == -1) + return -1; + + param = ctx->argv[ctx->argctr]; + newarg = malloc(sep + strlen(param) + 1); + if (!newarg) + return alloc_failed(); + + memcpy(newarg, arg, sep); + strcpy(newarg + sep, param); + res = process_opt(ctx, opt, sep, newarg, iso); + free(newarg); + + return res; +} + +static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) +{ + unsigned sep; + const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); + if (opt) { + for (; opt; opt = find_opt(opt + 1, arg, &sep)) { + int res; + if (sep && opt->template[sep] == ' ' && !arg[sep]) + res = process_opt_sep_arg(ctx, opt, sep, arg, iso); + else + res = process_opt(ctx, opt, sep, arg, iso); + if (res == -1) + return -1; + } + return 0; + } else + return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); +} + +static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) +{ + char *sep; + + do { + int res; + sep = strchr(opts, ','); + if (sep) + *sep = '\0'; + res = process_gopt(ctx, opts, 1); + if (res == -1) + return -1; + opts = sep + 1; + } while (sep); + + return 0; +} + +static int process_option_group(struct fuse_opt_context *ctx, const char *opts) +{ + int res; + char *copy; + const char *sep = strchr(opts, ','); + if (!sep) + return process_gopt(ctx, opts, 1); + + copy = strdup(opts); + if (!copy) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + res = process_real_option_group(ctx, copy); + free(copy); + return res; +} + +static int process_one(struct fuse_opt_context *ctx, const char *arg) +{ + if (ctx->nonopt || arg[0] != '-') + return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); + else if (arg[1] == 'o') { + if (arg[2]) + return process_option_group(ctx, arg + 2); + else { + if (next_arg(ctx, arg) == -1) + return -1; + + return process_option_group(ctx, ctx->argv[ctx->argctr]); + } + } else if (arg[1] == '-' && !arg[2]) { + if (add_arg(ctx, arg) == -1) + return -1; + ctx->nonopt = 1; + return 0; + } else + return process_gopt(ctx, arg, 0); +} + +static int opt_parse(struct fuse_opt_context *ctx) +{ + if (ctx->argc) { + if (add_arg(ctx, ctx->argv[0]) == -1) + return -1; + } + + for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) + if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) + return -1; + + if (ctx->opts) { + if (insert_arg(ctx, 1, "-o") == -1 || + insert_arg(ctx, 2, ctx->opts) == -1) + return -1; + } + return 0; +} + +int fuse_opt_parse(int argc, char *argv[], void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc, + int *argcout, char **argvout[]) +{ + int res; + struct fuse_opt_context ctx = { + .argc = argv ? argc : *argcout, + .argv = argv ? argv : *argvout, + .data = data, + .opt = opts, + .proc = proc, + .argcout = 0, + .argvout = NULL, + .opts = NULL, + .nonopt = 0, + }; + + res = opt_parse(&ctx); + if (!argv) + fuse_opt_free_args(ctx.argv); + free(ctx.opts); + if (res == -1) + fuse_opt_free_args(ctx.argvout); + else { + if (argcout) + *argcout = ctx.argcout; + if (argvout) + *argvout = ctx.argvout; + else + fuse_opt_free_args(ctx.argvout); + } + return res; +} diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index f3f4e21..e610b88 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -67,6 +67,11 @@ FUSE_2.5 { fuse_main_real_compat22; fuse_new; fuse_new_compat22; + fuse_opt_parse; + fuse_opt_add_opt; + fuse_opt_add_arg; + fuse_opt_free_args; + fuse_opt_match; fuse_reply_create; fuse_reply_open; fuse_reply_open_compat; diff --git a/lib/helper.c b/lib/helper.c index bb8243c..1f99c2f 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -7,9 +7,11 @@ */ #include "fuse_i.h" +#include "fuse_opt.h" #include <stdio.h> #include <stdlib.h> +#include <stddef.h> #include <unistd.h> #include <string.h> #include <limits.h> @@ -52,12 +54,6 @@ static void usage(const char *progname) ); } -static void invalid_option(const char *argv[], int argctr) -{ - fprintf(stderr, "fuse: invalid option: %s\n\n", argv[argctr]); - fprintf(stderr, "see `%s -h' for usage\n", argv[0]); -} - static void exit_handler(int sig) { (void) sig; @@ -99,187 +95,140 @@ static int set_signal_handlers(void) return 0; } -static int opt_member(const char *opts, const char *opt) +enum { + KEY_HELP, + KEY_HELP_NOHEADER, + KEY_DEBUG, + KEY_KERN, + KEY_ALLOW_ROOT, + KEY_RO, +}; + +struct helper_opts { + const char *progname; + int singlethread; + int foreground; + int allow_other; + int allow_root; + int fsname; + char *kernel_opts; + char *lib_opts; + char *mountpoint; +}; + +#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 } +#define FUSE_HELPER_KEY(t, k) { t, FUSE_OPT_OFFSET_KEY, k } + +static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-d", foreground), + FUSE_HELPER_OPT("debug", foreground), + FUSE_HELPER_OPT("-f", foreground), + FUSE_HELPER_OPT("-s", singlethread), + FUSE_HELPER_OPT("allow_other", allow_other), + FUSE_HELPER_OPT("allow_root", allow_root), + FUSE_HELPER_OPT("fsname=", fsname), + + FUSE_HELPER_KEY("-h", KEY_HELP), + FUSE_HELPER_KEY("--help", KEY_HELP), + FUSE_HELPER_KEY("-ho", KEY_HELP_NOHEADER), + FUSE_HELPER_KEY("-d", KEY_DEBUG), + FUSE_HELPER_KEY("debug", KEY_DEBUG), + FUSE_HELPER_KEY("allow_other", KEY_KERN), + FUSE_HELPER_KEY("allow_root", KEY_ALLOW_ROOT), + FUSE_HELPER_KEY("nonempty", KEY_KERN), + FUSE_HELPER_KEY("default_permissions", KEY_KERN), + FUSE_HELPER_KEY("fsname=", KEY_KERN), + FUSE_HELPER_KEY("large_read", KEY_KERN), + FUSE_HELPER_KEY("max_read=", KEY_KERN), + FUSE_HELPER_KEY("-r", KEY_RO), + FUSE_HELPER_KEY("ro", KEY_KERN), + FUSE_HELPER_KEY("rw", KEY_KERN), + FUSE_HELPER_KEY("suid", KEY_KERN), + FUSE_HELPER_KEY("nosuid", KEY_KERN), + FUSE_HELPER_KEY("dev", KEY_KERN), + FUSE_HELPER_KEY("nodev", KEY_KERN), + FUSE_HELPER_KEY("exec", KEY_KERN), + FUSE_HELPER_KEY("noexec", KEY_KERN), + FUSE_HELPER_KEY("async", KEY_KERN), + FUSE_HELPER_KEY("sync", KEY_KERN), + FUSE_HELPER_KEY("atime", KEY_KERN), + FUSE_HELPER_KEY("noatime", KEY_KERN), + FUSE_OPT_END +}; + +static int fuse_helper_opt_proc(void *data, const char *arg, int key) { - const char *e, *s = opts; - int optlen = strlen(opt); - for (s = opts; s; s = e + 1) { - if(!(e = strchr(s, ','))) + struct helper_opts *hopts = data; + + switch (key) { + case KEY_HELP: + case KEY_HELP_NOHEADER: + usage(key == KEY_HELP ? hopts->progname : NULL); + exit(1); + + case FUSE_OPT_KEY_OPT: + return fuse_opt_add_opt(&hopts->lib_opts, arg); + + case FUSE_OPT_KEY_NONOPT: + if (hopts->mountpoint) break; - if (e - s == optlen && strncmp(s, opt, optlen) == 0) - return 1; - } - return (s && strcmp(s, opt) == 0); -} -static int add_option_to(const char *opt, char **optp) -{ - unsigned len = strlen(opt); - if (*optp) { - unsigned oldlen = strlen(*optp); - *optp = (char *) realloc(*optp, oldlen + 1 + len + 1); - if (*optp == NULL) - return -1; - (*optp)[oldlen] = ','; - strcpy(*optp + oldlen + 1, opt); - } else { - *optp = (char *) malloc(len + 1); - if (*optp == NULL) + return fuse_opt_add_opt(&hopts->mountpoint, arg); + + case KEY_DEBUG: + return fuse_opt_add_opt(&hopts->lib_opts, "debug"); + + case KEY_ALLOW_ROOT: + if (fuse_opt_add_opt(&hopts->kernel_opts, "allow_other") == -1 || + fuse_opt_add_opt(&hopts->lib_opts, "allow_root") == -1) return -1; - strcpy(*optp, opt); + return 0; + + case KEY_RO: + arg = "ro"; + /* fall through */ + + case KEY_KERN: + return fuse_opt_add_opt(&hopts->kernel_opts, arg); } - return 0; + + fprintf(stderr, "fuse: invalid option `%s'\n", arg); + return -1; } -static int add_options(char **lib_optp, char **kernel_optp, const char *opts) +static int fuse_parse_cmdline(int argc, const char *argv[], + struct helper_opts *hopts) { - char *xopts = strdup(opts); - char *s = xopts; - char *opt; - int has_allow_other = 0; - int has_allow_root = 0; - - if (xopts == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); + int res; + + hopts->progname = argv[0]; + res = fuse_opt_parse(argc, (char **) argv, hopts, fuse_helper_opts, + fuse_helper_opt_proc, NULL, NULL); + if (res == -1) return -1; - } - while((opt = strsep(&s, ",")) != NULL) { - int res; - if (fuse_is_lib_option(opt)) { - res = add_option_to(opt, lib_optp); - /* Compatibility hack */ - if (strcmp(opt, "allow_root") == 0 && res != -1) { - has_allow_root = 1; - res = add_option_to("allow_other", kernel_optp); - } - } - else { - res = add_option_to(opt, kernel_optp); - if (strcmp(opt, "allow_other") == 0) - has_allow_other = 1; - } - if (res == -1) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - } - if (has_allow_other && has_allow_root) { + if (hopts->allow_other && hopts->allow_root) { fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); return -1; } - free(xopts); - return 0; -} -static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts, - char **lib_opts, char **mountpoint, - int *multithreaded, int *background) -{ - int res; - int argctr; - const char *basename; - char *fsname_opt; - - *kernel_opts = NULL; - *lib_opts = NULL; - *mountpoint = NULL; - *multithreaded = 1; - *background = 1; - - basename = strrchr(argv[0], '/'); - if (basename == NULL) - basename = argv[0]; - else if (basename[1] != '\0') - basename++; - - fsname_opt = (char *) malloc(strlen(basename) + 64); - if (fsname_opt == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - sprintf(fsname_opt, "fsname=%s", basename); - res = add_options(lib_opts, kernel_opts, fsname_opt); - free(fsname_opt); - if (res == -1) - goto err; - - for (argctr = 1; argctr < argc; argctr ++) { - if (argv[argctr][0] == '-') { - if (strlen(argv[argctr]) == 2) - switch (argv[argctr][1]) { - case 'o': - if (argctr + 1 == argc || argv[argctr+1][0] == '-') { - fprintf(stderr, "missing option after -o\n"); - fprintf(stderr, "see `%s -h' for usage\n", argv[0]); - goto err; - } - argctr ++; - res = add_options(lib_opts, kernel_opts, argv[argctr]); - if (res == -1) - goto err; - break; - - case 'd': - res = add_options(lib_opts, kernel_opts, "debug"); - if (res == -1) - goto err; - break; - - case 'r': - res = add_options(lib_opts, kernel_opts, "ro"); - if (res == -1) - goto err; - break; - - case 'f': - *background = 0; - break; - - case 's': - *multithreaded = 0; - break; - - case 'h': - usage(argv[0]); - goto err; - - default: - invalid_option(argv, argctr); - goto err; - } - else { - if (argv[argctr][1] == 'o') { - res = add_options(lib_opts, kernel_opts, &argv[argctr][2]); - if (res == -1) - goto err; - } else if(strcmp(argv[argctr], "-ho") == 0) { - usage(NULL); - goto err; - } else { - invalid_option(argv, argctr); - goto err; - } - } - } else if (*mountpoint == NULL) { - *mountpoint = strdup(argv[argctr]); - if (*mountpoint == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); - goto err; - } - } - else { - invalid_option(argv, argctr); - goto err; + if (!hopts->fsname) { + char *fsname_opt; + const char *basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else if (basename[1] != '\0') + basename++; + + fsname_opt = (char *) malloc(strlen(basename) + 64); + if (fsname_opt == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; } + sprintf(fsname_opt, "fsname=%s", basename); + fuse_opt_add_opt(&hopts->kernel_opts, fsname_opt); } return 0; - - err: - free(*kernel_opts); - free(*lib_opts); - free(*mountpoint); - return -1; } static struct fuse *fuse_setup_common(int argc, char *argv[], @@ -291,9 +240,7 @@ static struct fuse *fuse_setup_common(int argc, char *argv[], int compat) { struct fuse *fuse; - int background; - char *kernel_opts; - char *lib_opts; + struct helper_opts hopts; int res; if (fuse_instance != NULL) { @@ -301,21 +248,20 @@ static struct fuse *fuse_setup_common(int argc, char *argv[], return NULL; } - res = fuse_parse_cmdline(argc, (const char **) argv, &kernel_opts, - &lib_opts, mountpoint, multithreaded, - &background); + memset(&hopts, 0, sizeof(hopts)); + res = fuse_parse_cmdline(argc, (const char **) argv, &hopts); if (res == -1) - return NULL; + goto err_free; - *fd = fuse_mount(*mountpoint, kernel_opts); + *fd = fuse_mount(hopts.mountpoint, hopts.kernel_opts); if (*fd == -1) goto err_free; - fuse = fuse_new_common(*fd, lib_opts, op, op_size, compat); + fuse = fuse_new_common(*fd, hopts.lib_opts, op, op_size, compat); if (fuse == NULL) goto err_unmount; - if (background && !opt_member(lib_opts, "debug")) { + if (!hopts.foreground) { res = daemon(0, 0); if (res == -1) { perror("fuse: failed to daemonize program\n"); @@ -327,19 +273,21 @@ static struct fuse *fuse_setup_common(int argc, char *argv[], if (res == -1) goto err_destroy; + *mountpoint = hopts.mountpoint; + *multithreaded = !hopts.singlethread; fuse_instance = fuse; - free(kernel_opts); - free(lib_opts); + free(hopts.kernel_opts); + free(hopts.lib_opts); return fuse; err_destroy: fuse_destroy(fuse); err_unmount: - fuse_unmount(*mountpoint); + fuse_unmount(hopts.mountpoint); err_free: - free(kernel_opts); - free(lib_opts); - free(*mountpoint); + free(hopts.mountpoint); + free(hopts.kernel_opts); + free(hopts.lib_opts); return NULL; } -- 2.30.2