]> andersk Git - gssapi-openssh.git/blobdiff - openssh/sftp-client.c
update version number for patch release today
[gssapi-openssh.git] / openssh / sftp-client.c
index afbd1e6f3784a224c1ce5fbdd7c8928c8a178731..4f2a1e2800840406be251e3554126e1235b732f3 100644 (file)
@@ -1,28 +1,35 @@
 /*
- * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2001 Damien Miller.  All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 /* XXX: memleaks */
 /* XXX: signed vs unsigned */
-/* XXX: remove all logging, only return status codes */
+/* XXX: redesign to allow concurrent overlapped operations */
+/* XXX: we use fatal too much, error may be more appropriate in places */
 /* XXX: copy between two remote sites */
 
 #include "includes.h"
-RCSID("$OpenBSD: sftp-client.c,v 1.57 2005/07/27 10:39:03 dtucker Exp $");
-
-#include "openbsd-compat/sys-queue.h"
+RCSID("$OpenBSD: sftp-client.c,v 1.18 2001/07/14 15:10:16 stevesk Exp $");
 
 #include "buffer.h"
 #include "bufaux.h"
@@ -30,72 +37,62 @@ RCSID("$OpenBSD: sftp-client.c,v 1.57 2005/07/27 10:39:03 dtucker Exp $");
 #include "xmalloc.h"
 #include "log.h"
 #include "atomicio.h"
-#include "progressmeter.h"
 
 #include "sftp.h"
 #include "sftp-common.h"
 #include "sftp-client.h"
 
-extern volatile sig_atomic_t interrupted;
-extern int showprogress;
-
-/* Minimum amount of data to read at at time */
-#define MIN_READ_SIZE  512
-
-/* Maximum packet size */
-#define MAX_MSG_LENGTH (256 * 1024)
+/* How much data to read/write at at time during copies */
+/* XXX: what should this be? */
+#define COPY_SIZE      8192
 
-struct sftp_conn {
-       int fd_in;
-       int fd_out;
-       u_int transfer_buflen;
-       u_int num_requests;
-       u_int version;
-       u_int msg_id;
-};
+/* Message ID */
+static u_int msg_id = 1;
 
 static void
 send_msg(int fd, Buffer *m)
 {
-       u_char mlen[4];
+       int mlen = buffer_len(m);
+       int len;
+       Buffer oqueue;
 
-       if (buffer_len(m) > MAX_MSG_LENGTH)
-               fatal("Outbound message too long %u", buffer_len(m));
-
-       /* Send length first */
-       PUT_32BIT(mlen, buffer_len(m));
-       if (atomicio(vwrite, fd, mlen, sizeof(mlen)) != sizeof(mlen))
-               fatal("Couldn't send packet: %s", strerror(errno));
+       buffer_init(&oqueue);
+       buffer_put_int(&oqueue, mlen);
+       buffer_append(&oqueue, buffer_ptr(m), mlen);
+       buffer_consume(m, mlen);
 
-       if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) != buffer_len(m))
+       len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
+       if (len <= 0)
                fatal("Couldn't send packet: %s", strerror(errno));
 
-       buffer_clear(m);
+       buffer_free(&oqueue);
 }
 
 static void
 get_msg(int fd, Buffer *m)
 {
-       u_int msg_len;
-
-       buffer_append_space(m, 4);
-       if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
-               if (errno == EPIPE)
+       u_int len, msg_len;
+       unsigned char buf[4096];
+
+       len = atomicio(read, fd, buf, 4);
+       if (len == 0)
+               fatal("Connection closed");
+       else if (len == -1)
+               fatal("Couldn't read packet: %s", strerror(errno));
+
+       msg_len = GET_32BIT(buf);
+       if (msg_len > 256 * 1024)
+               fatal("Received message too long %d", msg_len);
+
+       while (msg_len) {
+               len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
+               if (len == 0)
                        fatal("Connection closed");
-               else
+               else if (len == -1)
                        fatal("Couldn't read packet: %s", strerror(errno));
-       }
-
-       msg_len = buffer_get_int(m);
-       if (msg_len > MAX_MSG_LENGTH)
-               fatal("Received message too long %u", msg_len);
 
-       buffer_append_space(m, msg_len);
-       if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
-               if (errno == EPIPE)
-                       fatal("Connection closed");
-               else
-                       fatal("Read packet: %s", strerror(errno));
+               msg_len -= len;
+               buffer_append(m, buf, len);
        }
 }
 
@@ -110,7 +107,7 @@ send_string_request(int fd, u_int id, u_int code, char *s,
        buffer_put_int(&msg, id);
        buffer_put_string(&msg, s, len);
        send_msg(fd, &msg);
-       debug3("Sent message fd %d T:%u I:%u", fd, code, id);
+       debug3("Sent message fd %d T:%d I:%d", fd, code, id);
        buffer_free(&msg);
 }
 
