Hello community,

here is the log from the commit of package sshfs for openSUSE:Factory checked 
in at 2012-04-12 09:52:14
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/sshfs (Old)
 and      /work/SRC/openSUSE:Factory/.sshfs.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "sshfs", Maintainer is "[email protected]"

Changes:
--------
--- /work/SRC/openSUSE:Factory/sshfs/sshfs.changes      2011-12-02 
16:26:08.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.sshfs.new/sshfs.changes 2012-04-12 
09:52:16.000000000 +0200
@@ -1,0 +2,27 @@
+Fri Mar 16 01:35:31 UTC 2012 - [email protected]
+
+- update to 2.4
+  * Add "-oworkaround=fstat" for SFTP servers which don't support
+    the FSTAT message.  Patch by: Percy Jahn
+  * Remove "-oPreferredAuthentications" from ssh options if the
+    "password_stdin" option is used.  Reported by E. Kuemmerle
+  * Fix double free if reconnection races with request sending.
+    Patch by E. Kuemmerle
+  * Submit max 32k reads and writes to the sftp server.  Also don't
+    limit the kernel to 64k reads and writes, rather split into 32k
+    sized chunks and send them to the server all at once.  This is
+    more efficient and less demanding from the server.  Reported by
+    Ludovic Courtès.  Fix suggested by Niels Möller
+  * Make chown respect the UID mapping policy.  Reported and tested
+    by Vivenzio Pagliari
+  * Add -o idmap=file, -o uidmap=FILE, -o gidmap=FILE. These options
+    allow you to create a pair of local files, similar to /etc/passwd or
+    /etc/group files from the remote server, and use those to remap all
+    the given UIDs/GIDs.
+  * Add -o slave. This option routes the sftp communication over stdin
+    and stdout, bypassing SSH and network.
+  * Make sure idmap files aren't writable by others otherwise, other
+    local users could change the mapping, and gain access to things
+    they shouldn't.  Patch by Mike Kelly
+
+-------------------------------------------------------------------

Old:
----
  sshfs-fuse-2.3.tar.gz

New:
----
  sshfs-fuse-2.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ sshfs.spec ++++++
--- /var/tmp/diff_new_pack.eneLRs/_old  2012-04-12 09:52:19.000000000 +0200
+++ /var/tmp/diff_new_pack.eneLRs/_new  2012-04-12 09:52:19.000000000 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package sshfs
 #
-# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -15,13 +15,15 @@
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
+
+
 Name:           sshfs
 BuildRequires:  automake
 BuildRequires:  fuse-devel
 BuildRequires:  glib2-devel
 Requires:       fuse
 Summary:        Filesystem client based on SSH file transfer protocol
-Version:        2.3
+Version:        2.4
 Release:        0
 License:        GPL-2.0+
 Group:          System/Filesystems

++++++ sshfs-fuse-2.3.tar.gz -> sshfs-fuse-2.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/ChangeLog new/sshfs-fuse-2.4/ChangeLog
--- old/sshfs-fuse-2.3/ChangeLog        2011-07-01 14:16:32.000000000 +0200
+++ new/sshfs-fuse-2.4/ChangeLog        2012-03-08 10:35:31.000000000 +0100
@@ -1,3 +1,55 @@
+2012-03-08  Miklos Szeredi <[email protected]>
+
+       * Released 2.4
+
+2012-03-08  Miklos Szeredi <[email protected]>
+
+       * Make sure idmap files aren't writable by others otherwise, other
+       local users could change the mapping, and gain access to things
+       they shouldn't.  Patch by Mike Kelly
+
+2012-02-08  Chris Wolfe <[email protected]>
+
+       * Add -o slave. This option routes the sftp communication over stdin
+       and stdout, bypassing SSH and network.
+
+2011-12-16  Mike Kelly <[email protected]>
+
+       * Add -o idmap=file, -o uidmap=FILE, -o gidmap=FILE. These options
+       allow you to create a pair of local files, similar to /etc/passwd or
+       /etc/group files from the remote server, and use those to remap all
+       the given UIDs/GIDs.
+
+2011-11-25  Miklos Szeredi <[email protected]>
+
+       * Make chown respect the UID mapping policy.  Reported and tested
+       by Vivenzio Pagliari
+
+2011-11-16  Miklos Szeredi <[email protected]>
+
+       * Submit max 32k reads and writes to the sftp server.  Also don't
+       limit the kernel to 64k reads and writes, rather split into 32k
+       sized chunks and send them to the server all at once.  This is
+       more efficient and less demanding from the server.  Reported by
+       Ludovic Courtès.  Fix suggested by Niels Möller
+
+2011-11-14  Miklos Szeredi <[email protected]>
+
+       * Fix double free if reconnection races with request sending.
+       Patch by E. Kuemmerle
+
+       * Add locking around modifver and connver
+
+2011-10-21  Miklos Szeredi <[email protected]>
+
+       * Remove "-oPreferredAuthentications" from ssh options if the
+       "password_stdin" option is used.  Reported by E. Kuemmerle
+
+2011-08-24  Miklos Szeredi <[email protected]>
+
+       * Add "-oworkaround=fstat" for SFTP servers which don't support
+       the FSTAT message.  Patch by: Percy Jahn
+
 2011-07-01  Miklos Szeredi <[email protected]>
 
        * Released 2.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/cache.c new/sshfs-fuse-2.4/cache.c
