+2004-07-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 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 <miklos@szeredi.hu>
* Optimize reading under 2.6 kernels by issuing multiple page
* 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 *);
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
*
* 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
* 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
* 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.
*/
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... *
* ----------------------------------------------------------- */
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;
}
{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}
};
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;
return NULL;
}
- f->flags = flags;
+ parse_lib_opts(f, opts);
f->fd = fd;
f->ctr = 0;
f->generation = 0;
#include <stdio.h>
#include <pthread.h>
+/* 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 {
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]);
}
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;
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;
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
}
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)
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;
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);
}
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());
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;
}
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;
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();
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();
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;
const char *type = "fuse";
struct stat stbuf;
int mtablock;
+ char *fsname;
res = check_perm(mnt, &stbuf);
if(res == -1)
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;
}
*/
char *dst = strdup(orig);
char *end;
+ if (dst == NULL)
+ return NULL;
+
for(end = dst + strlen(dst) - 1; end > dst && *end == '/'; end --)
*end = '\0';
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);
}
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++) {
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':
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) {
exit(1);
}
- fd = mount_fuse(mnt, &opts, fsname);
+ fd = mount_fuse(mnt, opts);
if(fd == -1)
exit(1);