From bd7661b82698c88f08cb82e57cc229cd9df8a3d4 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 23 Jul 2004 17:16:29 +0000 Subject: [PATCH] clean up mount option passing --- ChangeLog | 6 ++ include/fuse.h | 42 ++++++----- kernel/dev.c | 2 +- kernel/inode.c | 2 +- lib/fuse.c | 33 ++++++++- lib/fuse_i.h | 10 +++ lib/helper.c | 131 ++++++++++++++++++++++------------ lib/mount.c | 22 ++---- util/fusermount.c | 175 +++++++++++++++++++++++++--------------------- 9 files changed, 254 insertions(+), 169 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9f07e26..591f1cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2004-07-23 Miklos Szeredi + + * Clean up mount option passing to fusermount and to fuse_new() + BEWARE: this changes the userspace API slightly, and the command + line usage of programs using fuse_main() + 2004-07-20 Miklos Szeredi * Optimize reading under 2.6 kernels by issuing multiple page diff --git a/include/fuse.h b/include/fuse.h index 782a51c..726b681 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -90,20 +90,18 @@ typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type); * with the same flags. It is possible to have a file opened more * than once, in which case only the last release will mean, that no * more reads/writes will happen on the file. The return value of - * release is ignored. This call need only be implemented if this - * information is required, otherwise set this function to NULL. + * release is ignored. Implementing this method is optional. * * - flush() is called when close() has been called on an open file. * NOTE: this does not mean that the file is released (e.g. after * fork() an open file will have two references which both must be - * closed before the file is released). The flush() method can be + * closed before the file is released). The flush() method may be * called more than once for each open(). The return value of * flush() is passed on to the close() system call. Implementing - * this call is optional. If it is not needed by the filesystem, - * then set the callback pointer to NULL. + * this method is optional. * * - fsync() has a boolean 'datasync' parameter which if TRUE then do - * an fdatasync() operation. Implementing this call is optional. + * an fdatasync() operation. Implementing this method is optional. */ struct fuse_operations { int (*getattr) (const char *, struct stat *); @@ -139,15 +137,6 @@ struct fuse_context { gid_t gid; }; -/* FUSE flags: */ - -/** Enable debuging 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) - #ifdef __cplusplus extern "C" { #endif @@ -160,8 +149,7 @@ extern "C" { * * This function does the following: * - parses command line options (-d -s and -h) - * - passes all options after '--' to the fusermount program - * - mounts the filesystem by calling fusermount + * - passes relevant mount options to the fuse_mount() * - installs signal handlers for INT, HUP, TERM and PIPE * - registers an exit handler to unmount the filesystem on program exit * - creates a fuse handle @@ -194,11 +182,10 @@ struct fuse *fuse_get(void); * fuse_new() * * @param mountpoint the mount point path - * @param args array of arguments to be passed to fusermount (NULL - * terminated). Can be NULL if no arguments are needed. + * @param opts a comma separated list of mount options. Can be NULL. * @return the control file descriptor on success, -1 on failure */ -int fuse_mount(const char *mountpoint, const char *args[]); +int fuse_mount(const char *mountpoint, const char *opts); /* * Umount a FUSE mountpoint @@ -211,11 +198,12 @@ void fuse_unmount(const char *mountpoint); * Create a new FUSE filesystem. * * @param fd the control file descriptor - * @param flags any combination of the FUSE flags defined above, or 0 + * @param opts mount options to be used by the library * @param op the operations * @return the created FUSE handle */ -struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op); +struct fuse *fuse_new(int fd, const char *opts, + const struct fuse_operations *op); /** * Destroy the FUSE handle. @@ -268,6 +256,16 @@ void fuse_loop_mt(struct fuse *f); */ struct fuse_context *fuse_get_context(struct fuse *f); +/** + * Check whether a mount option should be passed to the kernel or the + * library + * + * @param opt the option to check + * @return 1 if it is a library option, 0 otherwise + */ +int fuse_is_lib_option(const char *opt); + + /* ----------------------------------------------------------- * * Advanced API for event handling, don't worry about this... * * ----------------------------------------------------------- */ diff --git a/kernel/dev.c b/kernel/dev.c index 1fc2943..a12820d 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -432,7 +432,7 @@ static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh) struct inode *inode = iget(fc->sb, uh->ino); int err = -ENOENT; if (inode) { - if (FUSE_FI(inode)) { + if (INO_FI(inode)) { invalidate_inode_pages(inode); err = 0; } diff --git a/kernel/inode.c b/kernel/inode.c index 8c1cba3..36ec038 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -175,7 +175,7 @@ static match_table_t tokens = { {opt_kernel_cache, "kernel_cache"}, {opt_large_read, "large_read"}, {opt_direct_io, "direct_io"}, - {opt_max_read, "max_read" }, + {opt_max_read, "max_read=%u" }, {opt_err, NULL} }; diff --git a/lib/fuse.c b/lib/fuse.c index cebca07..2b79d85 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -1550,7 +1550,36 @@ static int check_version(struct fuse *f) return 0; } -struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op) + +int fuse_is_lib_option(const char *opt) +{ + if (strcmp(opt, "debug") == 0 || + strcmp(opt, "hard_remove") == 0) + return 1; + else + return 0; +} + +static void parse_lib_opts(struct fuse *f, const char *opts) +{ + if (opts) { + char *xopts = strdup(opts); + char *s = xopts; + char *opt; + + while((opt = strsep(&s, ","))) { + if (strcmp(opt, "debug") == 0) + f->flags |= FUSE_DEBUG; + else if (strcmp(opt, "hard_remove") == 0) + f->flags |= FUSE_HARD_REMOVE; + else + fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt); + } + free(xopts); + } +} + +struct fuse *fuse_new(int fd, const char *opts, const struct fuse_operations *op) { struct fuse *f; struct node *root; @@ -1562,7 +1591,7 @@ struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op) return NULL; } - f->flags = flags; + parse_lib_opts(f, opts); f->fd = fd; f->ctr = 0; f->generation = 0; diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 8942d74..58c0a41 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -10,6 +10,16 @@ #include #include +/* FUSE flags: */ + +/** Enable debuging 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) + + typedef unsigned long fino_t; struct node { diff --git a/lib/helper.c b/lib/helper.c index 76b6eb7..b75855a 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -25,23 +25,31 @@ struct fuse *fuse_get(void) static void usage(char *progname) { fprintf(stderr, - "usage: %s mountpoint [options] [-- [fusermount options]]\n" + "usage: %s mountpoint [options]\n" "Options:\n" - " -d enable debug output (implies -f)\n" - " -f foreground operation\n" - " -s disable multithreaded operation\n" - " -i immediate removal (don't delay until last release)\n" - " -h print help\n" + " -d enable debug output (implies -f)\n" + " -f foreground operation\n" + " -s disable multithreaded operation\n" + " -o opt,[opt...] mount options\n" + " -h print help\n" "\n" - "Fusermount options:\n" - " see 'fusermount -h'\n", + "Mount options:\n" + " default_permissions enable permission checking\n" + " allow_other allow access to other users\n" + " kernel_cache cache files in kernel\n" + " large_read issue large read requests (2.4 only)\n" + " direct_io use direct I/O\n" + " max_read=N set maximum size of read requests\n" + " hard_remove immediate removal (don't hide files)\n" + " debug enable debug output\n" + " fsname=NAME set filesystem name in mtab\n", progname); exit(1); } static void invalid_option(char *argv[], int argctr) { - fprintf(stderr, "invalid option: %s\n", argv[argctr]); + fprintf(stderr, "invalid option: %s\n\n", argv[argctr]); usage(argv[0]); } @@ -81,12 +89,12 @@ static void set_signal_handlers() set_one_signal_handler(SIGPIPE, SIG_IGN); } -static int fuse_start(int fuse_fd, int flags, int multithreaded, +static int fuse_do(int fuse_fd, const char *opts, int multithreaded, int background, const struct fuse_operations *op) { int pid; - fuse = fuse_new(fuse_fd, flags, op); + fuse = fuse_new(fuse_fd, opts, op); if (fuse == NULL) return 1; @@ -111,35 +119,79 @@ static int fuse_start(int fuse_fd, int flags, int multithreaded, return 0; } +static void add_option_to(const char *opt, char **optp) +{ + unsigned len = strlen(opt); + if (*optp) { + unsigned oldlen = strlen(*optp); + *optp = realloc(*optp, oldlen + 1 + len + 1); + (*optp)[oldlen] = ','; + strcpy(*optp + oldlen + 1, opt); + } else { + *optp = malloc(len + 1); + strcpy(*optp, opt); + } +} + +static void add_options(char **lib_optp, char **kernel_optp, const char *opts) +{ + char *xopts = strdup(opts); + char *s = xopts; + char *opt; + + while((opt = strsep(&s, ",")) != NULL) { + if (fuse_is_lib_option(opt)) + add_option_to(opt, lib_optp); + else + add_option_to(opt, kernel_optp); + } + free(xopts); +} + void fuse_main(int argc, char *argv[], const struct fuse_operations *op) { int argctr; - int flags; int multithreaded; int background; int fuse_fd; char *fuse_mountpoint = NULL; - char **fusermount_args = NULL; - char *newargs[3]; char *basename; + char *kernel_opts = NULL; + char *lib_opts = NULL; + char *fsname_opt; int err; + + basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else if (basename[1] != '\0') + basename++; + + fsname_opt = malloc(strlen(basename) + 64); + sprintf(fsname_opt, "fsname=%s", basename); + add_options(&lib_opts, &kernel_opts, fsname_opt); + free(fsname_opt); - flags = 0; multithreaded = 1; background = 1; - for (argctr = 1; argctr < argc && !fusermount_args; argctr ++) { + 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\n"); + usage(argv[0]); + } + argctr ++; + add_options(&lib_opts, &kernel_opts, argv[argctr]); + break; + case 'd': - flags |= FUSE_DEBUG; + add_options(&lib_opts, &kernel_opts, "debug"); background = 0; break; - case 'i': - flags |= FUSE_HARD_REMOVE; - break; - case 'f': background = 0; break; @@ -152,15 +204,15 @@ void fuse_main(int argc, char *argv[], const struct fuse_operations *op) usage(argv[0]); break; - case '-': - fusermount_args = &argv[argctr+1]; - break; - default: invalid_option(argv, argctr); } - else - invalid_option(argv, argctr); + else { + if (argv[argctr][1] == 'o') + add_options(&lib_opts, &kernel_opts, &argv[argctr][2]); + else + invalid_option(argv, argctr); + } } else if (fuse_mountpoint == NULL) fuse_mountpoint = strdup(argv[argctr]); else @@ -168,30 +220,19 @@ void fuse_main(int argc, char *argv[], const struct fuse_operations *op) } if (fuse_mountpoint == NULL) { - fprintf(stderr, "missing mountpoint\n"); + fprintf(stderr, "missing mountpoint\n\n"); usage(argv[0]); } - if (fusermount_args != NULL) - fusermount_args -= 2; /* Hack! */ - else { - fusermount_args = newargs; - fusermount_args[2] = NULL; - } - basename = strrchr(argv[0], '/'); - if (basename == NULL) - basename = argv[0]; - else if (basename[1] != '\0') - basename++; - - fusermount_args[0] = "-n"; - fusermount_args[1] = basename; - - fuse_fd = fuse_mount(fuse_mountpoint, (const char **) fusermount_args); + fuse_fd = fuse_mount(fuse_mountpoint, kernel_opts); if (fuse_fd == -1) exit(1); + if (kernel_opts) + free(kernel_opts); - err = fuse_start(fuse_fd, flags, multithreaded, background, op); + err = fuse_do(fuse_fd, lib_opts, multithreaded, background, op); + if (lib_opts) + free(lib_opts); close(fuse_fd); fuse_unmount(fuse_mountpoint); if (err) diff --git a/lib/mount.c b/lib/mount.c index 648dfbc..05dcb59 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -78,7 +78,7 @@ void fuse_unmount(const char *mountpoint) system(umount_cmd); } -int fuse_mount(const char *mountpoint, const char *args[]) +int fuse_mount(const char *mountpoint, const char *opts) { const char *mountprog = FUSERMOUNT_PROG; int fds[2], pid; @@ -101,28 +101,14 @@ int fuse_mount(const char *mountpoint, const char *args[]) if(pid == 0) { char env[10]; - char **newargv; - int numargs = 0; - int actr; - int i; - - if(args != NULL) - while(args[numargs] != NULL) - numargs ++; - - newargv = (char **) malloc((1 + numargs + 2) * sizeof(char *)); - actr = 0; - newargv[actr++] = strdup(mountprog); - for(i = 0; i < numargs; i++) - newargv[actr++] = strdup(args[i]); - newargv[actr++] = strdup(mountpoint); - newargv[actr++] = NULL; + const char *argv[] = {mountprog, opts ? "-o" : mountpoint, opts, + mountpoint, NULL}; close(fds[1]); fcntl(fds[0], F_SETFD, 0); snprintf(env, sizeof(env), "%i", fds[0]); setenv(FUSE_COMMFD_ENV, env, 1); - execvp(mountprog, newargv); + execvp(mountprog, (char **) argv); perror("fuse: failed to exec fusermount"); exit(1); } diff --git a/util/fusermount.c b/util/fusermount.c index eff50bb..a6a02ec 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -41,14 +41,6 @@ const char *progname; -struct fuse_opts { - int kernel_cache; - int default_permissions; - int allow_other; - int large_read; - int direct_io; -}; - static const char *get_user_name() { struct passwd *pw = getpwuid(getuid()); @@ -97,7 +89,7 @@ static int add_mount(const char *fsname, const char *mnt, const char *type) fp = setmntent(mtab, "a"); if(fp == NULL) { - fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab, + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, strerror(errno)); return -1; } @@ -114,8 +106,10 @@ static int add_mount(const char *fsname, const char *mnt, const char *type) else opts = strdup("rw,nosuid,nodev"); - if(opts == NULL) + if(opts == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); return -1; + } ent.mnt_fsname = (char *) fsname; ent.mnt_dir = (char *) mnt; @@ -296,13 +290,23 @@ static void restore_privs() setfsgid(oldfsgid); } -static int do_mount(const char *dev, const char *mnt, const char *type, - mode_t rootmode, int fd, struct fuse_opts *opts) +static int begins_with(const char *s, const char *beg) +{ + if (strncmp(s, beg, strlen(beg)) == 0) + return 1; + else + return 0; +} + +static int do_mount(const char *mnt, const char *type, mode_t rootmode, + int fd, const char *opts, char **fsnamep) { int res; int flags = MS_NOSUID | MS_NODEV; - char optbuf[1024]; - char *s = optbuf; + char *optbuf; + const char *s; + char *d; + char *fsname = NULL; if(getuid() != 0) { res = drop_privs(); @@ -310,21 +314,56 @@ static int do_mount(const char *dev, const char *mnt, const char *type, return -1; } - s += sprintf(s, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid()); - if (opts->kernel_cache) - s += sprintf(s, ",kernel_cache"); - if (opts->default_permissions) - s += sprintf(s, ",default_permissions"); - if (opts->allow_other) - s += sprintf(s, ",allow_other"); - if (opts->large_read) - s += sprintf(s, ",large_read"); - if (opts->direct_io) - s += sprintf(s, ",direct_io"); - - res = mount(dev, mnt, type, flags, optbuf); - if(res == -1) + optbuf = malloc(strlen(opts) + 64); + if (!optbuf) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + for (s = opts, d = optbuf; *s;) { + unsigned len; + const char *fsname_str = "fsname="; + for (len = 0; s[len] && s[len] != ','; len++); + if (begins_with(s, fsname_str)) { + unsigned fsname_str_len = strlen(fsname_str); + if (fsname) + free(fsname); + fsname = malloc(len - fsname_str_len + 1); + if (!fsname) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + free(optbuf); + return -1; + } + memcpy(fsname, s + fsname_str_len, len - fsname_str_len); + fsname[len - fsname_str_len] = '\0'; + } else if (!begins_with(s, "fd=") && + !begins_with(s, "rootmode=") && + !begins_with(s, "uid=")) { + memcpy(d, s, len); + d += len; + *d++ = ','; + } + s += len; + if (*s) + s++; + } + sprintf(d, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid()); + if (fsname == NULL) { + fsname = strdup(FUSE_DEV); + if (!fsname) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + free(optbuf); + return -1; + } + } + + res = mount(fsname, mnt, type, flags, optbuf); + free(optbuf); + if(res == -1) { fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno)); + free(fsname); + } + *fsnamep = fsname; if(getuid() != 0) restore_privs(); @@ -370,8 +409,7 @@ static int check_perm(const char *mnt, struct stat *stbuf) return 0; } -static int mount_fuse(const char *mnt, struct fuse_opts *opts, - const char *fsname) +static int mount_fuse(const char *mnt, const char *opts) { int res; int fd; @@ -379,6 +417,7 @@ static int mount_fuse(const char *mnt, struct fuse_opts *opts, const char *type = "fuse"; struct stat stbuf; int mtablock; + char *fsname; res = check_perm(mnt, &stbuf); if(res == -1) @@ -404,22 +443,21 @@ static int mount_fuse(const char *mnt, struct fuse_opts *opts, return -1; } - if(fsname == NULL) - fsname = dev; - - res = do_mount(fsname, mnt, type, stbuf.st_mode & S_IFMT, fd, opts); + res = do_mount(mnt, type, stbuf.st_mode & S_IFMT, fd, opts, &fsname); if(res == -1) return -1; if(geteuid() == 0) { mtablock = lock_mtab(); res = add_mount(fsname, mnt, type); + free(fsname); unlock_mtab(mtablock); if(res == -1) { umount2(mnt, 2); /* lazy umount */ return -1; } - } + } else + free(fsname); return fd; } @@ -436,6 +474,9 @@ static char *resolve_path(const char *orig, int unmount) */ char *dst = strdup(orig); char *end; + if (dst == NULL) + return NULL; + for(end = dst + strlen(dst) - 1; end > dst && *end == '/'; end --) *end = '\0'; @@ -492,16 +533,11 @@ static void usage() fprintf(stderr, "%s: [options] mountpoint\n" "Options:\n" - " -h print help\n" - " -u unmount\n" - " -p check default permissions on files\n" - " -c cache in kernel space if possible\n" - " -x allow other users to access the files (only for root)\n" - " -n name add 'name' as the filesystem name to mtab\n" - " -l issue large reads\n" - " -r raw I/O\n" - " -q quiet: don't complain if unmount fails\n" - " -z lazy unmount\n", + " -h print help\n" + " -o opt[,opt...] mount options\n" + " -u unmount\n" + " -q quiet\n" + " -z lazy unmount\n", progname); exit(1); } @@ -516,13 +552,10 @@ int main(int argc, char *argv[]) int unmount = 0; int lazy = 0; char *commfd; - const char *fsname = NULL; int quiet = 0; - struct fuse_opts opts; int cfd; + const char *opts = ""; - - memset(&opts, 0, sizeof(struct fuse_opts)); progname = argv[0]; for(a = 1; a < argc; a++) { @@ -530,45 +563,25 @@ int main(int argc, char *argv[]) break; switch(argv[a][1]) { - case 'c': - opts.kernel_cache = 1; - break; - case 'h': usage(); break; - case 'u': - unmount = 1; - break; - - case 'z': - lazy = 1; - break; - - case 'p': - opts.default_permissions = 1; - break; - - case 'x': - opts.allow_other = 1; - break; - - case 'n': + case 'o': a++; if(a == argc) { - fprintf(stderr, "%s: Missing argument to -n\n", progname); + fprintf(stderr, "%s: Missing argument to -o\n", progname); exit(1); } - fsname = argv[a]; + opts = argv[a]; break; - case 'l': - opts.large_read = 1; + case 'u': + unmount = 1; break; - case 'r': - opts.direct_io = 1; + case 'z': + lazy = 1; break; case 'q': @@ -589,14 +602,16 @@ int main(int argc, char *argv[]) origmnt = argv[a++]; - if(getpid() != 0) + if(getuid() != 0) drop_privs(); mnt = resolve_path(origmnt, unmount); - if(mnt == NULL) + if(mnt == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); exit(1); + } - if(getpid() != 0) + if(getuid() != 0) restore_privs(); if(unmount) { @@ -623,7 +638,7 @@ int main(int argc, char *argv[]) exit(1); } - fd = mount_fuse(mnt, &opts, fsname); + fd = mount_fuse(mnt, opts); if(fd == -1) exit(1); -- 2.30.2