--- old/sshfs-fuse-2.3/cache.c  2010-06-24 11:32:56.000000000 +0200
+++ new/sshfs-fuse-2.4/cache.c  2012-02-08 10:21:00.000000000 +0100
@@ -164,7 +164,6 @@
 void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
 {
        struct node *node;
-       time_t now;
 
        if (!cache.on)
                return;
@@ -172,7 +171,6 @@
        pthread_mutex_lock(&cache.lock);
        if (wrctr == cache.write_ctr) {
                node = cache_get(path);
-               now = time(NULL);
                node->stat = *stbuf;
                node->stat_valid = time(NULL) + cache.stat_timeout;
                if (node->stat_valid > node->valid)
@@ -185,11 +183,9 @@
 static void cache_add_dir(const char *path, char **dir)
 {
        struct node *node;
-       time_t now;
 
        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;
@@ -209,11 +205,9 @@
 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);
        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;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/configure new/sshfs-fuse-2.4/configure
--- old/sshfs-fuse-2.3/configure        2011-07-01 14:28:28.000000000 +0200
+++ new/sshfs-fuse-2.4/configure        2012-03-08 10:29:15.000000000 +0100
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for sshfs-fuse 2.3.
+# Generated by GNU Autoconf 2.68 for sshfs-fuse 2.4.
 #
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -556,8 +556,8 @@
 # Identity of this package.
 PACKAGE_NAME='sshfs-fuse'
 PACKAGE_TARNAME='sshfs-fuse'
-PACKAGE_VERSION='2.3'
-PACKAGE_STRING='sshfs-fuse 2.3'
+PACKAGE_VERSION='2.4'
+PACKAGE_STRING='sshfs-fuse 2.4'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1213,7 +1213,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures sshfs-fuse 2.3 to adapt to many kinds of systems.
+\`configure' configures sshfs-fuse 2.4 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1279,7 +1279,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of sshfs-fuse 2.3:";;
+     short | recursive ) echo "Configuration of sshfs-fuse 2.4:";;
    esac
   cat <<\_ACEOF
 
@@ -1374,7 +1374,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-sshfs-fuse configure 2.3
+sshfs-fuse configure 2.4
 generated by GNU Autoconf 2.68
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1542,7 +1542,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by sshfs-fuse $as_me 2.3, which was
+It was created by sshfs-fuse $as_me 2.4, which was
 generated by GNU Autoconf 2.68.  Invocation command line was
 
   $ $0 $@
@@ -2357,7 +2357,7 @@
 
 # Define the identity of the package.
  PACKAGE='sshfs-fuse'
- VERSION='2.3'
+ VERSION='2.4'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -4383,7 +4383,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by sshfs-fuse $as_me 2.3, which was
+This file was extended by sshfs-fuse $as_me 2.4, which was
 generated by GNU Autoconf 2.68.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -4449,7 +4449,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-sshfs-fuse config.status 2.3
+sshfs-fuse config.status 2.4
 configured by $0, generated by GNU Autoconf 2.68,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/configure.ac 
new/sshfs-fuse-2.4/configure.ac
--- old/sshfs-fuse-2.3/configure.ac     2011-07-01 14:12:32.000000000 +0200
+++ new/sshfs-fuse-2.4/configure.ac     2012-03-08 10:29:03.000000000 +0100
@@ -1,4 +1,4 @@
-AC_INIT(sshfs-fuse, 2.3)
+AC_INIT(sshfs-fuse, 2.4)
 AM_INIT_AUTOMAKE
 AM_CONFIG_HEADER(config.h)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/sshfs.1 new/sshfs-fuse-2.4/sshfs.1
--- old/sshfs-fuse-2.3/sshfs.1  2010-06-24 11:32:56.000000000 +0200
+++ new/sshfs-fuse-2.4/sshfs.1  2012-02-14 12:55:14.000000000 +0100
@@ -102,6 +102,29 @@
 .TP
 user
 only translate UID of connecting user
+.TP
+file
+translate UIDs/GIDs based upon the contents of \fBuidfile \fR and
+\fBgidfile\fR
+.RE
+.TP
+\fB\-o\fR uidfile=FILE
+file containing username:uid mappings for \fBidmap=file\fR
+.RE
+.TP
+\fB\-o\fR gidfile=FILE
+file containing groupname:gid mappings for \fBidmap=file\fR
+.RE
+.TP
+\fB\-o\fR nomap=TYPE
+with idmap=file, how to handle missing mappings
+.RS 8
+.TP
+ignore
+don't do any re-mapping
+.TP
+error
+return an error (default)
 .RE
 .TP
 \fB\-o\fR ssh_command=CMD
@@ -115,6 +138,8 @@
 .TP
 \fB\-o\fR directport=PORT
 directly connect to PORT bypassing ssh
+\fB\-o\fR slave
+communicate over stdin and stdout bypassing network
 .TP
 \fB\-o\fR transform_symlinks
 transform absolute symlinks to relative
@@ -150,7 +175,7 @@
 \fB\-o\fR nonempty
 allow mounts over non\-empty file/dir
 .HP
-\fB\-o\fR default_permissions 
+\fB\-o\fR default_permissions
 enable permission checking by kernel
 .TP
 \fB\-o\fR fsname=NAME
@@ -225,7 +250,7 @@
 \fB\-o\fR sync_read
 perform reads synchronously
 .SS "Module options:"
-.TP 
+.TP
 [subdir]
 .TP
 \fB\-o\fR subdir=DIR
@@ -233,7 +258,7 @@
 .TP
 \fB\-o\fR [no]rellinksa
 transform absolute symlinks to relative
-.TP 
+.TP
 [iconv]
 .TP
 \fB\-o\fR from_code=CHARSET
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshfs-fuse-2.3/sshfs.c new/sshfs-fuse-2.4/sshfs.c
--- old/sshfs-fuse-2.3/sshfs.c  2011-07-01 14:11:13.000000000 +0200
+++ new/sshfs-fuse-2.4/sshfs.c  2012-03-08 10:34:39.000000000 +0100
@@ -35,6 +35,9 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <glib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <limits.h>
 
 #include "cache.h"
 
@@ -146,14 +149,27 @@
        struct list_head list;
 };
 
+struct sshfs_io {
+       int num_reqs;
+       pthread_cond_t finished;
+       int error;
+};
+
+struct read_req {
+       struct sshfs_io *sio;
+       struct list_head list;
+       struct buffer data;
+       size_t size;
+       ssize_t res;
+};
+
 struct read_chunk {
-       sem_t ready;
        off_t offset;
        size_t size;
-       struct buffer data;
        int refs;
-       int res;
        long modifver;
+       struct list_head reqs;
+       struct sshfs_io sio;
 };
 
 struct sshfs_file {
@@ -180,10 +196,19 @@
        int nodelaysrv_workaround;
        int truncate_workaround;
        int buflimit_workaround;
+       int fstat_workaround;
        int transform_symlinks;
        int follow_symlinks;
        int no_check_root;
        int detect_uid;
+       int idmap;
+       int nomap;
+       char *uid_file;
+       char *gid_file;
+       GHashTable *uid_map;
+       GHashTable *gid_map;
+       GHashTable *r_uid_map;
+       GHashTable *r_gid_map;
        unsigned max_read;
        unsigned max_write;
        unsigned ssh_ver;
@@ -193,6 +218,7 @@
        int foreground;
        int reconnect;
        int delay_connect;
+       int slave;
        char *host;
        char *base_path;
        GHashTable *reqtab;
@@ -200,7 +226,8 @@
        pthread_mutex_t lock_write;
        int processing_thread_started;
        unsigned int randseed;
-       int fd;
+       int rfd;
+       int wfd;
        int ptyfd;
        int ptyslavefd;
        int connver;
@@ -292,6 +319,17 @@
        KEY_CONFIGFILE,
 };
 
+enum {
+       IDMAP_NONE,
+       IDMAP_USER,
+       IDMAP_FILE,
+};
+
+enum {
+       NOMAP_IGNORE,
+       NOMAP_ERROR,
+};
+
 #define SSHFS_OPT(t, p, v) { t, offsetof(struct sshfs, p), v }
 
 static struct fuse_opt sshfs_opts[] = {
@@ -303,8 +341,13 @@
        SSHFS_OPT("ssh_protocol=%u",   ssh_ver, 0),
        SSHFS_OPT("-1",                ssh_ver, 1),
        SSHFS_OPT("workaround=%s",     workarounds, 0),
-       SSHFS_OPT("idmap=none",        detect_uid, 0),
-       SSHFS_OPT("idmap=user",        detect_uid, 1),
+       SSHFS_OPT("idmap=none",        idmap, IDMAP_NONE),
+       SSHFS_OPT("idmap=user",        idmap, IDMAP_USER),
+       SSHFS_OPT("idmap=file",        idmap, IDMAP_FILE),
+       SSHFS_OPT("uidfile=%s",        uid_file, 0),
+       SSHFS_OPT("gidfile=%s",        gid_file, 0),
+       SSHFS_OPT("nomap=ignore",      nomap, NOMAP_IGNORE),
+       SSHFS_OPT("nomap=error",       nomap, NOMAP_ERROR),
        SSHFS_OPT("sshfs_sync",        sync_write, 1),
        SSHFS_OPT("no_readahead",      sync_read, 1),
        SSHFS_OPT("sshfs_debug",       debug, 1),
@@ -314,6 +357,7 @@
        SSHFS_OPT("no_check_root",     no_check_root, 1),
        SSHFS_OPT("password_stdin",    password_stdin, 1),
        SSHFS_OPT("delay_connect",     delay_connect, 1),
+       SSHFS_OPT("slave",             slave, 1),
 
        FUSE_OPT_KEY("-p ",            KEY_PORT),
        FUSE_OPT_KEY("-C",             KEY_COMPRESS),
@@ -334,11 +378,13 @@
        SSHFS_OPT("none",       nodelaysrv_workaround, 0),
        SSHFS_OPT("none",       truncate_workaround, 0),
        SSHFS_OPT("none",       buflimit_workaround, 0),
+       SSHFS_OPT("none",       fstat_workaround, 0),
        SSHFS_OPT("all",        rename_workaround, 1),
        SSHFS_OPT("all",        nodelay_workaround, 1),
        SSHFS_OPT("all",        nodelaysrv_workaround, 1),
        SSHFS_OPT("all",        truncate_workaround, 1),
        SSHFS_OPT("all",        buflimit_workaround, 1),
+       SSHFS_OPT("all",        fstat_workaround, 1),
        SSHFS_OPT("rename",     rename_workaround, 1),
        SSHFS_OPT("norename",   rename_workaround, 0),
        SSHFS_OPT("nodelay",    nodelay_workaround, 1),
@@ -349,6 +395,8 @@
        SSHFS_OPT("notruncate", truncate_workaround, 0),
        SSHFS_OPT("buflimit",   buflimit_workaround, 1),
        SSHFS_OPT("nobuflimit", buflimit_workaround, 0),
+       SSHFS_OPT("fstat",      fstat_workaround, 1),
+       SSHFS_OPT("nofstat",    fstat_workaround, 0),
        FUSE_OPT_END
 };
 
@@ -426,6 +474,24 @@
        return head->next == head;
 }
 
+/* given a pointer to the uid/gid, and the mapping table, remap the
+ * uid/gid, if necessary */
+static inline int translate_id(uint32_t *id, GHashTable *map)
+{
+       gpointer id_p;
+       if (g_hash_table_lookup_extended(map, GUINT_TO_POINTER(*id), NULL, 
&id_p)) {
+               *id = GPOINTER_TO_UINT(id_p);
+               return 0;
+       }
+       switch (sshfs.nomap) {
+       case NOMAP_ERROR: return -1;
+       case NOMAP_IGNORE: return 0;
+       default:
+               fprintf(stderr, "internal error\n");
+               abort();
+       }
+}
+
 static inline void buf_init(struct buffer *buf, size_t size)
 {
        if (size) {
@@ -627,42 +693,48 @@
        uint32_t mode = S_IFREG | 0777;
 
        if (buf_get_uint32(buf, &flags) == -1)
-               return -1;
+               return -EIO;
        if (flagsp)
                *flagsp = flags;
        if ((flags & SSH_FILEXFER_ATTR_SIZE) &&
            buf_get_uint64(buf, &size) == -1)
-               return -1;
+               return -EIO;
        if ((flags & SSH_FILEXFER_ATTR_UIDGID) &&
            (buf_get_uint32(buf, &uid) == -1 ||
             buf_get_uint32(buf, &gid) == -1))
-               return -1;
+               return -EIO;
        if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
            buf_get_uint32(buf, &mode) == -1)
-               return -1;
+               return -EIO;
        if ((flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
                if (buf_get_uint32(buf, &atime) == -1 ||
                    buf_get_uint32(buf, &mtime) == -1)
-                       return -1;
+                       return -EIO;
        }
        if ((flags & SSH_FILEXFER_ATTR_EXTENDED)) {
                uint32_t extcount;
                unsigned i;
                if (buf_get_uint32(buf, &extcount) == -1)
-                       return -1;
+                       return -EIO;
                for (i = 0; i < extcount; i++) {
                        struct buffer tmp;
                        if (buf_get_data(buf, &tmp) == -1)
-                               return -1;
+                               return -EIO;
                        buf_free(&tmp);
                        if (buf_get_data(buf, &tmp) == -1)
-                               return -1;
+                               return -EIO;
                        buf_free(&tmp);
                }
        }
 
        if (sshfs.remote_uid_detected && uid == sshfs.remote_uid)
                uid = sshfs.local_uid;
+       if (sshfs.idmap == IDMAP_FILE && sshfs.uid_map)
+               if (translate_id(&uid, sshfs.uid_map) == -1)
+                       return -EPERM;
+       if (sshfs.idmap == IDMAP_FILE && sshfs.gid_map)
+               if (translate_id(&gid, sshfs.gid_map) == -1)
+                       return -EPERM;
 
        memset(stbuf, 0, sizeof(struct stat));
        stbuf->st_mode = mode;
@@ -729,7 +801,7 @@
        unsigned i;
 
        if (buf_get_uint32(buf, &count) == -1)
-               return -1;
+               return -EIO;
 
        for (i = 0; i < count; i++) {
                int err = -1;
@@ -737,16 +809,16 @@
                char *longname;
                struct stat stbuf;
                if (buf_get_string(buf, &name) == -1)
-                       return -1;
+                       return -EIO;
                if (buf_get_string(buf, &longname) != -1) {
                        free(longname);
-                       if (buf_get_attrs(buf, &stbuf, NULL) != -1) {
+                       err = buf_get_attrs(buf, &stbuf, NULL);
+                       if (!err) {
                                if (sshfs.follow_symlinks &&
                                    S_ISLNK(stbuf.st_mode)) {
                                        stbuf.st_mode = 0;
                                }
                                filler(h, name, &stbuf);
-                               err = 0;
                        }
                }
                free(name);
@@ -822,7 +894,7 @@
        while (1) {
                struct pollfd fds[2];
 
-               fds[0].fd = sshfs.fd;
+               fds[0].fd = sshfs.rfd;
                fds[0].events = POLLIN;
                fds[1].fd = sshfs.ptyfd;
                fds[1].events = POLLIN;
@@ -930,7 +1002,8 @@
                perror("failed to create socket pair");
                return -1;
        }
-       sshfs.fd = sockpair[0];
+       sshfs.rfd = sockpair[0];
+       sshfs.wfd = sockpair[0];
 
        pid = fork();
        if (pid == -1) {
@@ -1021,6 +1094,13 @@
        return 0;
 }
 
+static int connect_slave()
+{
+       sshfs.rfd = STDIN_FILENO;
+       sshfs.wfd = STDOUT_FILENO;
+       return 0;
+}
+
 static int connect_to(char *host, char *port)
 {
        int err;
@@ -1055,7 +1135,8 @@
 
        freeaddrinfo(ai);
 
-       sshfs.fd = sock;
+       sshfs.rfd = sock;
+       sshfs.wfd = sock;
        return 0;
 }
 
@@ -1063,7 +1144,7 @@
 {
        int res;
        while (count) {
-               res = writev(sshfs.fd, iov, count);
+               res = writev(sshfs.wfd, iov, count);
                if (res == -1) {
                        perror("write");
                        return -1;
@@ -1140,7 +1221,7 @@
        uint8_t *p = buf->p;
        size_t size = buf->size;
        while (size) {
-               res = read(sshfs.fd, p, size);
+               res = read(sshfs.rfd, p, size);
                if (res == -1) {
                        perror("read");
                        return -1;
@@ -1186,8 +1267,14 @@
 
 static void chunk_free(struct read_chunk *chunk)
 {
-       buf_free(&chunk->data);
-       sem_destroy(&chunk->ready);
+       while (!list_empty(&chunk->reqs)) {
+               struct read_req *rreq;
+
+               rreq = list_entry(chunk->reqs.prev, struct read_req, list);
+               list_del(&rreq->list);
+               buf_free(&rreq->data);
+               g_free(rreq);
+       }
        g_free(chunk);
 }
 
@@ -1295,8 +1382,11 @@
 
 static void close_conn(void)
 {
-       close(sshfs.fd);
-       sshfs.fd = -1;
+       close(sshfs.rfd);
+       if (sshfs.rfd != sshfs.wfd)
+               close(sshfs.wfd);
+       sshfs.rfd = -1;
+       sshfs.wfd = -1;
        if (sshfs.ptyfd != -1) {
                close(sshfs.ptyfd);
                sshfs.ptyfd = -1;
@@ -1358,16 +1448,20 @@
                struct buffer buf2;
 
                buf_init(&buf2, len - 5);
-               if (do_read(&buf2) == -1)
+               if (do_read(&buf2) == -1) {
+                       buf_free(&buf2);
                        return -1;
+               }
 
                do {
                        char *ext;
                        char *extdata;
 
                        if (buf_get_string(&buf2, &ext) == -1 ||
-                           buf_get_string(&buf2, &extdata) == -1)
+                           buf_get_string(&buf2, &extdata) == -1) {
+                               buf_free(&buf2);
                                return -1;
+                       }
 
                        DEBUG("Extension: %s <%s>\n", ext, extdata);
 
@@ -1383,6 +1477,7 @@
                            strcmp(extdata, "1") == 0)
                                sshfs.ext_hardlink = 1;
                } while (buf2.len < buf2.size);
+               buf_free(&buf2);
        }
        return 0;
 }
@@ -1492,7 +1587,7 @@
                fprintf(stderr, "failed to stat home directory (%i)\n", serr);
                goto out;
        }
-       if (buf_get_attrs(&buf, &stbuf, &flags) == -1)
+       if (buf_get_attrs(&buf, &stbuf, &flags) != 0)
                goto out;
 
        if (!(flags & SSH_FILEXFER_ATTR_UIDGID))
@@ -1550,8 +1645,12 @@
 
                goto out;
        }
-       if (buf_get_attrs(&buf, &stbuf, &flags) == -1)
+
+       int err2 = buf_get_attrs(&buf, &stbuf, &flags);
+       if (err2) {
+               err = err2;
                goto out;
+       }
 
        if (!(flags & SSH_FILEXFER_ATTR_PERMISSIONS))
                goto out;
@@ -1578,7 +1677,9 @@
 {
        int err;
 
-       if (sshfs.directport)
+       if (sshfs.slave)
+               err = connect_slave();
+       else if (sshfs.directport)
                err = connect_to(sshfs.host, sshfs.directport);
        else
                err = start_ssh();
@@ -1603,7 +1704,7 @@
        if (sshfs.processing_thread_started)
                return 0;
 
-       if (sshfs.fd == -1) {
+       if (sshfs.rfd == -1) {
                err = connect_remote();
                if (err)
                        return -EIO;
@@ -1747,9 +1848,16 @@
 
        err = -EIO;
        if (sftp_send_iov(type, id, iov, count) == -1) {
+               gboolean rmed;
+
                pthread_mutex_lock(&sshfs.lock);
-               g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
+               rmed = g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
                pthread_mutex_unlock(&sshfs.lock);
+
+               if (!rmed && !want_reply) {
+                       /* request already freed */
+                       return err;
+               }
                goto out;
        }
        if (want_reply)
@@ -1770,12 +1878,13 @@
 static int sftp_request_iov(uint8_t type, struct iovec *iov, size_t count,
                             uint8_t expect_type, struct buffer *outbuf)
 {
+       int err;
        struct request *req;
 
-       sftp_request_send(type, iov, count, NULL, NULL, expect_type, NULL,
-                         &req);
+       err = sftp_request_send(type, iov, count, NULL, NULL, expect_type, NULL,
+                               &req);
        if (expect_type == 0)
-               return 0;
+               return err;
 
        return sftp_request_wait(req, type, expect_type, outbuf);
 }
@@ -1799,8 +1908,7 @@
        err = sftp_request(sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT,
                           &buf, SSH_FXP_ATTRS, &outbuf);
        if (!err) {
-               if (buf_get_attrs(&outbuf, stbuf, NULL) == -1)
-                       err = -EIO;
+               err = buf_get_attrs(&outbuf, stbuf, NULL);
                buf_free(&outbuf);
        }
        buf_free(&buf);
@@ -1922,8 +2030,7 @@
                        struct buffer name;
                        err = sftp_request(SSH_FXP_READDIR, &handle, 
SSH_FXP_NAME, &name);
                        if (!err) {
-                               if (buf_get_entries(&name, h, filler) == -1)
-                                       err = -EIO;
+                               err = buf_get_entries(&name, h, filler);
                                buf_free(&name);
                        }
                } while (!err);
@@ -2108,6 +2215,7 @@
        buf_add_path(&buf, path);
        buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
        buf_add_uint32(&buf, mode);
+       /* FIXME: really needs LSETSTAT extension (debian Bug#640038) */
        err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
        buf_free(&buf);
        return err;
@@ -2117,6 +2225,16 @@
 {
        int err;
        struct buffer buf;
+
+       if (sshfs.remote_uid_detected && uid == sshfs.local_uid)
+               uid = sshfs.remote_uid;
+       if (sshfs.idmap == IDMAP_FILE && sshfs.r_uid_map)
+               if(translate_id(&uid, sshfs.r_uid_map) == -1)
+                       return -EPERM;
+       if (sshfs.idmap == IDMAP_FILE && sshfs.r_gid_map)
+               if (translate_id(&gid, sshfs.r_gid_map) == -1)
+                       return -EPERM;
+
        buf_init(&buf, 0);
        buf_add_path(&buf, path);
        buf_add_uint32(&buf, SSH_FILEXFER_ATTR_UIDGID);
@@ -2130,12 +2248,19 @@
 static int sshfs_truncate_workaround(const char *path, off_t size,
                                      struct fuse_file_info *fi);
 
+static void sshfs_inc_modifver(void)
+{
+       pthread_mutex_lock(&sshfs.lock);
+       sshfs.modifver++;
+       pthread_mutex_unlock(&sshfs.lock);
+}
+
 static int sshfs_truncate(const char *path, off_t size)
 {
        int err;
        struct buffer buf;
 
-       sshfs.modifver ++;
+       sshfs_inc_modifver();
        if (size == 0 || sshfs.truncate_workaround)
                return sshfs_truncate_workaround(path, size, NULL);
 
@@ -2164,7 +2289,13 @@
 
 static inline int sshfs_file_is_conn(struct sshfs_file *sf)
 {
-       return sf->connver == sshfs.connver;
+       int ret;
+
+       pthread_mutex_lock(&sshfs.lock);
+       ret = (sf->connver == sshfs.connver);
+       pthread_mutex_unlock(&sshfs.lock);
+
+       return ret;
 }
 
 static int sshfs_open_common(const char *path, mode_t mode,
@@ -2207,8 +2338,10 @@
        sf->is_seq = 0;
        sf->refs = 1;
        sf->next_pos = 0;
+       pthread_mutex_lock(&sshfs.lock);
        sf->modifver= sshfs.modifver;
        sf->connver = sshfs.connver;
+       pthread_mutex_unlock(&sshfs.lock);
        buf_init(&buf, 0);
        buf_add_path(&buf, path);
        buf_add_uint32(&buf, pflags);
@@ -2222,8 +2355,7 @@
        type = sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT;
        err2 = sftp_request(type, &buf, SSH_FXP_ATTRS, &outbuf);
        if (!err2) {
-               if (buf_get_attrs(&outbuf, &stbuf, NULL) == -1)
-                       err2 = -EIO;
+               err2 = buf_get_attrs(&outbuf, &stbuf, NULL);
                buf_free(&outbuf);
        }
        err = sftp_request_wait(open_req, SSH_FXP_OPEN, SSH_FXP_HANDLE,
@@ -2319,129 +2451,178 @@
        return 0;
 }
 
-static int sshfs_sync_read(struct sshfs_file *sf, char *rbuf, size_t size,
-                           off_t offset)
-{
-       int err;
-       struct buffer buf;
-       struct buffer data;
-       struct buffer *handle = &sf->handle;
-       buf_init(&buf, 0);
-       buf_add_buf(&buf, handle);
-       buf_add_uint64(&buf, offset);
-       buf_add_uint32(&buf, size);
-       err = sftp_request(SSH_FXP_READ, &buf, SSH_FXP_DATA, &data);
-       if (!err) {
-               uint32_t retsize;
-               err = -EIO;
-               if (buf_get_uint32(&data, &retsize) != -1) {
-                       if (retsize > size)
-                               fprintf(stderr, "long read\n");
-                       else {
-                               buf_get_mem(&data, rbuf, retsize);
-                               err = retsize;
-                       }
-               }
-               buf_free(&data);
-       } else if (err == MY_EOF)
-               err = 0;
-       buf_free(&buf);
-       return err;
-}
-
 static void sshfs_read_end(struct request *req)
 {
-       struct read_chunk *chunk = (struct read_chunk *) req->data;
+       struct read_req *rreq = (struct read_req *) req->data;
        if (req->error)
-               chunk->res = req->error;
+               rreq->res = req->error;
        else if (req->replied) {
-               chunk->res = -EIO;
+               rreq->res = -EIO;
 
                if (req->reply_type == SSH_FXP_STATUS) {
                        uint32_t serr;
                        if (buf_get_uint32(&req->reply, &serr) != -1) {
                                if (serr == SSH_FX_EOF)
-                                       chunk->res = 0;
+                                       rreq->res = 0;
+                               else
+                                       rreq->res = -sftp_error_to_errno(serr);
                        }
                } else if (req->reply_type == SSH_FXP_DATA) {
                        uint32_t retsize;
                        if (buf_get_uint32(&req->reply, &retsize) != -1) {
-                               if (retsize > chunk->size)
+                               if (retsize > rreq->size) {
                                        fprintf(stderr, "long read\n");
-                               else {
-                                       chunk->res = retsize;
-                                       chunk->data = req->reply;
+                               } else if (buf_check_get(&req->reply, retsize) 
!= -1) {
+                                       rreq->res = retsize;
+                                       rreq->data = req->reply;
                                        buf_init(&req->reply, 0);
                                }
                        }
-               } else
+               } else {
                        fprintf(stderr, "protocol error\n");
-       } else
-               chunk->res = -EIO;
+               }
+       } else {
+               rreq->res = -EIO;
+       }
 
-       sem_post(&chunk->ready);
-       chunk_put(chunk);
+       rreq->sio->num_reqs--;
+       if (!rreq->sio->num_reqs)
+               pthread_cond_broadcast(&rreq->sio->finished);
 }
 
 static void sshfs_read_begin(struct request *req)
 {
-       struct read_chunk *chunk = (struct read_chunk *) req->data;
-       chunk->refs++;
+       struct read_req *rreq = (struct read_req *) req->data;
+       rreq->sio->num_reqs++;
 }
 
-static void sshfs_send_async_read(struct sshfs_file *sf,
-                                  struct read_chunk *chunk)
+static struct read_chunk *sshfs_send_read(struct sshfs_file *sf, size_t size,
+                                         off_t offset)
 {
-       struct buffer buf;
+       struct read_chunk *chunk = g_new0(struct read_chunk, 1);
        struct buffer *handle = &sf->handle;
-       struct iovec iov;
 
-       buf_init(&buf, 0);
-       buf_add_buf(&buf, handle);
-       buf_add_uint64(&buf, chunk->offset);
-       buf_add_uint32(&buf, chunk->size);
-       buf_to_iov(&buf, &iov);
-       sftp_request_send(SSH_FXP_READ, &iov, 1, sshfs_read_begin,
-                         sshfs_read_end, 0, chunk, NULL);
-       buf_free(&buf);
+       pthread_cond_init(&chunk->sio.finished, NULL);
+       list_init(&chunk->reqs);
+       chunk->size = size;
+       chunk->offset = offset;
+       chunk->refs = 1;
+
+       while (size) {
+               int err;
+               struct buffer buf;
+               struct iovec iov[1];
+               struct read_req *rreq;
+               size_t bsize = size < sshfs.max_read ? size : sshfs.max_read;
+
+               rreq = g_new0(struct read_req, 1);
+               rreq->sio = &chunk->sio;
+               rreq->size = bsize;
+               buf_init(&rreq->data, 0);
+               list_add(&rreq->list, &chunk->reqs);
+
+               buf_init(&buf, 0);
+               buf_add_buf(&buf, handle);
+               buf_add_uint64(&buf, offset);
+               buf_add_uint32(&buf, bsize);
+               buf_to_iov(&buf, &iov[0]);
+               err = sftp_request_send(SSH_FXP_READ, iov, 1,
+                                       sshfs_read_begin,
+                                       sshfs_read_end,
+                                       0, rreq, NULL);
+
+               buf_free(&buf);
+               if (err)
+                       break;
+
+               size -= bsize;
+               offset += bsize;
+       }
+
+       return chunk;
 }
 
-static void submit_read(struct sshfs_file *sf, size_t size, off_t offset,
-                        struct read_chunk **chunkp)
+static int wait_chunk(struct read_chunk *chunk, char *buf, size_t size)
 {
-       struct read_chunk *chunk = g_new0(struct read_chunk, 1);
+       int res = 0;
+       struct read_req *rreq;
 
-       sem_init(&chunk->ready, 0, 0);
-       buf_init(&chunk->data, 0);
-       chunk->offset = offset;
-       chunk->size = size;
-       chunk->refs = 1;
-       chunk->modifver = sshfs.modifver;
-       sshfs_send_async_read(sf, chunk);
        pthread_mutex_lock(&sshfs.lock);
-       chunk_put(*chunkp);
-       *chunkp = chunk;
+       while (chunk->sio.num_reqs)
+              pthread_cond_wait(&chunk->sio.finished, &sshfs.lock);
        pthread_mutex_unlock(&sshfs.lock);
-}
 
-static int wait_chunk(struct read_chunk *chunk, char *buf, size_t size)
-{
-       int res;
-       while (sem_wait(&chunk->ready));
-       res = chunk->res;
+
+       if (chunk->sio.error) {
+               if (chunk->sio.error != MY_EOF)
+                       res = chunk->sio.error;
+
+               goto out;
+       }
+
+       while (!list_empty(&chunk->reqs) && size) {
+               rreq = list_entry(chunk->reqs.prev, struct read_req, list);
+
+               if (rreq->res < 0) {
+                       chunk->sio.error = rreq->res;
+                       break;
+               } if (rreq->res == 0) {
+                       chunk->sio.error = MY_EOF;
+                       break;
+               } else if (size < (size_t) rreq->res) {
+                       buf_get_mem(&rreq->data, buf, size);
+                       rreq->res -= size;
+                       rreq->size -= size;
+                       res += size;
+                       break;
+               } else {
+                       buf_get_mem(&rreq->data, buf, rreq->res);
+                       res += rreq->res;
+                       if ((size_t) rreq->res < rreq->size) {
+                               chunk->sio.error = MY_EOF;
+                               break;
+                       }
+                       buf += rreq->res;
+                       size -= rreq->res;
+                       list_del(&rreq->list);
+                       buf_free(&rreq->data);
+                       g_free(rreq);
+               }
+       }
+
        if (res > 0) {
-               if ((size_t) res > size)
-                       res = size;
-               buf_get_mem(&chunk->data, buf, res);
                chunk->offset += res;
                chunk->size -= res;
-               chunk->res -= res;
        }
-       sem_post(&chunk->ready);
+
+out:
        chunk_put_locked(chunk);
        return res;
 }
 
+static int sshfs_sync_read(struct sshfs_file *sf, char *buf, size_t size,
+                           off_t offset)
+{
+       struct read_chunk *chunk;
+
+       chunk = sshfs_send_read(sf, size, offset);
+       return wait_chunk(chunk, buf, size);
+}
+
+static void submit_read(struct sshfs_file *sf, size_t size, off_t offset,
+                        struct read_chunk **chunkp)
+{
+       struct read_chunk *chunk;
+
+       chunk = sshfs_send_read(sf, size, offset);
+       pthread_mutex_lock(&sshfs.lock);
+       chunk->modifver = sshfs.modifver;
+       chunk_put(*chunkp);
+       *chunkp = chunk;
+       chunk->refs++;
+       pthread_mutex_unlock(&sshfs.lock);
+}
+
 static struct read_chunk *search_read_chunk(struct sshfs_file *sf, off_t 
offset)
 {
        struct read_chunk *ch = sf->readahead;
@@ -2545,37 +2726,123 @@
        sshfs_file_put(sf);
 }
 
+static int sshfs_async_write(struct sshfs_file *sf, const char *wbuf,
+                            size_t size, off_t offset)
+{
+       int err = 0;
+       struct buffer *handle = &sf->handle;
+
+       while (!err && size) {
+               struct buffer buf;
+               struct iovec iov[2];
+               size_t bsize = size < sshfs.max_write ? size : sshfs.max_write;
+
+               buf_init(&buf, 0);
+               buf_add_buf(&buf, handle);
+               buf_add_uint64(&buf, offset);
+               buf_add_uint32(&buf, bsize);
+               buf_to_iov(&buf, &iov[0]);
+               iov[1].iov_base = (void *) wbuf;
+               iov[1].iov_len = bsize;
+               err = sftp_request_send(SSH_FXP_WRITE, iov, 2,
+                                       sshfs_write_begin, sshfs_write_end,
+                                       0, sf, NULL);
+               buf_free(&buf);
+               size -= bsize;
+               wbuf += bsize;
+               offset += bsize;
+       }
+
+       return err;
+}
+
+static void sshfs_sync_write_begin(struct request *req)
+{
+       struct sshfs_io *sio = (struct sshfs_io *) req->data;
+       sio->num_reqs++;
+}
+
+static void sshfs_sync_write_end(struct request *req)
+{
+       uint32_t serr;
+       struct sshfs_io *sio = (struct sshfs_io *) req->data;
+
+       if (req->error) {
+               sio->error = req->error;
+       } else if (req->replied) {
+               if (req->reply_type != SSH_FXP_STATUS) {
+                       fprintf(stderr, "protocol error\n");
+               } else if (buf_get_uint32(&req->reply, &serr) != -1 &&
+                        serr != SSH_FX_OK) {
+                       sio->error = -EIO;
+               }
+       }
+       sio->num_reqs--;
+       if (!sio->num_reqs)
+               pthread_cond_broadcast(&sio->finished);
+}
+
+
+static int sshfs_sync_write(struct sshfs_file *sf, const char *wbuf,
+                           size_t size, off_t offset)
+{
+       int err = 0;
+       struct buffer *handle = &sf->handle;
+       struct sshfs_io sio = { .error = 0, .num_reqs = 0 };
+
+       pthread_cond_init(&sio.finished, NULL);
+
+       while (!err && size) {
+               struct buffer buf;
+               struct iovec iov[2];
+               size_t bsize = size < sshfs.max_write ? size : sshfs.max_write;
+
+               buf_init(&buf, 0);
+               buf_add_buf(&buf, handle);
+               buf_add_uint64(&buf, offset);
+               buf_add_uint32(&buf, bsize);
+               buf_to_iov(&buf, &iov[0]);
+               iov[1].iov_base = (void *) wbuf;
+               iov[1].iov_len = bsize;
+               err = sftp_request_send(SSH_FXP_WRITE, iov, 2,
+                                       sshfs_sync_write_begin,
+                                       sshfs_sync_write_end,
+                                       0, &sio, NULL);
+               buf_free(&buf);
+               size -= bsize;
+               wbuf += bsize;
+               offset += bsize;
+       }
+
+       pthread_mutex_lock(&sshfs.lock);
+       while (sio.num_reqs)
+              pthread_cond_wait(&sio.finished, &sshfs.lock);
+       pthread_mutex_unlock(&sshfs.lock);
+
+       if (!err)
+               err = sio.error;
+
+       return err;
+}
+
 static int sshfs_write(const char *path, const char *wbuf, size_t size,
                        off_t offset, struct fuse_file_info *fi)
 {
        int err;
-       struct buffer buf;
        struct sshfs_file *sf = get_sshfs_file(fi);
-       struct buffer *handle = &sf->handle;
-       struct iovec iov[2];
 
        (void) path;
 
        if (!sshfs_file_is_conn(sf))
                return -EIO;
 
-       sshfs.modifver ++;
-       buf_init(&buf, 0);
-       buf_add_buf(&buf, handle);
-       buf_add_uint64(&buf, offset);
-       buf_add_uint32(&buf, size);
-       buf_to_iov(&buf, &iov[0]);
-       iov[1].iov_base = (void *) wbuf;
-       iov[1].iov_len = size;
-       if (!sshfs.sync_write && !sf->write_error) {
-               err = sftp_request_send(SSH_FXP_WRITE, iov, 2,
-                                       sshfs_write_begin, sshfs_write_end,
-                                       0, sf, NULL);
-       } else {
-               err = sftp_request_iov(SSH_FXP_WRITE, iov, 2, SSH_FXP_STATUS,
-                                      NULL);
-       }
-       buf_free(&buf);
+       sshfs_inc_modifver();
+
+       if (!sshfs.sync_write && !sf->write_error)
+               err = sshfs_async_write(sf, wbuf, size, offset);
+       else
+               err = sshfs_sync_write(sf, wbuf, size, offset);
+
        return err ? err : (int) size;
 }
 
@@ -2665,7 +2932,7 @@
        if (!sshfs_file_is_conn(sf))
                return -EIO;
 
-       sshfs.modifver ++;
+       sshfs_inc_modifver();
        if (sshfs.truncate_workaround)
                return sshfs_truncate_workaround(path, size, fi);
 
@@ -2693,12 +2960,14 @@
        if (!sshfs_file_is_conn(sf))
                return -EIO;
 
+       if (sshfs.fstat_workaround)
+               return sshfs_getattr(path, stbuf);
+
        buf_init(&buf, 0);
        buf_add_buf(&buf, &sf->handle);
        err = sftp_request(SSH_FXP_FSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
        if (!err) {
-               if (buf_get_attrs(&outbuf, stbuf, NULL) == -1)
-                       err = -EIO;
+               err = buf_get_attrs(&outbuf, stbuf, NULL);
                buf_free(&outbuf);
        }
        buf_free(&buf);
@@ -2910,10 +3179,17 @@
 "    -o idmap=TYPE          user/group ID mapping, possible types are:\n"
 "             none             no translation of the ID space (default)\n"
 "             user             only translate UID of connecting user\n"
+"             file             translate UIDs/GIDs contained in 
uidfile/gidfile\n"
+"    -o uidfile=FILE        file containing username:remote_uid mappings\n"
+"    -o gidfile=FILE        file containing groupname:remote_gid mappings\n"
+"    -o nomap=TYPE          with idmap=file, how to handle missing mappings\n"
+"             ignore           don't do any re-mapping\n"
+"             error            return an error (default)\n"
 "    -o ssh_command=CMD     execute CMD instead of 'ssh'\n"
 "    -o ssh_protocol=N      ssh protocol to use (default: 2)\n"
 "    -o sftp_server=SERV    path to sftp server or subsystem (default: sftp)\n"
 "    -o directport=PORT     directly connect to PORT bypassing ssh\n"
+"    -o slave               communicate over stdin and stdout bypassing 
network\n"
 "    -o transform_symlinks  transform absolute symlinks to relative\n"
 "    -o follow_symlinks     follow symlinks on the server\n"
 "    -o no_check_root       don't check for existence of 'dir' on server\n"
@@ -3099,7 +3375,6 @@
        }
        sshfs.password[n+1] = '\0';
        ssh_add_arg("-oNumberOfPasswordPrompts=1");
-       ssh_add_arg("-oPreferredAuthentications=password,keyboard-interactive");
 
        return 0;
 }
@@ -3123,7 +3398,7 @@
                                replace_arg(&sshfs.ssh_args.argv[0],
                                            sshfs.ssh_command);
                        } else {
-                               if (fuse_opt_insert_arg(&sshfs.ssh_args, i, 
+                               if (fuse_opt_insert_arg(&sshfs.ssh_args, i,
                                                sshfs.ssh_command) == -1)
                                        _exit(1);
                        }
@@ -3220,13 +3495,188 @@
                        return -1;
 
                if (!sshfs.no_check_root &&
-                   sftp_check_root(sshfs.base_path) == -1)
+                   sftp_check_root(sshfs.base_path) != 0)
                        return -1;
 
        }
        return 0;
 }
 
+/* number of ':' separated fields in a passwd/group file that we care
+ * about */
+#define IDMAP_FIELDS 3
+
+/* given a line from a uidmap or gidmap, parse out the name and id */
+static void parse_idmap_line(char *line, const char* filename,
+               const unsigned int lineno, uint32_t *ret_id, char **ret_name,
+               const int eof)
+{
+       /* chomp off the trailing newline */
+       char *p = line;
+       if ((p = strrchr(line, '\n')))
+               *p = '\0';
+       else if (!eof) {
+               fprintf(stderr, "%s:%u: line too long\n", filename, lineno);
+               exit(1);
+       }
+       char *tokens[IDMAP_FIELDS];
+       char *tok;
+       int i;
+       for (i = 0; (tok = strsep(&line, ":")) && (i < IDMAP_FIELDS) ; i++) {
+               tokens[i] = tok;
+       }
+
+       char *name_tok, *id_tok;
+       if (i == 2) {
+               /* assume name:id format */
+               name_tok = tokens[0];
+               id_tok = tokens[1];
+       } else if (i >= IDMAP_FIELDS) {
+               /* assume passwd/group file format */
+               name_tok = tokens[0];
+               id_tok = tokens[2];
+       } else {
+               fprintf(stderr, "%s:%u: unknown format\n", filename, lineno);
+               exit(1);
+       }
+
+       errno = 0;
+       uint32_t remote_id = strtoul(id_tok, NULL, 10);
+       if (errno) {
+               fprintf(stderr, "Invalid id number on line %u of '%s': %s\n",
+                               lineno, filename, strerror(errno));
+               exit(1);
+       }
+
+       *ret_name = strdup(name_tok);
+       *ret_id = remote_id;
+}
+
+/* read a uidmap or gidmap */
+static void read_id_map(char *file, uint32_t *(*map_fn)(char *),
+               const char *name_id, GHashTable **idmap, GHashTable **r_idmap)
+{
+       *idmap = g_hash_table_new(NULL, NULL);
+       *r_idmap = g_hash_table_new(NULL, NULL);
+       FILE *fp;
+       char line[LINE_MAX];
+       unsigned int lineno = 0;
+       uid_t local_uid = getuid();
+
+       fp = fopen(file, "r");
+       if (fp == NULL) {
+               fprintf(stderr, "failed to open '%s': %s\n",
+                               file, strerror(errno));
+               exit(1);
+       }
+       struct stat st;
+       if (fstat(fileno(fp), &st) == -1) {
+               fprintf(stderr, "failed to stat '%s': %s\n", file,
+                               strerror(errno));
+               exit(1);
+       }
+       if (st.st_uid != local_uid) {
+               fprintf(stderr, "'%s' is not owned by uid %lu\n", file,
+                               (unsigned long)local_uid);
+               exit(1);
+       }
+       if (st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) {
+               fprintf(stderr, "'%s' is writable by other users\n", file);
+               exit(1);
+       }
+
+       while (fgets(line, LINE_MAX, fp) != NULL) {
+               lineno++;
+               uint32_t remote_id;
+               char *name;
+
+               /* skip blank lines */
+               if (line[0] == '\n' || line[0] == '\0')
+                       continue;
+
+               parse_idmap_line(line, file, lineno, &remote_id, &name, 
feof(fp));
+
+               uint32_t *local_id = map_fn(name);
+               if (local_id == NULL) {
+                       /* not found */
+                       DEBUG("%s(%u): no local %s\n", name, remote_id, 
name_id);
+                       free(name);
+                       continue;
+               }
+
+               DEBUG("%s: remote %s %u => local %s %u\n",
+                               name, name_id, remote_id, name_id, *local_id);
+               g_hash_table_insert(*idmap, GUINT_TO_POINTER(remote_id), 
GUINT_TO_POINTER(*local_id));
+               g_hash_table_insert(*r_idmap, GUINT_TO_POINTER(*local_id), 
GUINT_TO_POINTER(remote_id));
+               free(name);
+               free(local_id);
+       }
+
+       if (fclose(fp) == EOF) {
+               fprintf(stderr, "failed to close '%s': %s",
+                               file, strerror(errno));
+               exit(1);
+       }
+}
+
+/* given a username, return a pointer to its uid, or NULL if it doesn't
+ * exist on this system */
+static uint32_t *username_to_uid(char *name)
+{
+       errno = 0;
+       struct passwd *pw = getpwnam(name);
+       if (pw == NULL) {
+               if (errno == 0) {
+                       /* "does not exist" */
+                       return NULL;
+               }
+               fprintf(stderr, "Failed to look up user '%s': %s\n",
+                               name, strerror(errno));
+               exit(1);
+       }
+       uint32_t *r = malloc(sizeof(uint32_t));
+       if (r == NULL) {
+               fprintf(stderr, "sshfs: memory allocation failed\n");
+               abort();
+       }
+       *r = pw->pw_uid;
+       return r;
+}
+
+/* given a groupname, return a pointer to its gid, or NULL if it doesn't
+ * exist on this system */
+static uint32_t *groupname_to_gid(char *name)
+{
+       errno = 0;
+       struct group *gr = getgrnam(name);
+       if (gr == NULL) {
+               if (errno == 0) {
+                       /* "does not exist" */
+                       return NULL;
+               }
+               fprintf(stderr, "Failed to look up group '%s': %s\n",
+                               name, strerror(errno));
+               exit(1);
+       }
+       uint32_t *r = malloc(sizeof(uint32_t));
+       if (r == NULL) {
+               fprintf(stderr, "sshfs: memory allocation failed\n");
+               abort();
+       }
+       *r = gr->gr_gid;
+       return r;
+}
+
+static inline void load_uid_map(void)
+{
+       read_id_map(sshfs.uid_file, &username_to_uid, "uid", &sshfs.uid_map, 
&sshfs.r_uid_map);
+}
+
+static inline void load_gid_map(void)
+{
+       read_id_map(sshfs.gid_file, &groupname_to_gid, "gid", &sshfs.gid_map, 
&sshfs.r_gid_map);
+}
+
 int main(int argc, char *argv[])
 {
        int res;
@@ -3239,8 +3689,9 @@
        g_thread_init(NULL);
 
        sshfs.blksize = 4096;
-       sshfs.max_read = 65536;
-       sshfs.max_write = 65536;
+       /* SFTP spec says all servers should allow at least 32k I/O */
+       sshfs.max_read = 32768;
+       sshfs.max_write = 32768;
        sshfs.nodelay_workaround = 1;
        sshfs.nodelaysrv_workaround = 0;
        sshfs.rename_workaround = 0;
@@ -3248,10 +3699,15 @@
        sshfs.buflimit_workaround = 1;
        sshfs.ssh_ver = 2;
        sshfs.progname = argv[0];
-       sshfs.fd = -1;
+       sshfs.rfd = -1;
+       sshfs.wfd = -1;
        sshfs.ptyfd = -1;
        sshfs.ptyslavefd = -1;
        sshfs.delay_connect = 0;
+       sshfs.slave = 0;
+       sshfs.detect_uid = 0;
+       sshfs.idmap = IDMAP_NONE;
+       sshfs.nomap = NOMAP_ERROR;
        ssh_add_arg("ssh");
        ssh_add_arg("-x");
        ssh_add_arg("-a");
@@ -3261,8 +3717,37 @@
            parse_workarounds() == -1)
                exit(1);
 
+       if (sshfs.idmap == IDMAP_USER)
+               sshfs.detect_uid = 1;
+       else if (sshfs.idmap == IDMAP_FILE) {
+               sshfs.uid_map = NULL;
+               sshfs.gid_map = NULL;
+               sshfs.r_uid_map = NULL;
+               sshfs.r_gid_map = NULL;
+               if (!sshfs.uid_file && !sshfs.gid_file) {
+                       fprintf(stderr, "need a uidfile or gidfile with 
idmap=file\n");
+                       exit(1);
+               }
+               if (sshfs.uid_file)
+                       load_uid_map();
+               if (sshfs.gid_file)
+                       load_gid_map();
+       }
+       free(sshfs.uid_file);
+       free(sshfs.gid_file);
+
        DEBUG("SSHFS version %s\n", PACKAGE_VERSION);
 
+       if (sshfs.slave) {
+               /* Force sshfs to the foreground when using stdin+stdout */
+               sshfs.foreground = 1;
+       }
+
+       if (sshfs.slave && sshfs.password_stdin) {
+               fprintf(stderr, "the password_stdin and slave options cannot 
both be specified\n");
+               exit(1);
+       }
+
        if (sshfs.password_stdin) {
                res = read_password();
                if (res == -1)
@@ -3320,12 +3805,6 @@
 
        if (fuse_is_lib_option("ac_attr_timeout="))
                fuse_opt_insert_arg(&args, 1, "-oauto_cache,ac_attr_timeout=0");
-       tmp = g_strdup_printf("-omax_read=%u", sshfs.max_read);
-       fuse_opt_insert_arg(&args, 1, tmp);
-       g_free(tmp);
-       tmp = g_strdup_printf("-omax_write=%u", sshfs.max_write);
-       fuse_opt_insert_arg(&args, 1, tmp);
-       g_free(tmp);
 #if FUSE_VERSION >= 27
        libver = fuse_version();
        assert(libver >= 27);
@@ -3352,11 +3831,16 @@
                int foreground;
                struct stat st;
 
-               res = fuse_parse_cmdline(&args, &mountpoint, &multithreaded, 
+               res = fuse_parse_cmdline(&args, &mountpoint, &multithreaded,
                                         &foreground);
                if (res == -1)
                        exit(1);
 
+               if (sshfs.slave) {
+                       /* Force sshfs to the foreground when using 
stdin+stdout */
+                       foreground = 1;
+               }
+
                res = stat(mountpoint, &st);
                if (res == -1) {
                        perror(mountpoint);
@@ -3370,7 +3854,7 @@
 
                res = fcntl(fuse_chan_fd(ch), F_SETFD, FD_CLOEXEC);
                if (res == -1)
-                       perror("WARNING: failed to set FD_CLOESEC on fuse 
device");
+                       perror("WARNING: failed to set FD_CLOEXEC on fuse 
device");
 
                fuse = fuse_new(ch, &args, cache_init(&sshfs_oper),
                                sizeof(struct fuse_operations), NULL);

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to