@@ -126,12 +123,12 @@ send_string_attrs_request(int fd, u_int id, u_int code, char *s,
        buffer_put_string(&msg, s, len);
        encode_attrib(&msg, a);
        send_msg(fd, &msg);
-       debug3("Sent message fd %d T:%u I:%u", fd, code, id);
+       debug3("Sent message fd %d T:%d I:%d", fd, code, id);
        buffer_free(&msg);
 }
 
 static u_int
-get_status(int fd, u_int expected_id)
+get_status(int fd, int expected_id)
 {
        Buffer msg;
        u_int type, id, status;
@@ -142,15 +139,15 @@ get_status(int fd, u_int expected_id)
        id = buffer_get_int(&msg);
 
        if (id != expected_id)
-               fatal("ID mismatch (%u != %u)", id, expected_id);
+               fatal("ID mismatch (%d != %d)", id, expected_id);
        if (type != SSH2_FXP_STATUS)
-               fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
+               fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d",
                    SSH2_FXP_STATUS, type);
 
        status = buffer_get_int(&msg);
        buffer_free(&msg);
 
-       debug3("SSH2_FXP_STATUS %u", status);
+       debug3("SSH2_FXP_STATUS %d", status);
 
        return(status);
 }
@@ -168,15 +165,14 @@ get_handle(int fd, u_int expected_id, u_int *len)
        id = buffer_get_int(&msg);
 
        if (id != expected_id)
-               fatal("ID mismatch (%u != %u)", id, expected_id);
+               fatal("ID mismatch (%d != %d)", id, expected_id);
        if (type == SSH2_FXP_STATUS) {
                int status = buffer_get_int(&msg);
 
                error("Couldn't get handle: %s", fx2txt(status));
-               buffer_free(&msg);
                return(NULL);
        } else if (type != SSH2_FXP_HANDLE)
-               fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
+               fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d",
                    SSH2_FXP_HANDLE, type);
 
        handle = buffer_get_string(&msg, len);
@@ -198,9 +194,9 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
        type = buffer_get_char(&msg);
        id = buffer_get_int(&msg);
 
-       debug3("Received stat reply T:%u I:%u", type, id);
+       debug3("Received stat reply T:%d I:%d", type, id);
        if (id != expected_id)
-               fatal("ID mismatch (%u != %u)", id, expected_id);
+               fatal("ID mismatch (%d != %d)", id, expected_id);
        if (type == SSH2_FXP_STATUS) {
                int status = buffer_get_int(&msg);
 
@@ -208,10 +204,9 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
                        debug("Couldn't stat remote file: %s", fx2txt(status));
                else
                        error("Couldn't stat remote file: %s", fx2txt(status));
-               buffer_free(&msg);
                return(NULL);
        } else if (type != SSH2_FXP_ATTRS) {
-               fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
+               fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
                    SSH2_FXP_ATTRS, type);
        }
        a = decode_attrib(&msg);
@@ -220,13 +215,11 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
        return(a);
 }
 
-struct sftp_conn *
-do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
+int
+do_init(int fd_in, int fd_out)
 {
-       u_int type;
-       int version;
+       int type, version;
        Buffer msg;
-       struct sftp_conn *ret;
 
        buffer_init(&msg);
        buffer_put_char(&msg, SSH2_FXP_INIT);
@@ -239,10 +232,10 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
 
        /* Expecting a VERSION reply */
        if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
-               error("Invalid packet back from SSH2_FXP_INIT (type %u)",
+               error("Invalid packet back from SSH2_FXP_INIT (type %d)",
                    type);
                buffer_free(&msg);
-               return(NULL);
+               return(-1);
        }
        version = buffer_get_int(&msg);
 
@@ -260,43 +253,25 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
 
        buffer_free(&msg);
 
-       ret = xmalloc(sizeof(*ret));
-       ret->fd_in = fd_in;
-       ret->fd_out = fd_out;
-       ret->transfer_buflen = transfer_buflen;
-       ret->num_requests = num_requests;
-       ret->version = version;
-       ret->msg_id = 1;
-
-       /* Some filexfer v.0 servers don't support large packets */
-       if (version == 0)
-               ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
-
-       return(ret);
-}
-
-u_int
-sftp_proto_version(struct sftp_conn *conn)
-{
-       return(conn->version);
+       return(version);
 }
 
 int
