X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/aff51935734441207923b8e59fbc3644fc4e7d2c..b0f6943a5dc83eaaaba15ebce897e162c1624033:/sftp-client.c diff --git a/sftp-client.c b/sftp-client.c index 8e657d1b..5ba4f0a9 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,25 +1,18 @@ +/* $OpenBSD: sftp-client.c,v 1.68 2006/07/17 01:31:09 stevesk Exp $ */ /* - * Copyright (c) 2001-2003 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,30 +21,37 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.45 2003/11/21 11:57:03 djm Exp $"); + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include +#include #include "openbsd-compat/sys-queue.h" #include "buffer.h" #include "bufaux.h" -#include "getput.h" #include "xmalloc.h" #include "log.h" #include "atomicio.h" #include "progressmeter.h" +#include "misc.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 */ +/* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 -/* Maximum packet size */ -#define MAX_MSG_LENGTH (256 * 1024) - struct sftp_conn { int fd_in; int fd_out; @@ -65,16 +65,19 @@ static void send_msg(int fd, Buffer *m) { u_char mlen[4]; + struct iovec iov[2]; - if (buffer_len(m) > MAX_MSG_LENGTH) + if (buffer_len(m) > SFTP_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)) <= 0) - fatal("Couldn't send packet: %s", strerror(errno)); - - if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0) + 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); + + if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) fatal("Couldn't send packet: %s", strerror(errno)); buffer_clear(m); @@ -83,26 +86,27 @@ send_msg(int fd, Buffer *m) static void get_msg(int fd, Buffer *m) { - ssize_t len; u_int msg_len; buffer_append_space(m, 4); - len = atomicio(read, fd, buffer_ptr(m), 4); - if (len == 0) - fatal("Connection closed"); - else if (len == -1) - fatal("Couldn't read packet: %s", strerror(errno)); + if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { + if (errno == EPIPE) + fatal("Connection closed"); + else + fatal("Couldn't read packet: %s", strerror(errno)); + } msg_len = buffer_get_int(m); - if (msg_len > MAX_MSG_LENGTH) + if (msg_len > SFTP_MAX_MSG_LENGTH) fatal("Received message too long %u", msg_len); buffer_append_space(m, msg_len); - len = atomicio(read, fd, buffer_ptr(m), msg_len); - if (len == 0) - fatal("Connection closed"); - else if (len == -1) - fatal("Read packet: %s", strerror(errno)); + if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { + if (errno == EPIPE) + fatal("Connection closed"); + else + fatal("Read packet: %s", strerror(errno)); + } } static void @@ -179,6 +183,7 @@ get_handle(int fd, u_int expected_id, u_int *len) 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", @@ -213,6 +218,7 @@ 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", @@ -315,7 +321,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++; @@ -338,9 +344,7 @@ 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:%u", id); @@ -397,8 +401,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); @@ -415,6 +418,13 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, do_close(conn, 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); } @@ -651,7 +661,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); @@ -741,10 +751,10 @@ 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; @@ -813,18 +823,23 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, max_req = 1; progress_counter = 0; - if (showprogress) { - if (size) - start_progress_meter(remote_path, size, - &progress_counter); - else - printf("Fetching %s to %s\n", remote_path, local_path); - } + 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)", @@ -849,7 +864,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 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)) ; @@ -912,8 +927,7 @@ 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; } } @@ -988,7 +1002,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, TAILQ_ENTRY(outstanding_ack) tq; }; TAILQ_HEAD(ackhead, outstanding_ack) acks; - struct outstanding_ack *ack; + struct outstanding_ack *ack = NULL; TAILQ_INIT(&acks); @@ -1044,17 +1058,19 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, offset = 0; if (showprogress) start_progress_meter(local_path, sb.st_size, &offset); - else - printf("Uploading %s to %s\n", local_path, remote_path); 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) + len = 0; + else do len = read(local_fd, data, conn->transfer_buflen); while ((len == -1) && (errno == EINTR || errno == EAGAIN)); @@ -1101,7 +1117,7 @@ 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); + for (ack = TAILQ_FIRST(&acks); ack != NULL && ack->id != r_id; ack = TAILQ_NEXT(ack, tq)) ; @@ -1119,7 +1135,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, goto done; } debug3("In write loop, ack for %u %u bytes at %llu", - ack->id, ack->len, (unsigned long long)ack->offset); + ack->id, ack->len, (unsigned long long)ack->offset); ++ackid; xfree(ack); }