clean up mount option passing
diff --git a/ChangeLog b/ChangeLog
index 9f07e26..591f1cb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+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
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 @@
  *  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 @@
     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 @@
  * 
  * 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 @@
  * 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 @@
  * 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 @@
  */
 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 @@
 	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 @@
 	{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 @@
     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 @@
         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 <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 {
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 @@
 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 @@
     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 @@
     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 @@
                     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 @@
     }
 
     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 @@
     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 @@
 
     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 @@
 
     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 @@
     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 @@
     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 @@
             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");
+    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(dev, mnt, type, flags, optbuf);
-    if(res == -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 @@
     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 @@
     const char *type = "fuse";
     struct stat stbuf;
     int mtablock;
+    char *fsname;
 
     res = check_perm(mnt, &stbuf);
     if(res == -1)
@@ -404,22 +443,21 @@
         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 @@
         */
         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 @@
     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 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,14 +563,19 @@
             break;
 
         switch(argv[a][1]) {
-        case 'c':
-            opts.kernel_cache = 1;
-            break;
-
         case 'h':
             usage();
             break;
 
+        case 'o':
+            a++;
+            if(a == argc) {
+                fprintf(stderr, "%s: Missing argument to -o\n", progname);
+                exit(1);
+            }
+            opts = argv[a];
+            break;
+
         case 'u':
             unmount = 1;
             break;
@@ -546,31 +584,6 @@
             lazy = 1;
             break;
             
-        case 'p':
-            opts.default_permissions = 1;
-            break;
-            
-        case 'x':
-            opts.allow_other = 1;
-            break;
-            
-        case 'n':
-            a++;
-            if(a == argc) {
-                fprintf(stderr, "%s: Missing argument to -n\n", progname);
-                exit(1);
-            }
-            fsname = argv[a];
-            break;
-
-        case 'l':
-            opts.large_read = 1;
-            break;
-
-        case 'r':
-            opts.direct_io = 1;
-            break;
-            
         case 'q':
             quiet = 1;
             break;
@@ -589,14 +602,16 @@
 
     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 @@
         exit(1);
     }
 
-    fd = mount_fuse(mnt, &opts, fsname);
+    fd = mount_fuse(mnt, opts);
     if(fd == -1)
         exit(1);