-do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
+do_close(int fd_in, int fd_out, char *handle, u_int handle_len)
 {
        u_int id, status;
        Buffer msg;
 
        buffer_init(&msg);
 
-       id = conn->msg_id++;
+       id = msg_id++;
        buffer_put_char(&msg, SSH2_FXP_CLOSE);
        buffer_put_int(&msg, id);
        buffer_put_string(&msg, handle, handle_len);
-       send_msg(conn->fd_out, &msg);
-       debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
+       send_msg(fd_out, &msg);
+       debug3("Sent message SSH2_FXP_CLOSE I:%d", id);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't close file: %s", fx2txt(status));
 
@@ -307,24 +282,24 @@ do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
 
 
 static int
-do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
+do_lsreaddir(int fd_in, int fd_out, char *path, int printflag,
     SFTP_DIRENT ***dir)
 {
        Buffer msg;
-       u_int count, type, id, handle_len, i, expected_id, ents = 0;
+       u_int type, id, handle_len, i, expected_id, ents = 0;
        char *handle;
 
-       id = conn->msg_id++;
+       id = msg_id++;
 
        buffer_init(&msg);
        buffer_put_char(&msg, SSH2_FXP_OPENDIR);
        buffer_put_int(&msg, id);
        buffer_put_cstring(&msg, path);
-       send_msg(conn->fd_out, &msg);
+       send_msg(fd_out, &msg);
 
        buffer_clear(&msg);
 
-       handle = get_handle(conn->fd_in, id, &handle_len);
+       handle = get_handle(fd_in, id, &handle_len);
        if (handle == NULL)
                return(-1);
 
@@ -333,29 +308,32 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
                *dir = xmalloc(sizeof(**dir));
                (*dir)[0] = NULL;
        }
+       
 
-       for (; !interrupted;) {
-               id = expected_id = conn->msg_id++;
+       for(;;) {
+               int count;
 
-               debug3("Sending SSH2_FXP_READDIR I:%u", id);
+               id = expected_id = msg_id++;
+
+               debug3("Sending SSH2_FXP_READDIR I:%d", id);
 
                buffer_clear(&msg);
                buffer_put_char(&msg, SSH2_FXP_READDIR);
                buffer_put_int(&msg, id);
                buffer_put_string(&msg, handle, handle_len);
-               send_msg(conn->fd_out, &msg);
+               send_msg(fd_out, &msg);
 
                buffer_clear(&msg);
 
-               get_msg(conn->fd_in, &msg);
+               get_msg(fd_in, &msg);
 
                type = buffer_get_char(&msg);
                id = buffer_get_int(&msg);
 
-               debug3("Received reply T:%u I:%u", type, id);
+               debug3("Received reply T:%d I:%d", type, id);
 
                if (id != expected_id)
-                       fatal("ID mismatch (%u != %u)", id, expected_id);
+                       fatal("ID mismatch (%d != %d)", id, expected_id);
 
                if (type == SSH2_FXP_STATUS) {
                        int status = buffer_get_int(&msg);
@@ -367,19 +345,18 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
                        } else {
                                error("Couldn't read directory: %s",
                                    fx2txt(status));
-                               do_close(conn, handle, handle_len);
-                               xfree(handle);
+                               do_close(fd_in, fd_out, handle, handle_len);
                                return(status);
                        }
                } else if (type != SSH2_FXP_NAME)
-                       fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
+                       fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
                            SSH2_FXP_NAME, type);
 
                count = buffer_get_int(&msg);
                if (count == 0)
                        break;
                debug3("Received %d SSH2_FXP_NAME responses", count);
-               for (i = 0; i < count; i++) {
+               for(i = 0; i < count; i++) {
                        char *filename, *longname;
                        Attrib *a;
 
@@ -406,30 +383,29 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
        }
 
        buffer_free(&msg);
-       do_close(conn, handle, handle_len);
+       do_close(fd_in, fd_out, handle, handle_len);
        xfree(handle);
 
-       /* Don't return partial matches on interrupt */
-       if (interrupted && dir != NULL && *dir != NULL) {
-               free_sftp_dirents(*dir);
-               *dir = xmalloc(sizeof(**dir));
-               **dir = NULL;
-       }
-
        return(0);
 }
 
 int
-do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
+do_ls(int fd_in, int fd_out, char *path)
 {
-       return(do_lsreaddir(conn, path, 0, dir));
+       return(do_lsreaddir(fd_in, fd_out, path, 1, NULL));
+}
+
+int
+do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir)
+{
+       return(do_lsreaddir(fd_in, fd_out, path, 0, dir));
 }
 
 void free_sftp_dirents(SFTP_DIRENT **s)
 {
        int i;
-
-       for (i = 0; s[i]; i++) {
+       
+       for(i = 0; s[i]; i++) {
                xfree(s[i]->filename);
                xfree(s[i]->longname);
                xfree(s[i]);
@@ -438,31 +414,30 @@ void free_sftp_dirents(SFTP_DIRENT **s)
 }
 
 int
-do_rm(struct sftp_conn *conn, char *path)
+do_rm(int fd_in, int fd_out, char *path)
 {
        u_int status, id;
 
        debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
 
-       id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
-           strlen(path));
-       status = get_status(conn->fd_in, id);
+       id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path));
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't delete file: %s", fx2txt(status));
        return(status);
 }
 
 int
-do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
+do_mkdir(int fd_in, int fd_out, char *path, Attrib *a)
 {
        u_int status, id;
 
-       id = conn->msg_id++;
-       send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
+       id = msg_id++;
+       send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path,
            strlen(path), a);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't create directory: %s", fx2txt(status));
 
@@ -470,15 +445,14 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
 }
 
 int
-do_rmdir(struct sftp_conn *conn, char *path)
+do_rmdir(int fd_in, int fd_out, char *path)
 {
        u_int status, id;
 
-       id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
-           strlen(path));
+       id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path));
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't remove directory: %s", fx2txt(status));
 
