updated to use libfuse's option parsing
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 9 Dec 2005 17:43:06 +0000 (17:43 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 9 Dec 2005 17:43:06 +0000 (17:43 +0000)
ChangeLog
Makefile.am
cache.c
cache.h
opts.c [deleted file]
opts.h [deleted file]
sshfs.c

index c3b136e..ae97538 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2005-12-09  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Use new option parsing interface of FUSE
+
 2005-11-28  Miklos Szeredi <miklos@szeredi.hu>
 
        * Set statvfs::f_frsize
index 1192634..0b5298b 100644 (file)
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
 
 bin_PROGRAMS = sshfs
-sshfs_SOURCES = sshfs.c cache.c opts.c cache.h opts.h
+sshfs_SOURCES = sshfs.c cache.c cache.h
 sshfs_CPPFLAGS = -DFUSE_USE_VERSION=25
diff --git a/cache.c b/cache.c
index 40ad0e3..8e44488 100644 (file)
--- a/cache.c
+++ b/cache.c
@@ -7,6 +7,7 @@
 */
 
 #include "cache.h"
+#include <fuse_opt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
 #include <pthread.h>
 
-#include "opts.h"
-
 #define DEFAULT_CACHE_TIMEOUT 20
 #define MAX_CACHE_SIZE 10000
 #define MIN_CACHE_CLEAN_INTERVAL 5
 #define CACHE_CLEAN_INTERVAL 60
 
-static int cache_on = 1;
-static unsigned cache_stat_timeout = DEFAULT_CACHE_TIMEOUT;
-static unsigned cache_dir_timeout = DEFAULT_CACHE_TIMEOUT;
-static unsigned cache_link_timeout = DEFAULT_CACHE_TIMEOUT;
+struct cache {
+    int on;
+    unsigned stat_timeout;
+    unsigned dir_timeout;
+    unsigned link_timeout;
+    struct fuse_cache_operations *next_oper;
+    GHashTable *table;
+    pthread_mutex_t lock;
+    time_t last_cleaned;
+};
+
+static struct cache cache;
 
 struct node {
     struct stat stat;
@@ -43,11 +50,6 @@ struct fuse_cache_dirhandle {
     GPtrArray *dir;
 };
 
-static struct fuse_cache_operations *next_oper;
-static GHashTable *cache;
-static pthread_mutex_t cache_lock;
-static time_t last_cleaned;
-
 static void free_node(gpointer node_)
 {
     struct node *node = (struct node *) node_;
@@ -67,22 +69,23 @@ static int cache_clean_entry(void *key_, struct node *node, time_t *now)
 static void cache_clean(void)
 {
     time_t now = time(NULL);
-    if (now > last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
-         (g_hash_table_size(cache) > MAX_CACHE_SIZE ||
-          now > last_cleaned + CACHE_CLEAN_INTERVAL)) {
-        g_hash_table_foreach_remove(cache, (GHRFunc) cache_clean_entry, &now);
-        last_cleaned = now;
+    if (now > cache.last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
+         (g_hash_table_size(cache.table) > MAX_CACHE_SIZE ||
+          now > cache.last_cleaned + CACHE_CLEAN_INTERVAL)) {
+        g_hash_table_foreach_remove(cache.table,
+                                    (GHRFunc) cache_clean_entry, &now);
+        cache.last_cleaned = now;
     }
 }
 
 static struct node *cache_lookup(const char *path)
 {
-    return (struct node *) g_hash_table_lookup(cache, path);
+    return (struct node *) g_hash_table_lookup(cache.table, path);
 }
 
 static void cache_purge(const char *path)
 {
-    g_hash_table_remove(cache, path);
+    g_hash_table_remove(cache.table, path);
 }
 
 static void cache_purge_parent(const char *path)
@@ -90,7 +93,7 @@ static void cache_purge_parent(const char *path)
     const char *s = strrchr(path, '/');
     if (s) {
         if (s == path)
-            g_hash_table_remove(cache, "/");
+            g_hash_table_remove(cache.table, "/");
         else {
             char *parent = g_strndup(path, s - path);
             cache_purge(parent);
@@ -101,27 +104,27 @@ static void cache_purge_parent(const char *path)
 
 static void cache_invalidate(const char *path)
 {
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     cache_purge(path);
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static void cache_invalidate_dir(const char *path)
 {
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     cache_purge(path);
     cache_purge_parent(path);
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static void cache_do_rename(const char *from, const char *to)
 {
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     cache_purge(from);
     cache_purge(to);
     cache_purge_parent(from);
     cache_purge_parent(to);
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static struct node *cache_get(const char *path)
@@ -130,7 +133,7 @@ static struct node *cache_get(const char *path)
     if (node == NULL) {
         char *pathcopy = g_strdup(path);
         node = g_new0(struct node, 1);
-        g_hash_table_insert(cache, pathcopy, node);
+        g_hash_table_insert(cache.table, pathcopy, node);
     }
     return node;
 }
@@ -140,15 +143,15 @@ static void cache_add_attr(const char *path, const struct stat *stbuf)
     struct node *node;
     time_t now;
 
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_get(path);
     now = time(NULL);
     node->stat = *stbuf;
-    node->stat_valid = time(NULL) + cache_stat_timeout;
+    node->stat_valid = time(NULL) + cache.stat_timeout;
     if (node->stat_valid > node->valid)
         node->valid = node->stat_valid;
     cache_clean();
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static void cache_add_dir(const char *path, char **dir)
@@ -156,16 +159,16 @@ static void cache_add_dir(const char *path, char **dir)
     struct node *node;
     time_t now;
 
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_get(path);
     now = time(NULL);
     g_strfreev(node->dir);
     node->dir = dir;
-    node->dir_valid = time(NULL) + cache_dir_timeout;
+    node->dir_valid = time(NULL) + cache.dir_timeout;
     if (node->dir_valid > node->valid)
         node->valid = node->dir_valid;
     cache_clean();
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static size_t my_strnlen(const char *s, size_t maxsize)
@@ -180,23 +183,23 @@ static void cache_add_link(const char *path, const char *link, size_t size)
     struct node *node;
     time_t now;
 
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_get(path);
     now = time(NULL);
     g_free(node->link);
     node->link = g_strndup(link, my_strnlen(link, size-1));
-    node->link_valid = time(NULL) + cache_link_timeout;
+    node->link_valid = time(NULL) + cache.link_timeout;
     if (node->link_valid > node->valid)
         node->valid = node->link_valid;
     cache_clean();
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 }
 
 static int cache_get_attr(const char *path, struct stat *stbuf)
 {
     struct node *node;
     int err = -EAGAIN;
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_lookup(path);
     if (node != NULL) {
         time_t now = time(NULL);
@@ -205,7 +208,7 @@ static int cache_get_attr(const char *path, struct stat *stbuf)
             err = 0;
         }
     }
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
     return err;
 }
 
@@ -213,7 +216,7 @@ static int cache_getattr(const char *path, struct stat *stbuf)
 {
     int err = cache_get_attr(path, stbuf);
     if (err) {
-        err = next_oper->oper.getattr(path, stbuf);
+        err = cache.next_oper->oper.getattr(path, stbuf);
         if (!err)
             cache_add_attr(path, stbuf);
     }
@@ -225,19 +228,19 @@ static int cache_readlink(const char *path, char *buf, size_t size)
     struct node *node;
     int err;
 
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_lookup(path);
     if (node != NULL) {
         time_t now = time(NULL);
         if (node->link_valid - now >= 0) {
             strncpy(buf, node->link, size-1);
             buf[size-1] = '\0';
-            pthread_mutex_unlock(&cache_lock);
+            pthread_mutex_unlock(&cache.lock);
             return 0;
         }
     }
-    pthread_mutex_unlock(&cache_lock);
-    err = next_oper->oper.readlink(path, buf, size);
+    pthread_mutex_unlock(&cache.lock);
+    err = cache.next_oper->oper.readlink(path, buf, size);
     if (!err)
         cache_add_link(path, buf, size);
 
@@ -265,24 +268,24 @@ static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
     char **dir;
     struct node *node;
 
-    pthread_mutex_lock(&cache_lock);
+    pthread_mutex_lock(&cache.lock);
     node = cache_lookup(path);
     if (node != NULL && node->dir != NULL) {
         time_t now = time(NULL);
         if (node->dir_valid - now >= 0) {
             for(dir = node->dir; *dir != NULL; dir++)
                 filler(h, *dir, 0, 0);
-            pthread_mutex_unlock(&cache_lock);
+            pthread_mutex_unlock(&cache.lock);
             return 0;
         }
     }
-    pthread_mutex_unlock(&cache_lock);
+    pthread_mutex_unlock(&cache.lock);
 
     ch.path = path;
     ch.h = h;
     ch.filler = filler;
     ch.dir = g_ptr_array_new();
-    err = next_oper->cache_getdir(path, &ch, cache_dirfill);
+    err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
     g_ptr_array_add(ch.dir, NULL);
     dir = (char **) ch.dir->pdata;
     if (!err)
@@ -306,12 +309,12 @@ static int cache_unity_getdir(const char *path, fuse_dirh_t h,
     struct fuse_cache_dirhandle ch;
     ch.h = h;
     ch.filler = filler;
-    return next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
+    return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
 }
 
 static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
 {
-    int err = next_oper->oper.mknod(path, mode, rdev);
+    int err = cache.next_oper->oper.mknod(path, mode, rdev);
     if (!err)
         cache_invalidate_dir(path);
     return err;
@@ -319,7 +322,7 @@ static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
 
 static int cache_mkdir(const char *path, mode_t mode)
 {
-    int err = next_oper->oper.mkdir(path, mode);
+    int err = cache.next_oper->oper.mkdir(path, mode);
     if (!err)
         cache_invalidate_dir(path);
     return err;
@@ -327,7 +330,7 @@ static int cache_mkdir(const char *path, mode_t mode)
 
 static int cache_unlink(const char *path)
 {
-    int err = next_oper->oper.unlink(path);
+    int err = cache.next_oper->oper.unlink(path);
     if (!err)
         cache_invalidate_dir(path);
     return err;
@@ -335,7 +338,7 @@ static int cache_unlink(const char *path)
 
 static int cache_rmdir(const char *path)
 {
-    int err = next_oper->oper.rmdir(path);
+    int err = cache.next_oper->oper.rmdir(path);
     if (!err)
         cache_invalidate_dir(path);
     return err;
@@ -343,7 +346,7 @@ static int cache_rmdir(const char *path)
 
 static int cache_symlink(const char *from, const char *to)
 {
-    int err = next_oper->oper.symlink(from, to);
+    int err = cache.next_oper->oper.symlink(from, to);
     if (!err)
         cache_invalidate_dir(to);
     return err;
@@ -351,7 +354,7 @@ static int cache_symlink(const char *from, const char *to)
 
 static int cache_rename(const char *from, const char *to)
 {
-    int err = next_oper->oper.rename(from, to);
+    int err = cache.next_oper->oper.rename(from, to);
     if (!err)
         cache_do_rename(from, to);
     return err;
@@ -359,7 +362,7 @@ static int cache_rename(const char *from, const char *to)
 
 static int cache_link(const char *from, const char *to)
 {
-    int err = next_oper->oper.link(from, to);
+    int err = cache.next_oper->oper.link(from, to);
     if (!err) {
         cache_invalidate(from);
         cache_invalidate_dir(to);
@@ -369,7 +372,7 @@ static int cache_link(const char *from, const char *to)
 
 static int cache_chmod(const char *path, mode_t mode)
 {
-    int err = next_oper->oper.chmod(path, mode);
+    int err = cache.next_oper->oper.chmod(path, mode);
     if (!err)
         cache_invalidate(path);
     return err;
@@ -377,7 +380,7 @@ static int cache_chmod(const char *path, mode_t mode)
 
 static int cache_chown(const char *path, uid_t uid, gid_t gid)
 {
-    int err = next_oper->oper.chown(path, uid, gid);
+    int err = cache.next_oper->oper.chown(path, uid, gid);
     if (!err)
         cache_invalidate(path);
     return err;
@@ -385,7 +388,7 @@ static int cache_chown(const char *path, uid_t uid, gid_t gid)
 
 static int cache_truncate(const char *path, off_t size)
 {
-    int err = next_oper->oper.truncate(path, size);
+    int err = cache.next_oper->oper.truncate(path, size);
     if (!err)
         cache_invalidate(path);
     return err;
@@ -393,7 +396,7 @@ static int cache_truncate(const char *path, off_t size)
 
 static int cache_utime(const char *path, struct utimbuf *buf)
 {
-    int err = next_oper->oper.utime(path, buf);
+    int err = cache.next_oper->oper.utime(path, buf);
     if (!err)
         cache_invalidate(path);
     return err;
@@ -402,7 +405,7 @@ static int cache_utime(const char *path, struct utimbuf *buf)
 static int cache_write(const char *path, const char *buf, size_t size,
                        off_t offset, struct fuse_file_info *fi)
 {
-    int res = next_oper->oper.write(path, buf, size, offset, fi);
+    int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
     if (res >= 0)
         cache_invalidate(path);
     return res;
@@ -412,7 +415,7 @@ static int cache_write(const char *path, const char *buf, size_t size,
 static int cache_create(const char *path, mode_t mode,
                         struct fuse_file_info *fi)
 {
-    int err = next_oper->oper.create(path, mode, fi);
+    int err = cache.next_oper->oper.create(path, mode, fi);
     if (!err)
         cache_invalidate_dir(path);
     return err;
@@ -421,7 +424,7 @@ static int cache_create(const char *path, mode_t mode,
 static int cache_ftruncate(const char *path, off_t size,
                            struct fuse_file_info *fi)
 {
-    int err = next_oper->oper.ftruncate(path, size, fi);
+    int err = cache.next_oper->oper.ftruncate(path, size, fi);
     if (!err)
         cache_invalidate(path);
     return err;
@@ -432,7 +435,7 @@ static int cache_fgetattr(const char *path, struct stat *stbuf,
 {
     int err = cache_get_attr(path, stbuf);
     if (err) {
-        err = next_oper->oper.fgetattr(path, stbuf, fi);
+        err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
         if (!err)
             cache_add_attr(path, stbuf);
     }
@@ -481,10 +484,10 @@ static void cache_unity_fill(struct fuse_cache_operations *oper,
 struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
 {
     static struct fuse_operations cache_oper;
-    next_oper = oper;
+    cache.next_oper = oper;
 
     cache_unity_fill(oper, &cache_oper);
-    if (cache_on) {
+    if (cache.on) {
         cache_oper.getattr  = oper->oper.getattr ? cache_getattr : NULL;
         cache_oper.readlink = oper->oper.readlink ? cache_readlink : NULL;
         cache_oper.getdir   = oper->cache_getdir ? cache_getdir : NULL;
@@ -505,10 +508,10 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
         cache_oper.ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
         cache_oper.fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
 #endif
-        pthread_mutex_init(&cache_lock, NULL);
-        cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
-                                      free_node);
-        if (cache == NULL) {
+        pthread_mutex_init(&cache.lock, NULL);
+        cache.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                                            free_node);
+        if (cache.table == NULL) {
             fprintf(stderr, "failed to create cache\n");
             return NULL;
         }
@@ -516,61 +519,31 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
     return &cache_oper;
 }
 
-enum {
-    COPT_CACHE,
-    COPT_TIMEOUT,
-    COPT_STAT_TIMEOUT,
-    COPT_DIR_TIMEOUT,
-    COPT_LINK_TIMEOUT,
-    COPT_LAST /* Last entry in this list! */
-};
-
-static struct opt cache_opts[] = {
-    [COPT_CACHE] =        { .optname = "cache" },
-    [COPT_TIMEOUT] =      { .optname = "cache_timeout" },
-    [COPT_STAT_TIMEOUT] = { .optname = "cache_stat_timeout" },
-    [COPT_DIR_TIMEOUT]  = { .optname = "cache_dir_timeout" },
-    [COPT_LINK_TIMEOUT] = { .optname = "cache_link_timeout" },
-    [COPT_LAST] =         { .optname = NULL }
-};
-
-static int get_timeout(int sel, unsigned *timeoutp)
+static int cache_opt_proc(void *data, const char *arg, int key)
 {
-    struct opt *o = &cache_opts[sel];
-    if (!o->present)
-        return 0;
-    if (opt_get_unsigned(o, timeoutp) == -1)
-        return -1;
+    (void) data, (void) arg, (void) key;
     return 1;
 }
 
-int cache_parse_options(int *argcp, char *argv[])
+static const struct fuse_opt cache_opts[] = {
+    { "cache=yes", offsetof(struct cache, on), 1 },
+    { "cache=no", offsetof(struct cache, on), 0 },
+    { "cache_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
+    { "cache_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
+    { "cache_timeout=%u", offsetof(struct cache, link_timeout), 0 },
+    { "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
+    { "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
+    { "cache_link_timeout=%u", offsetof(struct cache, link_timeout), 0 },
+    FUSE_OPT_END
+};
+
+int cache_parse_options(int *argcp, char **argvp[])
 {
-    unsigned timeout;
-    int res;
-    process_options(argcp, argv, cache_opts, 1);
-    if (cache_opts[COPT_CACHE].present) {
-        char *val = cache_opts[COPT_CACHE].value;
-        if (!val || !val[0] ||
-            (strcmp(val, "yes") != 0 && strcmp(val, "no") != 0)) {
-            fprintf(stderr, "Invalid or missing value for 'cache' option\n");
-            return -1;
-        }
-        if (strcmp(val, "yes") == 0)
-            cache_on = 1;
-        else
-            cache_on = 0;
-    }
-    if ((res = get_timeout(COPT_TIMEOUT, &timeout)) == -1)
-        return -1;
-    if (res == 1) {
-        cache_stat_timeout = timeout;
-        cache_dir_timeout = timeout;
-        cache_link_timeout = timeout;
-    }
-    if(get_timeout(COPT_STAT_TIMEOUT, &cache_stat_timeout) == -1 ||
-       get_timeout(COPT_DIR_TIMEOUT, &cache_dir_timeout) == -1 ||
-       get_timeout(COPT_LINK_TIMEOUT, &cache_link_timeout) == -1)
-        return -1;
-    return 0;
+    cache.stat_timeout = DEFAULT_CACHE_TIMEOUT;
+    cache.dir_timeout = DEFAULT_CACHE_TIMEOUT;
+    cache.link_timeout = DEFAULT_CACHE_TIMEOUT;
+    cache.on = 1;
+
+    return fuse_opt_parse(0, NULL, &cache, cache_opts, cache_opt_proc,
+                          argcp, argvp);
 }
diff --git a/cache.h b/cache.h
index b0b93b1..052c787 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -23,4 +23,4 @@ struct fuse_cache_operations {
 };
 
 struct fuse_operations *cache_init(struct fuse_cache_operations *oper);
-int cache_parse_options(int *argcp, char *argv[]);
+int cache_parse_options(int *argcp, char **argv[]);
diff --git a/opts.c b/opts.c
deleted file mode 100644 (file)
index 8cc3d76..0000000
--- a/opts.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-    Mount option parsing
-    Copyright (C) 2004  Miklos Szeredi <miklos@szeredi.hu>
-
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
-*/
-
-#include "opts.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-
-static int process_option(char *arg, struct opt opts[], int case_sensitive)
-{
-    int i;
-    char *eq = strchr(arg, '=');
-    if (eq)
-        *eq = '\0';
-
-    for (i = 0; opts[i].optname != NULL; i++) {
-        if (case_sensitive) {
-            if (strcmp(opts[i].optname, arg) == 0)
-                break;
-        } else if (strcasecmp(opts[i].optname, arg) == 0)
-            break;
-    }
-    if (opts[i].optname == NULL) {
-        if (eq)
-            *eq = '=';
-        return 0;
-    }
-    opts[i].present = 1;
-    if (eq) {
-        if (opts[i].value)
-            g_free(opts[i].value);
-        opts[i].value = g_strdup(eq+1);
-    }
-    return 1;
-}
-
-static int process_option_group(char *arg, struct opt opts[],
-                                int case_sensitive)
-{
-    int remove = 1;
-    char *prevcomma = NULL;
-    while (1) {
-        int remove_one;
-        char *comma = strchr(arg, ',');
-        if (comma)
-            *comma = '\0';
-        remove_one = process_option(arg, opts, case_sensitive);
-        if (remove_one) {
-            if (comma)
-                memmove(arg, comma + 1, strlen(comma + 1) + 1);
-        } else {
-            remove = 0;
-            if (comma)
-                arg = comma + 1;
-            if (prevcomma) 
-                *prevcomma = ',';
-            prevcomma = comma;
-        }
-        if (!comma)
-            break;
-    }
-    return remove;
-}
-
-void process_options(int *argcp, char *argv[], struct opt opts[],
-                     int case_sensitive)
-{
-    int argctr;
-    int newargctr;
-
-    for (argctr = 1, newargctr = 1; argctr < *argcp; argctr++) {
-        char *arg = argv[argctr];
-        int removed = 0;
-        if (arg[0] == '-' && arg[1] == 'o') {
-            if (arg[2])
-                removed = process_option_group(arg+2, opts, case_sensitive);
-            else {
-                if (argctr + 1 < *argcp) {
-                    argctr++;
-                    arg = argv[argctr];
-                    removed = process_option_group(arg, opts, case_sensitive);
-                    if (removed)
-                        g_free(argv[argctr-1]);
-                    else if (argctr != newargctr)
-                        argv[newargctr++] = argv[argctr-1];
-                }
-            }
-        }
-        if (removed)
-            g_free(arg);
-        else {
-            if(argctr != newargctr)
-                argv[newargctr] = arg;
-            newargctr++;
-        }
-    }
-    *argcp = newargctr;
-}
-
-int opt_get_unsigned(const struct opt *o, unsigned *valp)
-{
-    char *end;
-    unsigned val;
-    if (!o->value || !o->value[0]) {
-        fprintf(stderr, "Missing value for '%s' option\n", o->optname);
-        return -1;
-    }
-    val = strtoul(o->value, &end, 0);
-    if (end[0]) {
-        fprintf(stderr, "Invalid value for '%s' option\n", o->optname);
-        return -1;
-    }
-    *valp = val;
-    return 0;
-}
-
-char *opt_get_string(const struct opt *o)
-{
-    if (!o->value || !o->value[0]) {
-        fprintf(stderr, "Missing value for '%s' option\n", o->optname);
-        return NULL;
-    }
-    return o->value;
-}
diff --git a/opts.h b/opts.h
deleted file mode 100644 (file)
index 196c047..0000000
--- a/opts.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-    Mount option parsing
-    Copyright (C) 2004  Miklos Szeredi <miklos@szeredi.hu>
-
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
-*/
-
-struct opt {
-    const char *optname;
-    int present;
-    char *value;
-};
-
-void process_options(int *argcp, char *argv[], struct opt opts[], 
-                     int case_sensitive);
-
-int opt_get_unsigned(const struct opt *o, unsigned *valp);
-
-char *opt_get_string(const struct opt *o);
diff --git a/sshfs.c b/sshfs.c
index 2fbc26c..78519ba 100644 (file)
--- a/sshfs.c
+++ b/sshfs.c
@@ -9,6 +9,7 @@
 #include "config.h"
 
 #include <fuse.h>
+#include <fuse_opt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -27,7 +28,6 @@
 #include <glib.h>
 
 #include "cache.h"
-#include "opts.h"
 
 #if FUSE_VERSION >= 23
 #define SSHFS_USE_INIT
 
 #define SFTP_SERVER_PATH "/usr/lib/sftp-server"
 
-static unsigned int randseed;
-
-static int infd;
-static int outfd;
-static int connver;
-static int server_version;
-static unsigned ssh_ver = 2;
-static char *sftp_server = NULL;
-static int debug = 0;
-static int reconnect = 0;
-static int sync_write = 0;
-static int sync_read = 0;
-static int rename_workaround = 0;
-static int detect_uid = 0;
-static unsigned remote_uid;
-static unsigned local_uid;
-static int remote_uid_detected = 0;
-static char *base_path;
-static char *host;
-static unsigned blksize = 4096;
-
 struct buffer {
     uint8_t *p;
     size_t len;
@@ -162,86 +141,120 @@ struct sshfs_file {
     int connver;
 };
 
-static GHashTable *reqtab;
-static pthread_mutex_t lock;
-static int processing_thread_started;
-
-
-static struct opt ssh_opts[] = {
-    { .optname = "AddressFamily"                    },
-    { .optname = "BatchMode"                        },
-    { .optname = "BindAddress"                      },
-    { .optname = "ChallengeResponseAuthentication"  },
-    { .optname = "CheckHostIP"                      },
-    { .optname = "Cipher"                           },
-    { .optname = "Ciphers"                          },
-    { .optname = "Compression"                      },
-    { .optname = "CompressionLevel"                 },
-    { .optname = "ConnectionAttempts"               },
-    { .optname = "ConnectTimeout"                   },
-    { .optname = "GlobalKnownHostsFile"             },
-    { .optname = "GSSAPIAuthentication"             },
-    { .optname = "GSSAPIDelegateCredentials"        },
-    { .optname = "HostbasedAuthentication"          },
-    { .optname = "HostKeyAlgorithms"                },
-    { .optname = "HostKeyAlias"                     },
-    { .optname = "HostName"                         },
-    { .optname = "IdentityFile"                     },
-    { .optname = "IdentitiesOnly"                   },
-    { .optname = "LogLevel"                         },
-    { .optname = "MACs"                             },
-    { .optname = "NoHostAuthenticationForLocalhost" },
-    { .optname = "NumberOfPasswordPrompts"          },
-    { .optname = "PasswordAuthentication"           },
-    { .optname = "Port"                             },
-    { .optname = "PreferredAuthentications"         },
-    { .optname = "ProxyCommand"                     },
-    { .optname = "PubkeyAuthentication"             },
-    { .optname = "RhostsRSAAuthentication"          },
-    { .optname = "RSAAuthentication"                },
-    { .optname = "ServerAliveInterval"              },
-    { .optname = "ServerAliveCountMax"              },
-    { .optname = "SmartcardDevice"                  },
-    { .optname = "StrictHostKeyChecking"            },
-    { .optname = "TCPKeepAlive"                     },
-    { .optname = "UsePrivilegedPort"                },
-    { .optname = "UserKnownHostsFile"               },
-    { .optname = "VerifyHostKeyDNS"                 },
-    { .optname = NULL                               }
+struct sshfs {
+    const char *progname;
+    char *directport;
+    char *ssh_command;
+    char *sftp_server;
+    char **ssh_args;
+    int ssh_argc;
+    int rename_workaround;
+    int detect_uid;
+    unsigned max_read;
+    unsigned ssh_ver;
+    int sync_write;
+    int sync_read;
+    int debug;
+    int reconnect;
+    char *host;
+    char *base_path;
+    GHashTable *reqtab;
+    pthread_mutex_t lock;
+    int processing_thread_started;
+    unsigned int randseed;
+    int infd;
+    int outfd;
+    int connver;
+    int server_version;
+    unsigned remote_uid;
+    unsigned local_uid;
+    int remote_uid_detected;
+    unsigned blksize;
+};
+
+static struct sshfs sshfs;
+
+static const char *ssh_opts[] = {
+    "AddressFamily",
+    "BatchMode",
+    "BindAddress",
+    "ChallengeResponseAuthentication",
+    "CheckHostIP",
+    "Cipher",
+    "Ciphers",
+    "Compression",
+    "CompressionLevel",
+    "ConnectionAttempts",
+    "ConnectTimeout",
+    "GlobalKnownHostsFile",
+    "GSSAPIAuthentication",
+    "GSSAPIDelegateCredentials",
+    "HostbasedAuthentication",
+    "HostKeyAlgorithms",
+    "HostKeyAlias",
+    "HostName",
+    "IdentityFile",
+    "IdentitiesOnly",
+    "LogLevel",
+    "MACs",
+    "NoHostAuthenticationForLocalhost",
+    "NumberOfPasswordPrompts",
+    "PasswordAuthentication",
+    "Port",
+    "PreferredAuthentications",
+    "ProxyCommand",
+    "PubkeyAuthentication",
+    "RhostsRSAAuthentication",
+    "RSAAuthentication",
+    "ServerAliveInterval",
+    "ServerAliveCountMax",
+    "SmartcardDevice",
+    "StrictHostKeyChecking",
+    "TCPKeepAlive",
+    "UsePrivilegedPort",
+    "UserKnownHostsFile",
+    "VerifyHostKeyDNS",
+    NULL,
 };
 
 enum {
-    SOPT_DIRECTPORT,
-    SOPT_SSHCMD,
-    SOPT_SYNC_WRITE,
-    SOPT_SYNC_READ,
-    SOPT_MAX_READ,
-    SOPT_DEBUG,
-    SOPT_RECONNECT,
-    SOPT_WORKAROUND,
-    SOPT_IDMAP,
-    SOPT_PROTOCOL,
-    SOPT_SFTPSERVER,
-    SOPT_LAST /* Last entry in this list! */
+    KEY_PORT,
+    KEY_COMPRESS,
+    KEY_HELP,
+    KEY_VERSION,
 };
 
-static struct opt sshfs_opts[] = {
-    [SOPT_DIRECTPORT] = { .optname = "directport" },
-    [SOPT_SSHCMD]     = { .optname = "ssh_command" },
-    [SOPT_SYNC_WRITE] = { .optname = "sshfs_sync" },
-    [SOPT_SYNC_READ]  = { .optname = "no_readahead" },
-    [SOPT_MAX_READ]   = { .optname = "max_read" },
-    [SOPT_DEBUG]      = { .optname = "sshfs_debug" },
-    [SOPT_RECONNECT]  = { .optname = "reconnect" },
-    [SOPT_WORKAROUND] = { .optname = "workaround" },
-    [SOPT_IDMAP]      = { .optname = "idmap" },
-    [SOPT_PROTOCOL]   = { .optname = "ssh_protocol" },
-    [SOPT_SFTPSERVER] = { .optname = "sftp_server" },
-    [SOPT_LAST]       = { .optname = NULL }
+#define SSHFS_OPT(t, p, v) { t, offsetof(struct sshfs, p), v }
+#define SSHFS_KEY(t, k)    { t, FUSE_OPT_OFFSET_KEY, k }
+
+static struct fuse_opt sshfs_opts[] = {
+    SSHFS_OPT("directport=%s",     directport, 0),
+    SSHFS_OPT("ssh_command=%s",    ssh_command, 0),
+    SSHFS_OPT("sftp_server=%s",    sftp_server, 0),
+    SSHFS_OPT("max_read=%u",       max_read, 0),
+    SSHFS_OPT("ssh_protocol=%u",   ssh_ver, 0),
+    SSHFS_OPT("-1",                ssh_ver, 1),
+    SSHFS_OPT("workaround=none",   rename_workaround, 0),
+    SSHFS_OPT("workaround=rename", rename_workaround, 1),
+    SSHFS_OPT("workaround=all",    rename_workaround, 1),
+    SSHFS_OPT("idmap=none",        detect_uid, 0),
+    SSHFS_OPT("idmap=user",        detect_uid, 1),
+    SSHFS_OPT("sshfs_sync",        sync_write, 1),
+    SSHFS_OPT("no_readahead",      sync_read, 1),
+    SSHFS_OPT("sshfs_debug",       debug, 1),
+    SSHFS_OPT("reconnect",         reconnect, 1),
+
+    SSHFS_KEY("-p ",               KEY_PORT),
+    SSHFS_KEY("-C",                KEY_COMPRESS),
+    SSHFS_KEY("-V",                KEY_VERSION),
+    SSHFS_KEY("--version",         KEY_VERSION),
+    SSHFS_KEY("-h",                KEY_HELP),
+    SSHFS_KEY("--help",            KEY_HELP),
+    FUSE_OPT_END
 };
 
 #define DEBUG(format, args...) \
-       do { if (debug) fprintf(stderr, format, args); } while(0)
+       do { if (sshfs.debug) fprintf(stderr, format, args); } while(0)
 
 static const char *type_name(uint8_t type)
 {
@@ -318,8 +331,10 @@ static inline void buf_init(struct buffer *buf, size_t size)
 {
     if (size) {
         buf->p = (uint8_t *) malloc(size);
-        if (!buf->p)
+        if (!buf->p) {
+            fprintf(stderr, "sshfs: memory allocation failed\n");
             exit(1);
+        }
     } else
         buf->p = NULL;
     buf->len = 0;
@@ -347,8 +362,10 @@ static void buf_resize(struct buffer *buf, size_t len)
 {
     buf->size = (buf->len + len + 63) & ~31;
     buf->p = (uint8_t *) realloc(buf->p, buf->size);
-    if (!buf->p)
+    if (!buf->p) {
+        fprintf(stderr, "sshfs: memory allocation failed\n");
         exit(1);
+    }
 }
 
 static inline void buf_check_add(struct buffer *buf, size_t len)
@@ -407,7 +424,8 @@ static inline void buf_add_string(struct buffer *buf, const char *str)
 
 static inline void buf_add_path(struct buffer *buf, const char *path)
 {
-    char *realpath = g_strdup_printf("%s%s", base_path, path[1] ? path+1 : ".");
+    char *realpath =
+        g_strdup_printf("%s%s", sshfs.base_path, path[1] ? path+1 : ".");
     buf_add_string(buf, realpath);
     g_free(realpath);
 }
@@ -523,16 +541,17 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf, int *flagsp)
         }
     }
 
-    if (remote_uid_detected && uid == remote_uid)
-        uid = local_uid;
+    if (sshfs.remote_uid_detected && uid == sshfs.remote_uid)
+        uid = sshfs.local_uid;
 
     memset(stbuf, 0, sizeof(struct stat));
     stbuf->st_mode = mode;
     stbuf->st_nlink = 1;
     stbuf->st_size = size;
-    if (blksize) {
-        stbuf->st_blksize = blksize;
-        stbuf->st_blocks = ((size + blksize - 1) & ~(blksize - 1)) >> 9;
+    if (sshfs.blksize) {
+        stbuf->st_blksize = sshfs.blksize;
+        stbuf->st_blocks =
+            ((size + sshfs.blksize - 1) & ~(sshfs.blksize - 1)) >> 9;
     }
     stbuf->st_uid = uid;
     stbuf->st_gid = gid;
@@ -571,19 +590,24 @@ static int buf_get_entries(struct buffer *buf, fuse_cache_dirh_t h,
     return 0;
 }
 
-static int start_ssh(char *host)
+static void ssh_add_arg(const char *arg)
+{
+    if (fuse_opt_add_arg(&sshfs.ssh_argc, &sshfs.ssh_args, arg) == -1)
+        _exit(1);
+}
+
+static int start_ssh(void)
 {
     int inpipe[2];
     int outpipe[2];
-    int i;
     int pid;
 
     if (pipe(inpipe) == -1 || pipe(outpipe) == -1) {
         perror("failed to create pipe");
         return -1;
     }
-    infd = inpipe[0];
-    outfd = outpipe[1];
+    sshfs.infd = inpipe[0];
+    sshfs.outfd = outpipe[1];
 
     pid = fork();
     if (pid == -1) {
@@ -591,23 +615,14 @@ static int start_ssh(char *host)
         return -1;
     } else if (pid == 0) {
         int devnull;
-        int argctr = 0;
-        char *ssh_args[sizeof(ssh_opts)/sizeof(struct opt) + 32];
-        char *ssh_cmd;
-        char veropt[64];
 
         devnull = open("/dev/null", O_WRONLY);
 
-        if (sshfs_opts[SOPT_SSHCMD].present)
-            ssh_cmd = sshfs_opts[SOPT_SSHCMD].value;
-        else
-            ssh_cmd = "ssh";
-
         if (dup2(outpipe[0], 0) == -1 || dup2(inpipe[1], 1) == -1) {
             perror("failed to redirect input/output");
             _exit(1);
         }
-        if (!debug && devnull != -1)
+        if (!sshfs.debug && devnull != -1)
             dup2(devnull, 2);
 
         close(devnull);
@@ -627,33 +642,8 @@ static int start_ssh(char *host)
         }
         chdir("/");
 
-        ssh_args[argctr++] = ssh_cmd;
-        sprintf(veropt, "-%i", ssh_ver);
-        ssh_args[argctr++] = veropt;
-        ssh_args[argctr++] = "-x";
-        ssh_args[argctr++] = "-a";
-        ssh_args[argctr++] = "-oClearAllForwardings=yes";
-        for (i = 0; ssh_opts[i].optname != NULL; i++) {
-            if (ssh_opts[i].present) {
-                char *val = ssh_opts[i].value;
-                char *sopt = g_strdup_printf("-o%s=%s", ssh_opts[i].optname,
-                                             val ? val : "");
-                ssh_args[argctr++] = sopt;
-            }
-        }
-        ssh_args[argctr++] = host;
-        if (!sftp_server) {
-            if (ssh_ver == 1)
-                sftp_server = SFTP_SERVER_PATH;
-            else
-                sftp_server = "sftp";
-        }
-        if (ssh_ver != 1 && strchr(sftp_server, '/') == NULL)
-            ssh_args[argctr++] = "-s";
-        ssh_args[argctr++] = sftp_server;
-        ssh_args[argctr++] = NULL;
-
-        execvp(ssh_cmd, ssh_args);
+        execvp(sshfs.ssh_args[0], sshfs.ssh_args);
+        perror("execvp");
         _exit(1);
     }
     waitpid(pid, NULL, 0);
@@ -690,8 +680,8 @@ static int connect_to(char *host, char *port)
     }
     freeaddrinfo(ai);
 
-    infd = sock;
-    outfd = sock;
+    sshfs.infd = sock;
+    sshfs.outfd = sock;
     return 0;
 }
 
@@ -701,7 +691,7 @@ static int do_write(struct buffer *buf)
     size_t size = buf->len;
     int res;
     while (size) {
-        res = write(outfd, p, size);
+        res = write(sshfs.outfd, p, size);
         if (res == -1) {
             perror("write");
             return -1;
@@ -741,7 +731,7 @@ static int do_read(struct buffer *buf)
     uint8_t *p = buf->p;
     size_t size = buf->size;
     while (size) {
-        res = read(infd, p, size);
+        res = read(sshfs.infd, p, size);
         if (res == -1) {
             perror("read");
             return -1;
@@ -803,9 +793,9 @@ static void chunk_put(struct read_chunk *chunk)
 
 static void chunk_put_locked(struct read_chunk *chunk)
 {
-    pthread_mutex_lock(&lock);
+    pthread_mutex_lock(&sshfs.lock);
     chunk_put(chunk);
-    pthread_mutex_unlock(&lock);
+    pthread_mutex_unlock(&sshfs.lock);
 }
 
 static int clean_req(void *key_, struct request *req)
@@ -841,14 +831,14 @@ static void *process_requests(void *data_)
         if (buf_get_uint32(&buf, &id) == -1)
             break;
 
-        pthread_mutex_lock(&lock);
-        req = (struct request *) g_hash_table_lookup(reqtab,
+        pthread_mutex_lock(&sshfs.lock);
+        req = (struct request *) g_hash_table_lookup(sshfs.reqtab,
                                                      GUINT_TO_POINTER(id));
         if (req == NULL)
             fprintf(stderr, "request %i not found\n", id);
         else
-            g_hash_table_remove(reqtab, GUINT_TO_POINTER(id));
-        pthread_mutex_unlock(&lock);
+            g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
+        pthread_mutex_unlock(&sshfs.lock);
         if (req != NULL) {
             struct timeval now;
             unsigned int difftime;
@@ -864,28 +854,28 @@ static void *process_requests(void *data_)
                 sem_post(&req->ready);
             else {
                 if (req->end_func) {
-                    pthread_mutex_lock(&lock);
+                    pthread_mutex_lock(&sshfs.lock);
                     req->end_func(req);
-                    pthread_mutex_unlock(&lock);
+                    pthread_mutex_unlock(&sshfs.lock);
                 }
                 request_free(req);
             }
         } else
             buf_free(&buf);
     }
-    if (!reconnect) {
+    if (!sshfs.reconnect) {
         /* harakiri */
         kill(getpid(), SIGTERM);
     } else {
-        pthread_mutex_lock(&lock);
-        processing_thread_started = 0;
-        close(infd);
-        infd = -1;
-        close(outfd);
-        outfd = -1;
-        g_hash_table_foreach_remove(reqtab, (GHRFunc) clean_req, NULL);
-        connver ++;
-        pthread_mutex_unlock(&lock);
+        pthread_mutex_lock(&sshfs.lock);
+        sshfs.processing_thread_started = 0;
+        close(sshfs.infd);
+        sshfs.infd = -1;
+        close(sshfs.outfd);
+        sshfs.outfd = -1;
+        g_hash_table_foreach_remove(sshfs.reqtab, (GHRFunc) clean_req, NULL);
+        sshfs.connver ++;
+        pthread_mutex_unlock(&sshfs.lock);
     }
     return NULL;
 }
@@ -910,8 +900,8 @@ static int sftp_init()
     if (buf_get_uint32(&buf, &version) == -1)
         goto out;
 
-    server_version = version;
-    DEBUG("Server version: %i\n", server_version);
+    sshfs.server_version = version;
+    DEBUG("Server version: %i\n", sshfs.server_version);
     if (version > PROTO_VERSION)
         fprintf(stderr, "Warning: server uses version: %i, we support: %i\n",
                 version, PROTO_VERSION);
@@ -963,13 +953,13 @@ static void sftp_detect_uid()
     if (!(flags & SSH_FILEXFER_ATTR_UIDGID))
         goto out;
 
-    remote_uid = stbuf.st_uid;
-    local_uid = getuid();
-    remote_uid_detected = 1;
-    DEBUG("remote_uid = %i\n", remote_uid);
+    sshfs.remote_uid = stbuf.st_uid;
+    sshfs.local_uid = getuid();
+    sshfs.remote_uid_detected = 1;
+    DEBUG("remote_uid = %i\n", sshfs.remote_uid);
 
  out:
-    if (!remote_uid_detected)
+    if (!sshfs.remote_uid_detected)
         fprintf(stderr, "failed to detect remote user ID\n");
 
     buf_free(&buf);
@@ -979,10 +969,10 @@ static int connect_remote(void)
 {
     int err;
 
-    if (sshfs_opts[SOPT_DIRECTPORT].present)
-        err = connect_to(host, sshfs_opts[SOPT_DIRECTPORT].value);
+    if (sshfs.directport)
+        err = connect_to(sshfs.host, sshfs.directport);
     else
-        err = start_ssh(host);
+        err = start_ssh();
     if (!err)
         err = sftp_init();
 
@@ -993,10 +983,10 @@ static int start_processing_thread(void)
 {
     int err;
     pthread_t thread_id;
-    if (processing_thread_started)
+    if (sshfs.processing_thread_started)
         return 0;
 
-    if (outfd == -1) {
+    if (sshfs.outfd == -1) {
         err = connect_remote();
         if (err)
             return -EIO;
@@ -1008,14 +998,14 @@ static int start_processing_thread(void)
         return -EIO;
     }
     pthread_detach(thread_id);
-    processing_thread_started = 1;
+    sshfs.processing_thread_started = 1;
     return 0;
 }
 
 #ifdef SSHFS_USE_INIT
 static void *sshfs_init(void)
 {
-    if (detect_uid)
+    if (sshfs.detect_uid)
         sftp_detect_uid();
 
     start_processing_thread();
@@ -1039,7 +1029,7 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf,
     sem_init(&req->ready, 0, 0);
     buf_init(&req->reply, 0);
     buf_init(&buf2, buf->len + 4);
-    pthread_mutex_lock(&lock);
+    pthread_mutex_lock(&sshfs.lock);
     if (begin_func)
         begin_func(req);
     id = sftp_get_id();
@@ -1047,20 +1037,20 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf,
     buf_add_mem(&buf2, buf->p, buf->len);
     err = start_processing_thread();
     if (err) {
-        pthread_mutex_unlock(&lock);
+        pthread_mutex_unlock(&sshfs.lock);
         goto out;
     }
-    g_hash_table_insert(reqtab, GUINT_TO_POINTER(id), req);
+    g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
     gettimeofday(&req->start, NULL);
     DEBUG("[%05i] %s\n", id, type_name(type));
 
     err = -EIO;
     if (sftp_send(type, &buf2) == -1) {
-        g_hash_table_remove(reqtab, GUINT_TO_POINTER(id));
-        pthread_mutex_unlock(&lock);
+        g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
+        pthread_mutex_unlock(&sshfs.lock);
         goto out;
     }
-    pthread_mutex_unlock(&lock);
+    pthread_mutex_unlock(&sshfs.lock);
 
     if (expect_type == 0) {
         buf_free(&buf2);
@@ -1111,9 +1101,9 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf,
 
  out:
     if (end_func) {
-        pthread_mutex_lock(&lock);
+        pthread_mutex_lock(&sshfs.lock);
         end_func(req);
-        pthread_mutex_unlock(&lock);
+        pthread_mutex_unlock(&sshfs.lock);
     }
     buf_free(&buf2);
     request_free(req);
@@ -1157,7 +1147,7 @@ static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
     struct buffer buf;
     struct buffer name;
 
-    if (server_version < 3)
+    if (sshfs.server_version < 3)
         return -EPERM;
 
     buf_init(&buf, 0);
@@ -1259,7 +1249,7 @@ static int sshfs_symlink(const char *from, const char *to)
     int err;
     struct buffer buf;
 
-    if (server_version < 3)
+    if (sshfs.server_version < 3)
         return -EPERM;
 
     /* openssh sftp server doesn't follow standard: link target and
@@ -1310,7 +1300,7 @@ static void random_string(char *str, int length)
 {
     int i;
     for (i = 0; i < length; i++)
-        *str++ = (char)('0' + rand_r(&randseed) % 10);
+        *str++ = (char)('0' + rand_r(&sshfs.randseed) % 10);
     *str = '\0';
 }
 
@@ -1318,7 +1308,7 @@ static int sshfs_rename(const char *from, const char *to)
 {
     int err;
     err = sshfs_do_rename(from, to);
-    if (err == -EPERM && rename_workaround) {
+    if (err == -EPERM && sshfs.rename_workaround) {
         size_t tolen = strlen(to);
         if (tolen + RENAME_TEMP_CHARS < PATH_MAX) {
             int tmperr;
@@ -1411,7 +1401,7 @@ static int sshfs_utime(const char *path, struct utimbuf *ubuf)
 
 static inline int sshfs_file_is_conn(struct sshfs_file *sf)
 {
-    return sf->connver == connver;
+    return sf->connver == sshfs.connver;
 }
 
 static int sshfs_open_common(const char *path, mode_t mode,
@@ -1445,7 +1435,7 @@ static int sshfs_open_common(const char *path, mode_t mode,
     /* Assume random read after open */
     sf->is_seq = 0;
     sf->next_pos = 0;
-    sf->connver = connver;
+    sf->connver = sshfs.connver;
     buf_init(&buf, 0);
     buf_add_path(&buf, path);
     buf_add_uint32(&buf, pflags);
@@ -1481,22 +1471,22 @@ static int sshfs_flush(const char *path, struct fuse_file_info *fi)
     if (!sshfs_file_is_conn(sf))
         return -EIO;
 
-    if (sync_write)
+    if (sshfs.sync_write)
         return 0;
 
     (void) path;
-    pthread_mutex_lock(&lock);
+    pthread_mutex_lock(&sshfs.lock);
     if (!list_empty(&sf->write_reqs)) {
         curr_list = sf->write_reqs.prev;
         list_del(&sf->write_reqs);
         list_init(&sf->write_reqs);
         list_add(&write_reqs, curr_list);
         while (!list_empty(&write_reqs))
-            pthread_cond_wait(&sf->write_finished, &lock);
+            pthread_cond_wait(&sf->write_finished, &sshfs.lock);
     }
     err = sf->write_error;
     sf->write_error = 0;
-    pthread_mutex_unlock(&lock);
+    pthread_mutex_unlock(&sshfs.lock);
     return err;
 }
 
@@ -1620,10 +1610,10 @@ static int submit_read(struct sshfs_file *sf, size_t size, off_t offset,
     chunk->refs = 1;
     err = sshfs_send_async_read(sf, chunk);
     if (!err) {
-        pthread_mutex_lock(&lock);
+        pthread_mutex_lock(&sshfs.lock);
         chunk_put(*chunkp);
         *chunkp = chunk;
-        pthread_mutex_unlock(&lock);
+        pthread_mutex_unlock(&sshfs.lock);
     } else
         chunk_put(chunk);
 
@@ -1668,12 +1658,12 @@ static int sshfs_async_read(struct sshfs_file *sf, char *rbuf, size_t size,
     size_t origsize = size;
     int curr_is_seq;
 
-    pthread_mutex_lock(&lock);
+    pthread_mutex_lock(&sshfs.lock);
     curr_is_seq = sf->is_seq;
     sf->is_seq = (sf->next_pos == offset);
     sf->next_pos = offset + size;
     chunk = search_read_chunk(sf, offset);
-    pthread_mutex_unlock(&lock);
+    pthread_mutex_unlock(&sshfs.lock);
 
     if (chunk && chunk->size < size) {
         chunk_prev = chunk;
@@ -1716,7 +1706,7 @@ static int sshfs_read(const char *path, char *rbuf, size_t size, off_t offset,
     if (!sshfs_file_is_conn(sf))
         return -EIO;
 
-    if (sync_read)
+    if (sshfs.sync_read)
         return sshfs_sync_read(sf, rbuf, size, offset);
     else
         return sshfs_async_read(sf, rbuf, size, offset);
@@ -1765,7 +1755,7 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
     buf_add_buf(&buf, handle);
     buf_add_uint64(&buf, offset);
     buf_add_data(&buf, &data);
-    if (!sync_write && !sf->write_error)
+    if (!sshfs.sync_write && !sf->write_error)
         err = sftp_request_async(SSH_FXP_WRITE, &buf, sshfs_write_begin,
                                  sshfs_write_end, sf);
     else
@@ -1780,7 +1770,7 @@ static int sshfs_statfs(const char *path, struct statvfs *buf)
     (void) path;
 
     buf->f_namemax = 255;
-    buf->f_bsize = blksize;
+    buf->f_bsize = sshfs.blksize;
     buf->f_frsize = 512;
     buf->f_blocks = 999999999 * 2;
     buf->f_bfree =  999999999 * 2;
@@ -1862,9 +1852,9 @@ static int sshfs_fgetattr(const char *path, struct stat *stbuf,
 
 static int processing_init(void)
 {
-    pthread_mutex_init(&lock, NULL);
-    reqtab = g_hash_table_new(NULL, NULL);
-    if (!reqtab) {
+    pthread_mutex_init(&sshfs.lock, NULL);
+    sshfs.reqtab = g_hash_table_new(NULL, NULL);
+    if (!sshfs.reqtab) {
         fprintf(stderr, "failed to create hash table\n");
         return -1;
     }
@@ -1940,152 +1930,126 @@ static void usage(const char *progname)
     exit(1);
 }
 
+static int is_ssh_opt(const char *arg)
+{
+    if (arg[0] != '-') {
+        unsigned arglen = strlen(arg);
+        const char **o;
+        for (o = ssh_opts; *o; o++) {
+            unsigned olen = strlen(*o);
+            if (arglen > olen && arg[olen] == '=' &&
+                strncasecmp(arg, *o, olen) == 0)
+                return 1;
+        }
+    }
+    return 0;
+}
+
+static int sshfs_opt_proc(void *data, const char *arg, int key)
+{
+    char *tmp;
+    (void) data;
+
+    switch (key) {
+    case FUSE_OPT_KEY_OPT:
+        if (is_ssh_opt(arg)) {
+            tmp = g_strdup_printf("-o%s", arg);
+            ssh_add_arg(tmp);
+            g_free(tmp);
+            return 0;
+        }
+        return 1;
+
+    case FUSE_OPT_KEY_NONOPT:
+        if (!sshfs.host && strchr(arg, ':')) {
+            sshfs.host = strdup(arg);
+            return 0;
+        }
+        return 1;
+
+    case KEY_PORT:
+        tmp = g_strdup_printf("-oPort=%s", arg + 2);
+        ssh_add_arg(tmp);
+        g_free(tmp);
+        return 0;
+
+    case KEY_COMPRESS:
+        ssh_add_arg("-oCompression=yes");
+        return 0;
+
+    case KEY_HELP:
+        usage(sshfs.progname);
+
+    case KEY_VERSION:
+        fprintf(stderr, "SSHFS version %s\n", PACKAGE_VERSION);
+        exit(0);
+
+    default:
+        fprintf(stderr, "internal error\n");
+        abort();
+    }
+}
+
 int main(int argc, char *argv[])
 {
-    char *fsname;
     int res;
-    int argctr;
-    unsigned max_read = 65536;
-    int newargc = 0;
-    char **newargv = (char **) malloc((argc + 10) * sizeof(char *));
-    newargv[newargc++] = argv[0];
-
-    for (argctr = 1; argctr < argc; argctr++) {
-        char *arg = argv[argctr];
-        if (arg[0] == '-') {
-            switch (arg[1]) {
-            case '-':
-                if (strcmp(arg+2, "help") == 0)
-                    goto help;
-                else if (strcmp(arg+2, "version") == 0)
-                    goto version;
-                break;
-
-            case 'V':
-            version:
-                fprintf(stderr, "SSHFS version %s\n", PACKAGE_VERSION);
-                exit(0);
-                break;
-
-            case 'h':
-            help:
-                usage(argv[0]);
-                break;
-
-            case 'C':
-                if (!arg[2])
-                    arg = "-oCompression=yes";
-                break;
-
-            case '1':
-                if (!arg[2])
-                    arg = "-ossh_protocol=1";
-                break;
-
-            case 'p':
-                if (arg[2] || argctr + 1 < argc) {
-                    char *newarg;
-                    if (arg[2])
-                        arg = arg + 2;
-                    else
-                        arg = argv[++argctr];
-                    newarg = malloc(strlen(arg)+32);
-                    sprintf(newarg, "-oPort=%s", arg);
-                    arg = newarg;
-                }
-                break;
-            }
-            newargv[newargc++] = g_strdup(arg);
-        } else if (!host && strchr(arg, ':'))
-            host = g_strdup(arg);
-        else
-            newargv[newargc++] = g_strdup(arg);
-    }
-    if (!host) {
+    int argcout;
+    char **argvout;
+    char *tmp;
+    char *fsname;
+    char *base_path;
+    const char *sftp_server;
+
+    sshfs.progname = argv[0];
+    sshfs.blksize = 4096;
+    sshfs.max_read = 65536;
+    sshfs.ssh_ver = 2;
+    ssh_add_arg("ssh");
+    ssh_add_arg("-x");
+    ssh_add_arg("-a");
+    ssh_add_arg("-oClearAllForwardings=yes");
+
+    if (fuse_opt_parse(argc, argv, &sshfs, sshfs_opts, sshfs_opt_proc,
+                       &argcout, &argvout) == -1)
+        exit(1);
+
+    if (!sshfs.host) {
         fprintf(stderr, "missing host\n");
         fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
         exit(1);
     }
 
-    fsname = g_strdup(host);
-    base_path = strchr(host, ':');
+    fsname = g_strdup(sshfs.host);
+    base_path = strchr(sshfs.host, ':');
     *base_path++ = '\0';
     if (base_path[0] && base_path[strlen(base_path)-1] != '/')
-        base_path = g_strdup_printf("%s/", base_path);
+        sshfs.base_path = g_strdup_printf("%s/", base_path);
     else
-        base_path = g_strdup(base_path);
-
-    process_options(&newargc, newargv, sshfs_opts, 1);
-    if (sshfs_opts[SOPT_SYNC_WRITE].present)
-        sync_write = 1;
-    if (sshfs_opts[SOPT_SYNC_READ].present)
-        sync_read = 1;
-    if (sshfs_opts[SOPT_DEBUG].present)
-        debug = 1;
-    if (sshfs_opts[SOPT_WORKAROUND].present) {
-        struct opt *o = &sshfs_opts[SOPT_WORKAROUND];
-        char *s = opt_get_string(o);
-        if (!s)
-            exit(1);
+        sshfs.base_path = g_strdup(base_path);
 
-        for (s = strtok(s, ":"); s; s = strtok(NULL, ":")) {
-            if (strcmp(s, "none") == 0)
-                ;
-            else if (strcmp(s, "all") == 0)
-                rename_workaround = 1;
-            else if (strcmp(s, "rename") == 0)
-                rename_workaround = 1;
-            else {
-                fprintf(stderr, "Invalid value for '%s' option\n", o->optname);
-                exit(1);
-            }
-        }
+    if (sshfs.ssh_command) {
+        free(sshfs.ssh_args[0]);
+        sshfs.ssh_args[0] = sshfs.ssh_command;
     }
-    if (sshfs_opts[SOPT_IDMAP].present) {
-        struct opt *o = &sshfs_opts[SOPT_IDMAP];
-        char *idmap = opt_get_string(o);
-        if (!idmap)
-            exit(1);
 
-        if (strcmp(idmap, "none") == 0)
-            detect_uid = 0;
-        else if (strcmp(idmap, "user") == 0)
-            detect_uid = 1;
-        else {
-            fprintf(stderr, "Invalid value for '%s' option\n", o->optname);
-            exit(1);
-        }
-    }
-    if (sshfs_opts[SOPT_RECONNECT].present)
-        reconnect = 1;
-    if (sshfs_opts[SOPT_MAX_READ].present) {
-        unsigned val;
-        if (opt_get_unsigned(&sshfs_opts[SOPT_MAX_READ], &val) == -1)
-            exit(1);
+    tmp = g_strdup_printf("-%i", sshfs.ssh_ver);
+    ssh_add_arg(tmp);
+    g_free(tmp);
+    ssh_add_arg(sshfs.host);
+    if (sshfs.sftp_server)
+        sftp_server = sshfs.sftp_server;
+    else if (sshfs.ssh_ver == 1)
+        sftp_server = SFTP_SERVER_PATH;
+    else
+        sftp_server = "sftp";
 
-        if (val < max_read)
-            max_read = val;
-    }
-    if (sshfs_opts[SOPT_PROTOCOL].present) {
-        if (opt_get_unsigned(&sshfs_opts[SOPT_PROTOCOL], &ssh_ver) == -1)
-            exit(1);
-    }
-    if (sshfs_opts[SOPT_SFTPSERVER].present) {
-        sftp_server = opt_get_string(&sshfs_opts[SOPT_SFTPSERVER]);
-        if (!sftp_server)
-            exit(1);
-    }
-    if (sshfs_opts[SOPT_DIRECTPORT].present)
-        res = connect_to(host, sshfs_opts[SOPT_DIRECTPORT].value);
-    else {
-        process_options(&newargc, newargv, ssh_opts, 0);
-        res = start_ssh(host);
-    }
-    if (res == -1)
-        exit(1);
+    if (sshfs.ssh_ver != 1 && strchr(sftp_server, '/') == NULL)
+        ssh_add_arg("-s");
 
-    res = sftp_init();
-    if (res == -1)
+    ssh_add_arg(sftp_server);
+    free(sshfs.sftp_server);
+
+    if (connect_remote() == -1)
         exit(1);
 
 #ifndef SSHFS_USE_INIT
@@ -2097,15 +2061,26 @@ int main(int argc, char *argv[])
     if (res == -1)
         exit(1);
 
-    res = cache_parse_options(&newargc, newargv);
+    res = cache_parse_options(&argcout, &argvout);
     if (res == -1)
         exit(1);
 
-    randseed = time(0);
+    sshfs.randseed = time(0);
+
+    if (sshfs.max_read > 65536)
+        sshfs.max_read = 65536;
 
-    newargv[newargc++] = g_strdup_printf("-omax_read=%u", max_read);
-    newargv[newargc++] = g_strdup_printf("-ofsname=sshfs#%s", fsname);
+    tmp = g_strdup_printf("-omax_read=%u", sshfs.max_read);
+    fuse_opt_add_arg(&argcout, &argvout, tmp);
+    g_free(tmp);
+    tmp = g_strdup_printf("-ofsname=sshfs#%s", fsname);
+    fuse_opt_add_arg(&argcout, &argvout, tmp);
+    g_free(tmp);
     g_free(fsname);
-    newargv[newargc] = NULL;
-    return fuse_main(newargc, newargv, cache_init(&sshfs_oper));
+    res = fuse_main(argcout, argvout, cache_init(&sshfs_oper));
+    fuse_opt_free_args(argvout);
+    fuse_opt_free_args(sshfs.ssh_args);
+    free(sshfs.directport);
+
+    return res;
 }