X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/ab33e02a07ff3d5c7d80f9ba60c2e00b8fb7332c..caba7de7d39b58f9f4fa9f33ca04531e0358e266:/sftp-client.c diff --git a/sftp-client.c b/sftp-client.c index 9fa8376e..b189422d 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,25 +1,18 @@ +/* $OpenBSD: sftp-client.c,v 1.80 2008/01/21 19:20:17 djm Exp $ */ /* - * Copyright (c) 2001,2002 Damien Miller. All rights reserved. + * Copyright (c) 2001-2004 Damien Miller * - * 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. + * 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. * - * 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. + * 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. */ /* XXX: memleaks */ @@ -28,22 +21,41 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.30 2002/04/01 22:07:17 markus Exp $"); -#include "openbsd-compat/fake-queue.h" +#include +#include +#include "openbsd-compat/sys-queue.h" +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include -#include "buffer.h" -#include "bufaux.h" -#include "getput.h" #include "xmalloc.h" +#include "buffer.h" #include "log.h" #include "atomicio.h" +#include "progressmeter.h" +#include "misc.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" -/* Minimum amount of data to read at at time */ +extern volatile sig_atomic_t interrupted; +extern int showprogress; + +/* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 struct sftp_conn { @@ -58,47 +70,48 @@ struct sftp_conn { static void send_msg(int fd, Buffer *m) { - int mlen = buffer_len(m); - int len; - Buffer oqueue; + u_char mlen[4]; + struct iovec iov[2]; + + if (buffer_len(m) > SFTP_MAX_MSG_LENGTH) + fatal("Outbound message too long %u", buffer_len(m)); - buffer_init(&oqueue); - buffer_put_int(&oqueue, mlen); - buffer_append(&oqueue, buffer_ptr(m), mlen); - buffer_consume(m, mlen); + /* Send length first */ + put_u32(mlen, buffer_len(m)); + iov[0].iov_base = mlen; + iov[0].iov_len = sizeof(mlen); + iov[1].iov_base = buffer_ptr(m); + iov[1].iov_len = buffer_len(m); - len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); - if (len <= 0) + if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) fatal("Couldn't send packet: %s", strerror(errno)); - buffer_free(&oqueue); + buffer_clear(m); } static void get_msg(int fd, Buffer *m) { - 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) + u_int msg_len; + + buffer_append_space(m, 4); + if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { + if (errno == EPIPE) fatal("Connection closed"); - else if (len == -1) + else fatal("Couldn't read packet: %s", strerror(errno)); + } + + msg_len = buffer_get_int(m); + if (msg_len > SFTP_MAX_MSG_LENGTH) + fatal("Received message too long %u", msg_len); - msg_len -= len; - buffer_append(m, buf, 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)); } } @@ -113,7 +126,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:%d I:%d", fd, code, id); + debug3("Sent message fd %d T:%u I:%u", fd, code, id); buffer_free(&msg); } @@ -129,12 +142,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:%d I:%d", fd, code, id); + debug3("Sent message fd %d T:%u I:%u", fd, code, id); buffer_free(&msg); } static u_int -get_status(int fd, int expected_id) +get_status(int fd, u_int expected_id) { Buffer msg; u_int type, id, status; @@ -145,15 +158,15 @@ get_status(int fd, int expected_id) id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", id, expected_id); if (type != SSH2_FXP_STATUS) - fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", + fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", SSH2_FXP_STATUS, type); status = buffer_get_int(&msg); buffer_free(&msg); - debug3("SSH2_FXP_STATUS %d", status); + debug3("SSH2_FXP_STATUS %u", status); return(status); } @@ -171,14 +184,15 @@ get_handle(int fd, u_int expected_id, u_int *len) id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", 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(%d) packet, got %d", + fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", SSH2_FXP_HANDLE, type); handle = buffer_get_string(&msg, len); @@ -200,9 +214,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:%d I:%d", type, id); + debug3("Received stat reply T:%u I:%u", type, id); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); @@ -210,9 +224,10 @@ 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(%d) packet, got %d", + fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", SSH2_FXP_ATTRS, type); } a = decode_attrib(&msg); @@ -224,7 +239,8 @@ get_decode_stat(int fd, u_int expected_id, int quiet) struct sftp_conn * do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) { - int type, version; + u_int type; + int version; Buffer msg; struct sftp_conn *ret; @@ -239,7 +255,7 @@ 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 %d)", + error("Invalid packet back from SSH2_FXP_INIT (type %u)", type); buffer_free(&msg); return(NULL); @@ -294,7 +310,7 @@ do_close(struct sftp_conn *conn, char *handle, u_int handle_len) 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:%d", id); + debug3("Sent message SSH2_FXP_CLOSE I:%u", id); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) @@ -311,7 +327,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, SFTP_DIRENT ***dir) { Buffer msg; - u_int type, id, handle_len, i, expected_id, ents = 0; + u_int count, type, id, handle_len, i, expected_id, ents = 0; char *handle; id = conn->msg_id++; @@ -334,12 +350,10 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, (*dir)[0] = NULL; } - for (;;) { - int count; - + for (; !interrupted;) { id = expected_id = conn->msg_id++; - debug3("Sending SSH2_FXP_READDIR I:%d", id); + debug3("Sending SSH2_FXP_READDIR I:%u", id); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_READDIR); @@ -354,10 +368,10 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, type = buffer_get_char(&msg); id = buffer_get_int(&msg); - debug3("Received reply T:%d I:%d", type, id); + debug3("Received reply T:%u I:%u", type, id); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); @@ -370,10 +384,11 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, error("Couldn't read directory: %s", fx2txt(status)); do_close(conn, handle, handle_len); + xfree(handle); return(status); } } else if (type != SSH2_FXP_NAME) - fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); @@ -392,8 +407,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, printf("%s\n", longname); if (dir) { - *dir = xrealloc(*dir, sizeof(**dir) * - (ents + 2)); + *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xmalloc(sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); (*dir)[ents]->longname = xstrdup(longname); @@ -410,13 +424,14 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, do_close(conn, handle, handle_len); xfree(handle); - return(0); -} + /* Don't return partial matches on interrupt */ + if (interrupted && dir != NULL && *dir != NULL) { + free_sftp_dirents(*dir); + *dir = xmalloc(sizeof(**dir)); + **dir = NULL; + } -int -do_ls(struct sftp_conn *conn, char *path) -{ - return(do_lsreaddir(conn, path, 1, NULL)); + return(0); } int @@ -508,7 +523,7 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet) if (quiet) debug("Server version does not support lstat operation"); else - log("Server version does not support lstat operation"); + logit("Server version does not support lstat operation"); return(do_stat(conn, path, quiet)); } @@ -519,6 +534,7 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet) return(get_decode_stat(conn->fd_in, id, quiet)); } +#ifdef notyet Attrib * do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) { @@ -530,6 +546,7 @@ do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) return(get_decode_stat(conn->fd_in, id, quiet)); } +#endif int do_setstat(struct sftp_conn *conn, char *path, Attrib *a) @@ -584,7 +601,7 @@ do_realpath(struct sftp_conn *conn, char *path) id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status = buffer_get_int(&msg); @@ -592,7 +609,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(%d) packet, got %d", + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); @@ -652,7 +669,7 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) buffer_init(&msg); - /* Send rename request */ + /* Send symlink request */ id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_SYMLINK); buffer_put_int(&msg, id); @@ -665,12 +682,13 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) - error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, + error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return(status); } +#ifdef notyet char * do_readlink(struct sftp_conn *conn, char *path) { @@ -690,7 +708,7 @@ do_readlink(struct sftp_conn *conn, char *path) id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%d != %d)", id, expected_id); + fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status = buffer_get_int(&msg); @@ -698,7 +716,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(%d) packet, got %d", + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); @@ -717,6 +735,7 @@ do_readlink(struct sftp_conn *conn, char *path) return(filename); } +#endif static void send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, @@ -742,10 +761,11 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, Attrib junk, *a; Buffer msg; char *handle; - int local_fd, status, num_req, max_req, write_error; + 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; + u_int handle_len, mode, type, id, buflen, num_req, max_req; + off_t progress_counter; struct request { u_int id; u_int len; @@ -763,13 +783,13 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, /* XXX: should we preserve set[ug]id? */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) - mode = S_IWRITE | (a->perm & 0777); + mode = a->perm & 0777; else mode = 0666; if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && - (a->perm & S_IFDIR)) { - error("Cannot download a directory: %s", remote_path); + (!S_ISREG(a->perm))) { + error("Cannot download non-regular file: %s", remote_path); return(-1); } @@ -790,7 +810,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, attrib_clear(&junk); /* Send empty attributes */ encode_attrib(&msg, &junk); send_msg(conn->fd_out, &msg); - debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); + debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); handle = get_handle(conn->fd_in, id, &handle_len); if (handle == NULL) { @@ -798,10 +818,12 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, return(-1); } - local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); + 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)); + do_close(conn, handle, handle_len); buffer_free(&msg); xfree(handle); return(-1); @@ -810,10 +832,25 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, /* 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; u_int len; + /* + * 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; + } + /* Send some more requests */ while (num_req < max_req) { debug3("Request range %llu -> %llu (%d/%d)", @@ -835,10 +872,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); - debug3("Received reply T:%d I:%d R:%d", type, id, max_req); + debug3("Received reply T:%u I:%u R:%d", type, id, max_req); /* Find the request in our queue */ - for(req = TAILQ_FIRST(&requests); + for (req = TAILQ_FIRST(&requests); req != NULL && req->id != id; req = TAILQ_NEXT(req, tq)) ; @@ -862,14 +899,15 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, (unsigned long long)req->offset + len - 1); if (len > req->len) fatal("Received more data than asked for " - "%d > %d", len, req->len); + "%u > %u", len, req->len); if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || - atomicio(write, local_fd, data, len) != len) && + 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) { @@ -900,18 +938,20 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, (unsigned long long)offset, num_req); max_req = 1; - } - else if (max_req < conn->num_requests + 1) { + } else if (max_req <= conn->num_requests) { ++max_req; } } break; default: - fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", + fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", SSH2_FXP_DATA, type); } } + if (showprogress && size) + stop_progress_meter(); + /* Sanity check */ if (TAILQ_FIRST(&requests) != NULL) fatal("Transfer complete, but requests still in queue"); @@ -931,11 +971,11 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, /* Override umask and utimes if asked */ #ifdef HAVE_FCHMOD if (pflag && fchmod(local_fd, mode) == -1) -#else +#else if (pflag && chmod(local_path, mode) == -1) #endif /* HAVE_FCHMOD */ error("Couldn't set mode on \"%s\": %s", local_path, - strerror(errno)); + strerror(errno)); if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { struct timeval tv[2]; tv[0].tv_sec = a->atime; @@ -943,7 +983,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 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)); + local_path, strerror(errno)); } } close(local_fd); @@ -957,9 +997,10 @@ int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, int pflag) { - int local_fd, status; + int local_fd; + int status = SSH2_FX_OK; u_int handle_len, id, type; - u_int64_t offset; + off_t offset; char *handle, *data; Buffer msg; struct stat sb; @@ -969,11 +1010,11 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, struct outstanding_ack { u_int id; u_int len; - u_int64_t offset; + off_t offset; TAILQ_ENTRY(outstanding_ack) tq; }; TAILQ_HEAD(ackhead, outstanding_ack) acks; - struct outstanding_ack *ack; + struct outstanding_ack *ack = NULL; TAILQ_INIT(&acks); @@ -988,6 +1029,11 @@ 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; @@ -1006,7 +1052,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *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:%d P:%s", id, remote_path); + debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); buffer_clear(&msg); @@ -1014,7 +1060,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, if (handle == NULL) { close(local_fd); buffer_free(&msg); - return(-1); + return -1; } startid = ackid = id + 1; @@ -1022,14 +1068,21 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, /* Read from local and write to remote */ offset = 0; + if (showprogress) + start_progress_meter(local_path, sb.st_size, &offset); + for (;;) { int len; /* - * Can't use atomicio here because it returns 0 on EOF, thus losing - * the last block of the file + * 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. */ - do + if (interrupted || status != SSH2_FX_OK) + len = 0; + else do len = read(local_fd, data, conn->transfer_buflen); while ((len == -1) && (errno == EINTR || errno == EAGAIN)); @@ -1051,8 +1104,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, buffer_put_int64(&msg, offset); buffer_put_string(&msg, data, len); send_msg(conn->fd_out, &msg); - debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", - id, (unsigned long long)offset, len); + debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", + id, (unsigned long long)offset, len); } else if (TAILQ_FIRST(&acks) == NULL) break; @@ -1061,10 +1114,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 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); - id = buffer_get_int(&msg); + r_id = buffer_get_int(&msg); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%d) packet, " @@ -1074,46 +1129,47 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, debug3("SSH2_FXP_STATUS %d", status); /* Find the request in our queue */ - for(ack = TAILQ_FIRST(&acks); - ack != NULL && ack->id != id; + 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 %d", id); + 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); - goto done; - } - debug3("In write loop, ack for %u %d bytes at %llu", - ack->id, ack->len, (unsigned long long)ack->offset); + debug3("In write loop, ack for %u %u bytes at %lld", + ack->id, ack->len, (long long)ack->offset); ++ackid; - free(ack); + xfree(ack); } offset += len; + if (offset < 0) + fatal("%s: offset < 0", __func__); } + buffer_free(&msg); + + if (showprogress) + stop_progress_meter(); xfree(data); + if (status != SSH2_FX_OK) { + error("Couldn't write to remote file \"%s\": %s", + remote_path, fx2txt(status)); + status = -1; + } + if (close(local_fd) == -1) { error("Couldn't close local file \"%s\": %s", local_path, strerror(errno)); - do_close(conn, handle, handle_len); status = -1; - goto done; } /* Override umask and utimes if asked */ if (pflag) do_fsetstat(conn, handle, handle_len, &a); - status = do_close(conn, handle, handle_len); - -done: + if (do_close(conn, handle, handle_len) != SSH2_FX_OK) + status = -1; xfree(handle); - buffer_free(&msg); - return(status); + + return status; }