@@ -486,61 +460,45 @@ do_rmdir(struct sftp_conn *conn, char *path)
 }
 
 Attrib *
-do_stat(struct sftp_conn *conn, char *path, int quiet)
+do_stat(int fd_in, int fd_out, char *path, int quiet)
 {
        u_int id;
 
-       id = conn->msg_id++;
-
-       send_string_request(conn->fd_out, id,
-           conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
-           path, strlen(path));
-
-       return(get_decode_stat(conn->fd_in, id, quiet));
+       id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
+       return(get_decode_stat(fd_in, id, quiet));
 }
 
 Attrib *
-do_lstat(struct sftp_conn *conn, char *path, int quiet)
+do_lstat(int fd_in, int fd_out, char *path, int quiet)
 {
        u_int id;
 
-       if (conn->version == 0) {
-               if (quiet)
-                       debug("Server version does not support lstat operation");
-               else
-                       logit("Server version does not support lstat operation");
-               return(do_stat(conn, path, quiet));
-       }
-
-       id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
-           strlen(path));
-
-       return(get_decode_stat(conn->fd_in, id, quiet));
+       id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
+       return(get_decode_stat(fd_in, id, quiet));
 }
 
 Attrib *
-do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
+do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet)
 {
        u_int id;
 
-       id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
-           handle_len);
-
-       return(get_decode_stat(conn->fd_in, id, quiet));
+       id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
+       return(get_decode_stat(fd_in, id, quiet));
 }
 
 int
-do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
+do_setstat(int fd_in, int fd_out, char *path, Attrib *a)
 {
        u_int status, id;
 
-       id = conn->msg_id++;
-       send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
+       id = msg_id++;
+       send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path,
            strlen(path), a);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't setstat on \"%s\": %s", path,
                    fx2txt(status));
@@ -549,16 +507,16 @@ do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
 }
 
 int
-do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
+do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len,
     Attrib *a)
 {
        u_int status, id;
 
-       id = conn->msg_id++;
-       send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
+       id = msg_id++;
+       send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle,
            handle_len, a);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
                error("Couldn't fsetstat: %s", fx2txt(status));
 
@@ -566,25 +524,24 @@ do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
 }
 
 char *
-do_realpath(struct sftp_conn *conn, char *path)
+do_realpath(int fd_in, int fd_out, char *path)
 {
        Buffer msg;
        u_int type, expected_id, count, id;
        char *filename, *longname;
        Attrib *a;
 
-       expected_id = id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
-           strlen(path));
+       expected_id = id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path));
 
        buffer_init(&msg);
 
-       get_msg(conn->fd_in, &msg);
+       get_msg(fd_in, &msg);
        type = buffer_get_char(&msg);
        id = buffer_get_int(&msg);
 
        if (id != expected_id)
-               fatal("ID mismatch (%u != %u)", id, expected_id);
+               fatal("ID mismatch (%d != %d)", id, expected_id);
 
        if (type == SSH2_FXP_STATUS) {
                u_int status = buffer_get_int(&msg);
@@ -592,7 +549,7 @@ do_realpath(struct sftp_conn *conn, char *path)
                error("Couldn't canonicalise: %s", fx2txt(status));
                return(NULL);
        } else if (type != SSH2_FXP_NAME)
-               fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
+               fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
                    SSH2_FXP_NAME, type);
 
        count = buffer_get_int(&msg);
@@ -613,7 +570,7 @@ do_realpath(struct sftp_conn *conn, char *path)
 }
 
 int
-do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
+do_rename(int fd_in, int fd_out, char *oldpath, char *newpath)
 {
        Buffer msg;
        u_int status, id;
@@ -621,76 +578,70 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
        buffer_init(&msg);
 
        /* Send rename request */
-       id = conn->msg_id++;
+       id = msg_id++;
        buffer_put_char(&msg, SSH2_FXP_RENAME);
        buffer_put_int(&msg, id);
        buffer_put_cstring(&msg, oldpath);
        buffer_put_cstring(&msg, newpath);
-       send_msg(conn->fd_out, &msg);
+       send_msg(fd_out, &msg);
        debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
            newpath);
        buffer_free(&msg);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
-               error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
-                   newpath, fx2txt(status));
+               error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
+                   fx2txt(status));
 
        return(status);
 }
 
 int
-do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
+do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath)
 {
        Buffer msg;
        u_int status, id;
 
-       if (conn->version < 3) {
-               error("This server does not support the symlink operation");
-               return(SSH2_FX_OP_UNSUPPORTED);
-       }
-
        buffer_init(&msg);
 
-       /* Send symlink request */
-       id = conn->msg_id++;
+       /* Send rename request */
+       id = msg_id++;
        buffer_put_char(&msg, SSH2_FXP_SYMLINK);
        buffer_put_int(&msg, id);
        buffer_put_cstring(&msg, oldpath);
        buffer_put_cstring(&msg, newpath);
-       send_msg(conn->fd_out, &msg);
+       send_msg(fd_out, &msg);
        debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
            newpath);
        buffer_free(&msg);
 
