importing sources sshfs_0_1 start
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 12 Nov 2004 22:54:02 +0000 (22:54 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 12 Nov 2004 22:54:02 +0000 (22:54 +0000)
Makefile [new file with mode: 0644]
sshfs.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c2394d1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+CC = gcc
+
+CFLAGS := -Wall -W -g 
+LDLIBS := -lpthread -ldl -rdynamic
+
+PKGCONFIG := env PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config
+FUSEVER := $(shell $(PKGCONFIG) --modversion fuse 2> /dev/null)
+ifeq ($(FUSEVER),)
+       LDLIBS += -lfuse
+else
+       CFLAGS += $(shell $(PKGCONFIG) --cflags fuse)
+       LDLIBS += $(shell $(PKGCONFIG) --libs fuse)
+endif
+
+CPPFLAGS := -D_FILE_OFFSET_BITS=64
+#CPPFLAGS += -DDEBUG
+
+sshfs: sshfs.o 
+
+clean:
+       rm -f *.o sshfs
diff --git a/sshfs.c b/sshfs.c
new file mode 100644 (file)
index 0000000..263e6b6
--- /dev/null
+++ b/sshfs.c
@@ -0,0 +1,887 @@
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#define SSH_FXP_INIT                1
+#define SSH_FXP_VERSION             2
+#define SSH_FXP_OPEN                3
+#define SSH_FXP_CLOSE               4
+#define SSH_FXP_READ                5
+#define SSH_FXP_WRITE               6
+#define SSH_FXP_LSTAT               7
+#define SSH_FXP_FSTAT               8
+#define SSH_FXP_SETSTAT             9
+#define SSH_FXP_FSETSTAT           10
+#define SSH_FXP_OPENDIR            11
+#define SSH_FXP_READDIR            12
+#define SSH_FXP_REMOVE             13
+#define SSH_FXP_MKDIR              14
+#define SSH_FXP_RMDIR              15
+#define SSH_FXP_REALPATH           16
+#define SSH_FXP_STAT               17
+#define SSH_FXP_RENAME             18
+#define SSH_FXP_READLINK           19
+#define SSH_FXP_SYMLINK            20
+#define SSH_FXP_STATUS            101
+#define SSH_FXP_HANDLE            102
+#define SSH_FXP_DATA              103
+#define SSH_FXP_NAME              104
+#define SSH_FXP_ATTRS             105
+#define SSH_FXP_EXTENDED          200
+#define SSH_FXP_EXTENDED_REPLY    201
+
+#define SSH_FILEXFER_ATTR_SIZE          0x00000001
+#define SSH_FILEXFER_ATTR_UIDGID        0x00000002
+#define SSH_FILEXFER_ATTR_PERMISSIONS   0x00000004
+#define SSH_FILEXFER_ATTR_ACMODTIME     0x00000008
+#define SSH_FILEXFER_ATTR_EXTENDED      0x80000000
+
+#define SSH_FX_OK                            0
+#define SSH_FX_EOF                           1
+#define SSH_FX_NO_SUCH_FILE                  2
+#define SSH_FX_PERMISSION_DENIED             3
+#define SSH_FX_FAILURE                       4
+#define SSH_FX_BAD_MESSAGE                   5
+#define SSH_FX_NO_CONNECTION                 6
+#define SSH_FX_CONNECTION_LOST               7
+#define SSH_FX_OP_UNSUPPORTED                8
+
+#define SSH_FXF_READ            0x00000001
+#define SSH_FXF_WRITE           0x00000002
+#define SSH_FXF_APPEND          0x00000004
+#define SSH_FXF_CREAT           0x00000008
+#define SSH_FXF_TRUNC           0x00000010
+#define SSH_FXF_EXCL            0x00000020
+
+#define PROTO_VERSION 3
+
+#define MY_EOF 1
+
+static int infd;
+static int outfd;
+
+struct buffer {
+    uint8_t *p;
+    size_t len;
+    size_t size;
+};
+
+static inline void buf_init(struct buffer *buf, size_t size)
+{
+    if (size) {
+        buf->p = malloc(size);
+        if (!buf->p)
+            exit(1);
+    } else
+        buf->p = NULL;
+    buf->len = 0;
+    buf->size = size;
+}
+
+static inline void buf_free(struct buffer *buf)
+{
+    free(buf->p);
+}
+
+static inline void buf_finish(struct buffer *buf)
+{
+    buf->len = buf->size;
+}
+
+
+static inline void buf_clear(struct buffer *buf)
+{
+    buf_free(buf);
+    buf_init(buf, 0);
+}
+
+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)
+        exit(1);
+}
+
+static inline void buf_check_add(struct buffer *buf, size_t len)
+{
+    if (buf->len + len > buf->size)
+        buf_resize(buf, len);
+}
+
+#define _buf_add_mem(b, d, l)    \
+    buf_check_add(b, l);       \
+    memcpy(b->p + b->len, d, l); \
+    b->len += l;
+
+
+static inline void buf_add_mem(struct buffer *buf, const void *data,
+                               size_t len)
+{
+    _buf_add_mem(buf, data, len);
+}
+
+static inline void buf_add_uint8(struct buffer *buf, uint8_t val)
+{
+    _buf_add_mem(buf, &val, 1);
+}
+
+static inline void buf_add_uint32(struct buffer *buf, uint32_t val)
+{
+    uint32_t nval = htonl(val);
+    _buf_add_mem(buf, &nval, 4);
+}
+
+static inline void buf_add_uint64(struct buffer *buf, uint64_t val)
+{
+    buf_add_uint32(buf, val >> 32);
+    buf_add_uint32(buf, val & 0xffffffff);
+}
+
+static inline void buf_add_data(struct buffer *buf, const struct buffer *data)
+{
+    buf_add_uint32(buf, data->len);
+    buf_add_mem(buf, data->p, data->len);
+}
+
+static inline void buf_add_string(struct buffer *buf, const char *str)
+{
+    struct buffer data;
+    data.p = (uint8_t *) str;
+    data.len = strlen(str);
+    buf_add_data(buf, &data);
+}
+
+static int buf_check_get(struct buffer *buf, size_t len)
+{
+    if (buf->len + len > buf->size) {
+        fprintf(stderr, "buffer too short\n");
+        return -1;
+    } else
+        return 0; 
+}
+
+static inline int buf_get_mem(struct buffer *buf, void *data, size_t len)
+{
+    if (buf_check_get(buf, len) == -1)
+        return -1;
+    memcpy(data, buf->p + buf->len, len);
+    buf->len += len;
+    return 0;
+}
+
+static inline int buf_get_uint8(struct buffer *buf, uint8_t *val)
+{
+    return buf_get_mem(buf, val, 1);
+}
+
+static inline int buf_get_uint32(struct buffer *buf, uint32_t *val)
+{
+    uint32_t nval;
+    if (buf_get_mem(buf, &nval, 4) == -1)
+        return -1;
+    *val = ntohl(nval);
+    return 0;
+}
+
+static inline int buf_get_uint64(struct buffer *buf, uint64_t *val)
+{
+    uint32_t val1;
+    uint32_t val2;
+    if (buf_get_uint32(buf, &val1) == -1 || buf_get_uint32(buf, &val2) == -1)
+        return -1;
+    *val = ((uint64_t) val1 << 32) + val2;
+    return 0;
+}
+
+static inline int buf_get_data(struct buffer *buf, struct buffer *data)
+{
+    uint32_t len;
+    if (buf_get_uint32(buf, &len) == -1 || len > buf->size - buf->len)
+        return -1;
+    buf_init(data, len + 1);
+    data->size = len;
+    if (buf_get_mem(buf, data->p, data->size) == -1) {
+        buf_free(data);
+        return -1;
+    }
+    return 0;
+}
+
+static inline int buf_get_string(struct buffer *buf, char **str)
+{
+    struct buffer data;
+    if (buf_get_data(buf, &data) == -1)
+        return -1;
+    data.p[data.size] = '\0';
+    *str = (char *) data.p;
+    return 0;
+}
+
+static int buf_get_attrs(struct buffer *buf, struct stat *stbuf)
+{
+    uint32_t flags;
+    uint64_t size = 0;
+    uint32_t uid = 0;
+    uint32_t gid = 0;
+    uint32_t atime = 0;
+    uint32_t mtime = 0;
+    uint32_t mode = S_IFREG | 0777;
+
+    if (buf_get_uint32(buf, &flags) == -1)
+        return -1;
+    if ((flags & SSH_FILEXFER_ATTR_SIZE) &&
+        buf_get_uint64(buf, &size) == -1)
+        return -1;
+    if ((flags & SSH_FILEXFER_ATTR_UIDGID) &&
+        (buf_get_uint32(buf, &uid) == -1 ||
+         buf_get_uint32(buf, &gid) == -1))
+        return -1;
+    if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
+        buf_get_uint32(buf, &mode) == -1)
+        return -1;
+    if ((flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
+        if (buf_get_uint32(buf, &atime) == -1 ||
+            buf_get_uint32(buf, &mtime) == -1)
+            return -1;
+    } 
+    if ((flags & SSH_FILEXFER_ATTR_EXTENDED)) {
+        uint32_t extcount;
+        unsigned i;
+        if (buf_get_uint32(buf, &extcount) == -1)
+            return -1;
+        for (i = 0; i < extcount; i++) {
+            struct buffer tmp;
+            if (buf_get_data(buf, &tmp) == -1)
+                return -1;
+            buf_free(&tmp);
+            if (buf_get_data(buf, &tmp) == -1)
+                return -1;
+            buf_free(&tmp);
+        }
+    }
+    memset(stbuf, 0, sizeof(struct stat));
+    stbuf->st_mode = mode;
+    stbuf->st_nlink = 1;
+    stbuf->st_size = size;
+    stbuf->st_uid = uid;
+    stbuf->st_gid = gid;
+    stbuf->st_atime = atime;
+    stbuf->st_mtime = mtime;
+    return 0;
+}
+
+static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
+                           fuse_dirfil_t filler)
+{
+    uint32_t count;
+    unsigned i;
+
+    if (buf_get_uint32(buf, &count) == -1)
+        return -1;
+    
+    for (i = 0; i < count; i++) {
+        int err = -1;
+        char *name;
+        char *longname;
+        struct stat stbuf;
+        if (buf_get_string(buf, &name) == -1)
+            return -1;
+        if (buf_get_string(buf, &longname) != -1) {
+            free(longname);
+            if (buf_get_attrs(buf, &stbuf) != -1) {
+                filler(h, name, stbuf.st_mode >> 12);
+                err = 0;
+            }
+        }
+        free(name);
+        if (err)
+            return err;
+    }
+    return 0;
+}
+
+static int start_ssh(const char *host)
+{
+    int inpipe[2];
+    int outpipe[2];
+    int pid;
+
+    if (pipe(inpipe) == -1 || pipe(outpipe) == -1) {
+        perror("failed to create pipe");
+        return -1;
+    }
+    infd = inpipe[0];
+    outfd = outpipe[1];
+
+    pid = fork();
+    if (pid == -1) {
+        perror("failed to fork");
+        return -1;
+    } else if (pid == 0) {
+        if (dup2(outpipe[0], 0) == -1 || dup2(inpipe[1], 1) == -1) {
+            perror("failed to redirect input/output");
+            exit(1);
+        }
+        close(inpipe[0]);
+        close(inpipe[1]);
+        close(outpipe[0]);
+        close(outpipe[1]);
+        execlp("ssh", "ssh", "-2", "-x", "-a", "-oClearAllForwardings=yes",
+               host, "-s", "sftp", NULL);
+        exit(1);
+    }
+    close(inpipe[1]);
+    close(outpipe[0]);
+    return 0;
+}
+
+static int do_write(struct buffer *buf)
+{
+    uint8_t *p = buf->p;
+    size_t size = buf->len;
+    int res;
+    while (size) {
+        res = write(outfd, p, size);
+        if (res == -1) {
+            perror("write");
+            return -1;
+        } else if (res == 0) {
+            fprintf(stderr, "zero write\n");
+            return -1;
+        }
+        size -= res;
+        p += res;
+    }
+    return 0;
+}
+
+static uint32_t sftp_get_id(void)
+{
+    static uint32_t idctr;
+    return idctr++;
+}
+
+static int sftp_send(uint8_t type, struct buffer *buf)
+{
+    int res;
+    struct buffer buf2;
+    buf_init(&buf2, 5);
+    buf_add_uint32(&buf2, buf->len + 1);
+    buf_add_uint8(&buf2, type);
+    res = do_write(&buf2);
+    if (res != -1)
+        res = do_write(buf);
+    buf_free(&buf2);
+    return res;
+}
+
+static int do_read(struct buffer *buf)
+{
+    int res;
+    uint8_t *p = buf->p;
+    size_t size = buf->size;
+    while (size) {
+        res = read(infd, p, size);
+        if (res == -1) {
+            perror("read");
+            return -1;
+        } else if (res == 0) {
+            fprintf(stderr, "end of file read\n");
+            return -1;
+        }
+        size -= res;
+        p += res;
+    }
+    return 0;
+}
+
+static int sftp_read(uint8_t *type, struct buffer *buf)
+{
+    int res;
+    struct buffer buf2;
+    uint32_t len;
+    buf_init(&buf2, 5);
+    res = do_read(&buf2);
+    if (res != -1) {
+        buf_get_uint32(&buf2, &len);
+        buf_get_uint8(&buf2, type);
+        buf_init(buf, len - 1);
+        res = do_read(buf);
+    }
+    buf_free(&buf2);
+    return res;
+}
+
+static int sftp_request(uint8_t type, const struct buffer *buf,
+                        uint8_t expect_type, struct buffer *outbuf)
+{
+    int err;
+    struct buffer buf2;
+    uint32_t id = sftp_get_id();
+    uint32_t idback;
+    uint8_t typeback;
+
+    buf_init(&buf2, buf->len + 4);
+    buf_add_uint32(&buf2, id);
+    buf_add_mem(&buf2, buf->p, buf->len);
+    
+    err = -EIO;
+    if (sftp_send(type, &buf2) == -1)
+        goto out;
+    buf_clear(&buf2);
+    if (sftp_read(&typeback, &buf2) == -1)
+        goto out;
+    err = -EPROTO;
+    if (typeback != expect_type && typeback != SSH_FXP_STATUS) {
+        fprintf(stderr, "protocol error\n");
+        goto out;
+    }
+    if (buf_get_uint32(&buf2, &idback) == -1)
+        goto out;
+    if (idback != id) {
+        fprintf(stderr, "Invalid ID received\n");
+        goto out;
+    }
+    if (typeback == SSH_FXP_STATUS) {
+        uint32_t serr;
+        if (buf_get_uint32(&buf2, &serr) == -1)
+            goto out;
+        
+        switch (serr) {
+        case SSH_FX_OK:
+            if (expect_type == SSH_FXP_STATUS)
+                err = 0;
+            else
+                err = -EPROTO;
+            break;
+
+        case SSH_FX_EOF:
+            if (type == SSH_FXP_READ || type == SSH_FXP_READDIR)
+                err = MY_EOF;
+            else
+                err = -EPROTO;
+            break;
+
+        case SSH_FX_NO_SUCH_FILE:      err = -ENOENT; break;
+        case SSH_FX_PERMISSION_DENIED: err = -EACCES; break;            
+        case SSH_FX_FAILURE:           err = -EPERM;  break;
+        case SSH_FX_BAD_MESSAGE:
+        default:                       err = -EPROTO; break;
+        }
+    } else {
+        buf_init(outbuf, buf2.size - buf2.len);
+        buf_get_mem(&buf2, outbuf->p, outbuf->size);
+        err = 0;
+    }
+
+ out:
+    buf_free(&buf2);
+    return err;
+        
+}
+
+static int sshfs_getattr(const char *path, struct stat *stbuf)
+{
+    int err;
+    struct buffer buf;
+    struct buffer outbuf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    err = sftp_request(SSH_FXP_LSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
+    if (!err) {
+        if (buf_get_attrs(&outbuf, stbuf) == -1)
+            err = -EPROTO;
+        buf_free(&outbuf);
+    }
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
+{
+    int err;
+    struct buffer buf;
+    struct buffer name;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    err = sftp_request(SSH_FXP_READLINK, &buf, SSH_FXP_NAME, &name);
+    if (!err) {
+        uint32_t count;
+        char *link;
+        err = -EPROTO;
+        if(buf_get_uint32(&name, &count) != -1 && count == 1 &&
+           buf_get_string(&name, &link) != -1) {
+            strncpy(linkbuf, link, size-1);
+            linkbuf[size-1] = '\0';
+            free(link);
+            err = 0;
+        }
+        buf_free(&name);
+    }
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
+{
+    int err;
+    struct buffer buf;
+    struct buffer handle;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    err = sftp_request(SSH_FXP_OPENDIR, &buf, SSH_FXP_HANDLE, &handle);
+    if (!err) {
+        int err2;
+        buf_finish(&handle);
+        do {
+            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 = -EPROTO;
+                buf_free(&name);
+            }
+        } while (!err);
+        if (err == MY_EOF)
+            err = 0;
+        
+        
+        err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
+        if (!err)
+            err = err2;
+        buf_free(&handle);
+    }
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_mkdir(const char *path, mode_t mode)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
+    buf_add_uint32(&buf, mode);
+    err = sftp_request(SSH_FXP_MKDIR, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+static int sshfs_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+    int err;
+    struct buffer buf;
+    struct buffer handle;
+
+    (void) rdev;
+
+    if ((mode & S_IFMT) != S_IFREG)
+        return -EPERM;
+
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_EXCL);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
+    buf_add_uint32(&buf, mode);
+    err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
+    if (!err) {
+        int err2;
+        buf_finish(&handle);
+        err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
+        if (!err)
+            err = err2;
+        buf_free(&handle);
+    }
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_symlink(const char *from, const char *to)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    /* openssh sftp server doesn't follow standard: link target and
+       link name are mixed up, so we must also be non-standard :( */
+    buf_add_string(&buf, from);
+    buf_add_string(&buf, to);
+    err = sftp_request(SSH_FXP_SYMLINK, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_unlink(const char *path)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    err = sftp_request(SSH_FXP_REMOVE, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_rmdir(const char *path)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    err = sftp_request(SSH_FXP_RMDIR, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_rename(const char *from, const char *to)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, from);
+    buf_add_string(&buf, to);
+    err = sftp_request(SSH_FXP_RENAME, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_chmod(const char *path, mode_t mode)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
+    buf_add_uint32(&buf, mode);
+    err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_chown(const char *path, uid_t uid, gid_t gid)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_UIDGID);
+    buf_add_uint32(&buf, uid);
+    buf_add_uint32(&buf, gid);
+    err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_truncate(const char *path, off_t size)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
+    buf_add_uint64(&buf, size);
+    err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_utime(const char *path, struct utimbuf *ubuf)
+{
+    int err;
+    struct buffer buf;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FILEXFER_ATTR_ACMODTIME);
+    buf_add_uint32(&buf, ubuf->actime);
+    buf_add_uint32(&buf, ubuf->modtime);
+    err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_open(const char *path, int flags)
+{
+    int err;
+    struct buffer buf;
+    struct buffer handle;
+    uint32_t pflags;
+    if ((flags & O_ACCMODE) == O_RDONLY)
+        pflags = SSH_FXF_READ;
+    else if((flags & O_ACCMODE) == O_WRONLY)
+        pflags = SSH_FXF_WRITE;
+    else 
+        pflags = SSH_FXF_READ | SSH_FXF_WRITE;
+     
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, pflags);
+    buf_add_uint32(&buf, 0);
+    err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
+    if (!err) {
+        int err2;
+        buf_finish(&handle);
+        err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
+        if (!err)
+            err = err2;
+        buf_free(&handle);
+    }
+    buf_free(&buf);
+    return err;
+}
+
+static int sshfs_read(const char *path, char *rbuf, size_t size, off_t offset)
+{
+    int err;
+    struct buffer buf;
+    struct buffer handle;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FXF_READ);
+    buf_add_uint32(&buf, 0);
+    err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
+    if (!err) {
+        struct buffer data;
+        int err2;
+        buf_finish(&handle);
+        buf_add_uint64(&handle, offset);
+        buf_add_uint32(&handle, size);
+        err = sftp_request(SSH_FXP_READ, &handle, SSH_FXP_DATA, &data);
+        if (!err) {
+            uint32_t retsize;
+            err = -EPROTO;
+            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);
+        }
+        err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
+        if (err2 && err >= 0)
+            err = err2;
+        buf_free(&handle);
+    }
+    buf_free(&buf);
+    return err;    
+}
+
+static int sshfs_write(const char *path, const char *wbuf, size_t size,
+                       off_t offset)
+{
+    int err;
+    struct buffer buf;
+    struct buffer handle;
+    buf_init(&buf, 0);
+    buf_add_string(&buf, path);
+    buf_add_uint32(&buf, SSH_FXF_WRITE);
+    buf_add_uint32(&buf, 0);
+    err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
+    if (!err) {
+        struct buffer data;
+        int err2;
+        data.p = (uint8_t *) wbuf;
+        data.len = size;
+        buf_finish(&handle);
+        buf_add_uint64(&handle, offset);
+        buf_add_data(&handle, &data);
+        err = sftp_request(SSH_FXP_WRITE, &handle, SSH_FXP_STATUS, NULL);
+        err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
+        if (err2 && err >= 0)
+            err = err2;
+        buf_free(&handle);
+    }
+    buf_free(&buf);
+    return err;    
+}
+
+static int sftp_init()
+{
+    int res = -1;
+    uint8_t type;
+    uint32_t version;
+    struct buffer buf;
+    buf_init(&buf, 4);
+    buf_add_uint32(&buf, PROTO_VERSION);
+    if (sftp_send(SSH_FXP_INIT, &buf) == -1)
+        goto out;
+    buf_clear(&buf);
+    if (sftp_read(&type, &buf) == -1)
+        goto out;
+    if (type != SSH_FXP_VERSION) {
+        fprintf(stderr, "protocol error\n");
+        goto out;
+    }
+    if (buf_get_uint32(&buf, &version) == -1)
+        goto out;
+    if (version != PROTO_VERSION) {
+        fprintf(stderr, "server version: %i, we need: %i\n",
+                version, PROTO_VERSION);
+        goto out;
+    }
+    res = 0;
+ out:
+    buf_free(&buf);
+    return res;
+}
+
+static struct fuse_operations sshfs_oper = {
+    .getattr   = sshfs_getattr,
+    .readlink   = sshfs_readlink,
+    .getdir    = sshfs_getdir,
+    .mknod      = sshfs_mknod,
+    .mkdir      = sshfs_mkdir,
+    .symlink   = sshfs_symlink,
+    .unlink     = sshfs_unlink,
+    .rmdir      = sshfs_rmdir,
+    .rename     = sshfs_rename,
+    .chmod      = sshfs_chmod,
+    .chown     = sshfs_chown,
+    .truncate  = sshfs_truncate,
+    .utime      = sshfs_utime,
+    .open       = sshfs_open,
+    .read       = sshfs_read,
+    .write      = sshfs_write,
+};
+
+int main(int argc, char *argv[])
+{
+    char *host;
+    int res;
+    int argctr;
+    char **newargv;
+    
+
+    if (argc < 3) {
+        fprintf(stderr, "usage: %s [user@]host mountpoint [mount options]\n",
+                argv[0]);
+        exit(1);
+    }
+
+    host = argv[1];
+    
+    res = start_ssh(host);
+    if (res == -1)
+        exit(1);
+    
+    res = sftp_init();
+    if (res == -1)
+        exit(1);
+    
+    newargv = (char **) malloc((argc + 10) * sizeof(char *));
+    newargv[0] = argv[0];
+    for (argctr = 1; argctr < argc - 1; argctr++)
+        newargv[argctr] = argv[argctr + 1];
+    newargv[argctr++] = "-s";
+    newargv[argctr++] = "-omax_read=65536";
+    newargv[argctr] = NULL;
+    return fuse_main(argctr, newargv, &sshfs_oper);
+}