-       status = get_status(conn->fd_in, id);
+       status = get_status(fd_in, id);
        if (status != SSH2_FX_OK)
-               error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
-                   newpath, fx2txt(status));
+               error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
+                   fx2txt(status));
 
        return(status);
 }
 
 char *
-do_readlink(struct sftp_conn *conn, char *path)
+do_readlink(int fd_in, int fd_out, char *path)
 {
        Buffer msg;
        u_int type, expected_id, count, id;
        char *filename, *longname;
        Attrib *a;
 
-       expected_id = id = conn->msg_id++;
-       send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
-           strlen(path));
+       expected_id = id = msg_id++;
+       send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path));
 
        buffer_init(&msg);
 
-       get_msg(conn->fd_in, &msg);
+       get_msg(fd_in, &msg);
        type = buffer_get_char(&msg);
        id = buffer_get_int(&msg);
 
        if (id != expected_id)
-               fatal("ID mismatch (%u != %u)", id, expected_id);
+               fatal("ID mismatch (%d != %d)", id, expected_id);
 
        if (type == SSH2_FXP_STATUS) {
                u_int status = buffer_get_int(&msg);
@@ -698,7 +649,7 @@ do_readlink(struct sftp_conn *conn, char *path)
                error("Couldn't readlink: %s", fx2txt(status));
                return(NULL);
        } else if (type != SSH2_FXP_NAME)
-               fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
+               fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
                    SSH2_FXP_NAME, type);
 
        count = buffer_get_int(&msg);
@@ -718,284 +669,162 @@ do_readlink(struct sftp_conn *conn, char *path)
        return(filename);
 }
 
-static void
-send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
-    char *handle, u_int handle_len)
-{
-       Buffer msg;
-
-       buffer_init(&msg);
-       buffer_clear(&msg);
-       buffer_put_char(&msg, SSH2_FXP_READ);
-       buffer_put_int(&msg, id);
-       buffer_put_string(&msg, handle, handle_len);
-       buffer_put_int64(&msg, offset);
-       buffer_put_int(&msg, len);
-       send_msg(fd_out, &msg);
-       buffer_free(&msg);
-}
-
 int
-do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
+do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
     int pflag)
 {
-       Attrib junk, *a;
-       Buffer msg;
+       int local_fd;
+       u_int expected_id, handle_len, mode, type, id;
+       u_int64_t offset;
        char *handle;
-       int local_fd, status = 0, write_error;
-       int read_error, write_errno;
-       u_int64_t offset, size;
-       u_int handle_len, mode, type, id, buflen, num_req, max_req;
-       off_t progress_counter;
-       struct request {
-               u_int id;
-               u_int len;
-               u_int64_t offset;
-               TAILQ_ENTRY(request) tq;
-       };
-       TAILQ_HEAD(reqhead, request) requests;
-       struct request *req;
-
-       TAILQ_INIT(&requests);
+       Buffer msg;
+       Attrib junk, *a;
+       int status;
 
-       a = do_stat(conn, remote_path, 0);
+       a = do_stat(fd_in, fd_out, remote_path, 0);
        if (a == NULL)
                return(-1);
 
        /* XXX: should we preserve set[ug]id? */
        if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
-               mode = a->perm & 0777;
+               mode = S_IWRITE | (a->perm & 0777);
        else
                mode = 0666;
 
        if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
-           (!S_ISREG(a->perm))) {
-               error("Cannot download non-regular file: %s", remote_path);
+           (a->perm & S_IFDIR)) {
+               error("Cannot download a directory: %s", remote_path);
                return(-1);
        }
 
-       if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
-               size = a->size;
-       else
-               size = 0;
+       local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+       if (local_fd == -1) {
+               error("Couldn't open local file \"%s\" for writing: %s",
+                   local_path, strerror(errno));
+               return(-1);
+       }
 
-       buflen = conn->transfer_buflen;
        buffer_init(&msg);
 
        /* Send open request */
-       id = conn->msg_id++;
+       id = msg_id++;
        buffer_put_char(&msg, SSH2_FXP_OPEN);
        buffer_put_int(&msg, id);
        buffer_put_cstring(&msg, remote_path);
        buffer_put_int(&msg, SSH2_FXF_READ);
        attrib_clear(&junk); /* Send empty attributes */
        encode_attrib(&msg, &junk);
-       send_msg(conn->fd_out, &msg);
-       debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
+       send_msg(fd_out, &msg);
+       debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
 
-       handle = get_handle(conn->fd_in, id, &handle_len);
+       handle = get_handle(fd_in, id, &handle_len);
        if (handle == NULL) {
                buffer_free(&msg);
-               return(-1);
-       }
-
-       local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
-           mode | S_IWRITE);
-       if (local_fd == -1) {
-               error("Couldn't open local file \"%s\" for writing: %s",
-                   local_path, strerror(errno));
-               buffer_free(&msg);
-               xfree(handle);
+               close(local_fd);
                return(-1);
        }
 
        /* Read from remote and write to local */
-       write_error = read_error = write_errno = num_req = offset = 0;
-       max_req = 1;
-       progress_counter = 0;
-
-       if (showprogress && size != 0)
-               start_progress_meter(remote_path, size, &progress_counter);
-
-       while (num_req > 0 || max_req > 0) {
-               char *data;
+       offset = 0;
+       for(;;) {
                u_int len;
+               char *data;
 
-               /*
-                * Simulate EOF on interrupt: stop sending new requests and
-                * allow outstanding requests to drain gracefully
-                */
-               if (interrupted) {
-                       if (num_req == 0) /* If we haven't started yet... */
-                               break;
-                       max_req = 0;
-               }
+               id = expected_id = msg_id++;
 
-               /* Send some more requests */
-               while (num_req < max_req) {
-                       debug3("Request range %llu -> %llu (%d/%d)",
-                           (unsigned long long)offset,
-                           (unsigned long long)offset + buflen - 1,
-                           num_req, max_req);
-                       req = xmalloc(sizeof(*req));
-                       req->id = conn->msg_id++;
-                       req->len = buflen;
-                       req->offset = offset;
-                       offset += buflen;
-                       num_req++;
-                       TAILQ_INSERT_TAIL(&requests, req, tq);
-                       send_read_request(conn->fd_out, req->id, req->offset,
-                           req->len, handle, handle_len);
-               }
+               buffer_clear(&msg);
+               buffer_put_char(&msg, SSH2_FXP_READ);
+               buffer_put_int(&msg, id);
+               buffer_put_string(&msg, handle, handle_len);
+               buffer_put_int64(&msg, offset);
+               buffer_put_int(&msg, COPY_SIZE);
+               send_msg(fd_out, &msg);
+               debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
+                   id, (u_int64_t)offset, COPY_SIZE);
 
                buffer_clear(&msg);
-               get_msg(conn->fd_in, &msg);
+
+               get_msg(fd_in, &msg);
                type = buffer_get_char(&msg);
                id = buffer_get_int(&msg);
-               debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
-
-               /* Find the request in our queue */
-               for (req = TAILQ_FIRST(&requests);
-                   req != NULL && req->id != id;
-                   req = TAILQ_NEXT(req, tq))
-                       ;
-               if (req == NULL)
-                       fatal("Unexpected reply %u", id);
-
-               switch (type) {
-               case SSH2_FXP_STATUS:
+               debug3("Received reply T:%d I:%d", type, id);
+               if (id != expected_id)
+                       fatal("ID mismatch (%d != %d)", id, expected_id);
+               if (type == SSH2_FXP_STATUS) {
                        status = buffer_get_int(&msg);
-                       if (status != SSH2_FX_EOF)
-                               read_error = 1;
-                       max_req = 0;
-                       TAILQ_REMOVE(&requests, req, tq);
-                       xfree(req);
-                       num_req--;
-                       break;
-               case SSH2_FXP_DATA:
-                       data = buffer_get_string(&msg, &len);
-                       debug3("Received data %llu -> %llu",
-                           (unsigned long long)req->offset,
-                           (unsigned long long)req->offset + len - 1);
-                       if (len > req->len)
-                               fatal("Received more data than asked for "
-                                   "%u > %u", len, req->len);
-                       if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
-                           atomicio(vwrite, local_fd, data, len) != len) &&
-                           !write_error) {
-                               write_errno = errno;
-                               write_error = 1;
-                               max_req = 0;
-                       }
-                       progress_counter += len;
-                       xfree(data);
 
-                       if (len == req->len) {
-                               TAILQ_REMOVE(&requests, req, tq);
-                               xfree(req);
-                               num_req--;
-                       } else {
-                               /* Resend the request for the missing data */
-                               debug3("Short data block, re-requesting "
-                                   "%llu -> %llu (%2d)",
-                                   (unsigned long long)req->offset + len,
-                                   (unsigned long long)req->offset +
-                                   req->len - 1, num_req);
-                               req->id = conn->msg_id++;
-                               req->len -= len;
-                               req->offset += len;
-                               send_read_request(conn->fd_out, req->id,
-                                   req->offset, req->len, handle, handle_len);
-                               /* Reduce the request size */
-                               if (len < buflen)
-                                       buflen = MAX(MIN_READ_SIZE, len);
-                       }
-                       if (max_req > 0) { /* max_req = 0 iff EOF received */
-                               if (size > 0 && offset > size) {
-                                       /* Only one request at a time
-                                        * after the expected EOF */
-                                       debug3("Finish at %llu (%2d)",
-                                           (unsigned long long)offset,
-                                           num_req);
-                                       max_req = 1;
-                               } else if (max_req <= conn->num_requests) {
-                                       ++max_req;
-                               }
+                       if (status == SSH2_FX_EOF)
+                               break;
+                       else {
+                               error("Couldn't read from remote "
+                                   "file \"%s\" : %s", remote_path,
+                                    fx2txt(status));
+                               do_close(fd_in, fd_out, handle, handle_len);
+                               goto done;
                        }
-                       break;
-               default:
-                       fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
+               } else if (type != SSH2_FXP_DATA) {
+                       fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
                            SSH2_FXP_DATA, type);
                }
-       }
 
-       if (showprogress && size)
-               stop_progress_meter();
+               data = buffer_get_string(&msg, &len);
+               if (len > COPY_SIZE)
+                       fatal("Received more data than asked for %d > %d",
+                           len, COPY_SIZE);
 
-       /* Sanity check */
-       if (TAILQ_FIRST(&requests) != NULL)
-               fatal("Transfer complete, but requests still in queue");
+               debug3("In read loop, got %d offset %llu", len,
+                   (u_int64_t)offset);
+               if (atomicio(write, local_fd, data, len) != len) {
+                       error("Couldn't write to \"%s\": %s", local_path,
+                           strerror(errno));
+                       do_close(fd_in, fd_out, handle, handle_len);
+                       status = -1;
+                       xfree(data);
+                       goto done;
+               }
 
-       if (read_error) {
-               error("Couldn't read from remote file \"%s\" : %s",
-                   remote_path, fx2txt(status));
-               do_close(conn, handle, handle_len);
-       } else if (write_error) {
-               error("Couldn't write to \"%s\": %s", local_path,
-                   strerror(write_errno));
-               status = -1;
-               do_close(conn, handle, handle_len);
-       } else {
-               status = do_close(conn, handle, handle_len);
+               offset += len;
+               xfree(data);
+       }
+       status = do_close(fd_in, fd_out, handle, handle_len);
 
-               /* Override umask and utimes if asked */
+       /* Override umask and utimes if asked */
 #ifdef HAVE_FCHMOD
-               if (pflag && fchmod(local_fd, mode) == -1)
-#else
-               if (pflag && chmod(local_path, mode) == -1)
+       if (pflag && fchmod(local_fd, mode) == -1)
+#else 
+       if (pflag && chmod(local_path, mode) == -1)
 #endif /* HAVE_FCHMOD */
-                       error("Couldn't set mode on \"%s\": %s", local_path,
+               error("Couldn't set mode on \"%s\": %s", local_path,
+                   strerror(errno));
+       if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
+               struct timeval tv[2];
+               tv[0].tv_sec = a->atime;
+               tv[1].tv_sec = a->mtime;
+               tv[0].tv_usec = tv[1].tv_usec = 0;
+               if (utimes(local_path, tv) == -1)
+                       error("Can't set times on \"%s\": %s", local_path,
                            strerror(errno));
-               if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
-                       struct timeval tv[2];
-                       tv[0].tv_sec = a->atime;
-                       tv[1].tv_sec = a->mtime;
-                       tv[0].tv_usec = tv[1].tv_usec = 0;
-                       if (utimes(local_path, tv) == -1)
-                               error("Can't set times on \"%s\": %s",
-                                   local_path, strerror(errno));
-               }
        }
+
+done:
        close(local_fd);
        buffer_free(&msg);
        xfree(handle);
-
-       return(status);
+       return status;
 }
 
 int
-do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
+do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
     int pflag)
 {
-       int local_fd, status;
-       u_int handle_len, id, type;
+       int local_fd;
+       u_int handle_len, id;
        u_int64_t offset;
-       char *handle, *data;
+       char *handle;
        Buffer msg;
        struct stat sb;
        Attrib a;
-       u_int32_t startid;
-       u_int32_t ackid;
-       struct outstanding_ack {
-               u_int id;
-               u_int len;
-               u_int64_t offset;
-               TAILQ_ENTRY(outstanding_ack) tq;
-       };
-       TAILQ_HEAD(ackhead, outstanding_ack) acks;
-       struct outstanding_ack *ack = NULL;
-
-       TAILQ_INIT(&acks);
+       int status;
 
        if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
                error("Couldn't open local file \"%s\" for reading: %s",
@@ -1008,11 +837,6 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
                close(local_fd);
                return(-1);
        }
-       if (!S_ISREG(sb.st_mode)) {
-               error("%s is not a regular file", local_path);
-               close(local_fd);
-               return(-1);
-       }
        stat_to_attrib(&sb, &a);
 
        a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
@@ -1024,134 +848,85 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
        buffer_init(&msg);
 
        /* Send open request */
-       id = conn->msg_id++;
+       id = msg_id++;
        buffer_put_char(&msg, SSH2_FXP_OPEN);
        buffer_put_int(&msg, id);
        buffer_put_cstring(&msg, remote_path);
        buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
        encode_attrib(&msg, &a);
-       send_msg(conn->fd_out, &msg);
-       debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
+       send_msg(fd_out, &msg);
+       debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
 
        buffer_clear(&msg);
 
-       handle = get_handle(conn->fd_in, id, &handle_len);
+       handle = get_handle(fd_in, id, &handle_len);
        if (handle == NULL) {
                close(local_fd);
                buffer_free(&msg);
                return(-1);
        }
 
-       startid = ackid = id + 1;
-       data = xmalloc(conn->transfer_buflen);
-
        /* Read from local and write to remote */
        offset = 0;
-       if (showprogress)
-               start_progress_meter(local_path, sb.st_size, &offset);
-
-       for (;;) {
+       for(;;) {
                int len;
+               char data[COPY_SIZE];
 
                /*
-                * Can't use atomicio here because it returns 0 on EOF,
-                * thus losing the last block of the file.
-                * Simulate an EOF on interrupt, allowing ACKs from the
-                * server to drain.
+                * Can't use atomicio here because it returns 0 on EOF, thus losing
+                * the last block of the file
                 */
-               if (interrupted)
-                       len = 0;
-               else do
-                       len = read(local_fd, data, conn->transfer_buflen);
+               do
+                       len = read(local_fd, data, COPY_SIZE);
                while ((len == -1) && (errno == EINTR || errno == EAGAIN));
 
                if (len == -1)
                        fatal("Couldn't read from \"%s\": %s", local_path,
                            strerror(errno));
-
-               if (len != 0) {
-                       ack = xmalloc(sizeof(*ack));
-                       ack->id = ++id;
-                       ack->offset = offset;
-                       ack->len = len;
-                       TAILQ_INSERT_TAIL(&acks, ack, tq);
-
-                       buffer_clear(&msg);
-                       buffer_put_char(&msg, SSH2_FXP_WRITE);
-                       buffer_put_int(&msg, ack->id);
-                       buffer_put_string(&msg, handle, handle_len);
-                       buffer_put_int64(&msg, offset);
-                       buffer_put_string(&msg, data, len);
-                       send_msg(conn->fd_out, &msg);
-                       debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
-                           id, (unsigned long long)offset, len);
-               } else if (TAILQ_FIRST(&acks) == NULL)
+               if (len == 0)
                        break;
 
-               if (ack == NULL)
-                       fatal("Unexpected ACK %u", id);
-
-               if (id == startid || len == 0 ||
-                   id - ackid >= conn->num_requests) {
-                       u_int r_id;
-
-                       buffer_clear(&msg);
-                       get_msg(conn->fd_in, &msg);
-                       type = buffer_get_char(&msg);
-                       r_id = buffer_get_int(&msg);
-
-                       if (type != SSH2_FXP_STATUS)
-                               fatal("Expected SSH2_FXP_STATUS(%d) packet, "
-                                   "got %d", SSH2_FXP_STATUS, type);
-
-                       status = buffer_get_int(&msg);
-                       debug3("SSH2_FXP_STATUS %d", status);
-
-                       /* Find the request in our queue */
-                       for (ack = TAILQ_FIRST(&acks);
-                           ack != NULL && ack->id != r_id;
-                           ack = TAILQ_NEXT(ack, tq))
-                               ;
-                       if (ack == NULL)
-                               fatal("Can't find request for ID %u", r_id);
-                       TAILQ_REMOVE(&acks, ack, tq);
-
-                       if (status != SSH2_FX_OK) {
-                               error("Couldn't write to remote file \"%s\": %s",
-                                   remote_path, fx2txt(status));
-                               do_close(conn, handle, handle_len);
-                               close(local_fd);
-                               xfree(data);
-                               xfree(ack);
-                               goto done;
-                       }
-                       debug3("In write loop, ack for %u %u bytes at %llu",
-                           ack->id, ack->len, (unsigned long long)ack->offset);
-                       ++ackid;
-                       xfree(ack);
+               buffer_clear(&msg);
+               buffer_put_char(&msg, SSH2_FXP_WRITE);
+               buffer_put_int(&msg, ++id);
+               buffer_put_string(&msg, handle, handle_len);
+               buffer_put_int64(&msg, offset);
+               buffer_put_string(&msg, data, len);
+               send_msg(fd_out, &msg);
+               debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
+                   id, (u_int64_t)offset, len);
+
+               status = get_status(fd_in, id);
+               if (status != SSH2_FX_OK) {
+                       error("Couldn't write to remote file \"%s\": %s",
+                           remote_path, fx2txt(status));
+                       do_close(fd_in, fd_out, handle, handle_len);
+                       close(local_fd);
+                       goto done;
                }
+               debug3("In write loop, got %d offset %llu", len,
+                   (u_int64_t)offset);
+
                offset += len;
        }
-       if (showprogress)
-               stop_progress_meter();
-       xfree(data);
 
        if (close(local_fd) == -1) {
                error("Couldn't close local file \"%s\": %s", local_path,
                    strerror(errno));
-               do_close(conn, handle, handle_len);
+               do_close(fd_in, fd_out, handle, handle_len);
                status = -1;
                goto done;
        }
 
        /* Override umask and utimes if asked */
        if (pflag)
-               do_fsetstat(conn, handle, handle_len, &a);
+               do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
 
-       status = do_close(conn, handle, handle_len);
+       status = do_close(fd_in, fd_out, handle, handle_len);
 
 done:
        xfree(handle);
        buffer_free(&msg);
-       return(status);
+       return status;
 }
+
This page took 0.107387 seconds and 4 git commands to generate.