]> andersk Git - gssapi-openssh.git/blobdiff - openssh/sftp-client.c
Import of OpenSSH 4.9p1
[gssapi-openssh.git] / openssh / sftp-client.c
index ffff0fe5affabf63a2762aac0c5bb2e419ce5080..69c6377859070b44555df41d8fb5fbe7eb63e286 100644 (file)
@@ -1,25 +1,18 @@
+/* $OpenBSD: sftp-client.c,v 1.81 2008/03/23 12:54:01 djm Exp $ */
 /*
- * Copyright (c) 2001-2003 Damien Miller.  All rights reserved.
+ * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
  *
- * 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 */
 /* XXX: copy between two remote sites */
 
 #include "includes.h"
-RCSID("$OpenBSD: sftp-client.c,v 1.44 2003/06/28 16:23:06 deraadt Exp $");
 
+#include <sys/types.h>
+#include <sys/param.h>
 #include "openbsd-compat/sys-queue.h"
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
 
-#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"
 
+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;
@@ -59,22 +65,27 @@ struct sftp_conn {
        u_int num_requests;
        u_int version;
        u_int msg_id;
+#define SFTP_EXT_POSIX_RENAME  1
+       u_int exts;
 };
 
 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));
+       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 (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0)
+       if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
                fatal("Couldn't send packet: %s", strerror(errno));
 
        buffer_clear(m);
@@ -83,26 +94,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 +191,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 +226,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",
@@ -227,7 +241,7 @@ 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)
 {
-       u_int type;
+       u_int type, exts = 0;
        int version;
        Buffer msg;
        struct sftp_conn *ret;
@@ -258,6 +272,8 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
                char *value = buffer_get_string(&msg, NULL);
 
                debug2("Init extension: \"%s\"", name);
+               if (strcmp(name, "posix-rename@openssh.com") == 0)
+                       exts |= SFTP_EXT_POSIX_RENAME;
                xfree(name);
                xfree(value);
        }
@@ -271,6 +287,7 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
        ret->num_requests = num_requests;
        ret->version = version;
        ret->msg_id = 1;
+       ret->exts = exts;
 
        /* Some filexfer v.0 servers don't support large packets */
        if (version == 0)
@@ -315,7 +332,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 +355,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 +412,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 +429,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);
 }
 
@@ -518,6 +539,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)
 {
@@ -529,6 +551,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)
@@ -621,13 +644,20 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
 
        /* Send rename request */
        id = conn->msg_id++;
-       buffer_put_char(&msg, SSH2_FXP_RENAME);
-       buffer_put_int(&msg, id);
+       if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
+               buffer_put_char(&msg, SSH2_FXP_EXTENDED);
+               buffer_put_int(&msg, id);
+               buffer_put_cstring(&msg, "posix-rename@openssh.com");
+       } else {
+               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);
-       debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
-           newpath);
+       debug3("Sent message %s \"%s\" -> \"%s\"",
+           (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
+           "SSH2_FXP_RENAME", oldpath, newpath);
        buffer_free(&msg);
 
        status = get_status(conn->fd_in, id);
@@ -651,7 +681,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);
@@ -670,6 +700,7 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
        return(status);
 }
 
+#ifdef notyet
 char *
 do_readlink(struct sftp_conn *conn, char *path)
 {
@@ -716,6 +747,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,
@@ -741,10 +773,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;
@@ -798,11 +830,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, 
+       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);
@@ -813,18 +846,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 +887,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 +950,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;
                                }
                        }
@@ -946,7 +983,7 @@ 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,
@@ -972,9 +1009,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;
@@ -984,11 +1022,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);
 
@@ -1034,7 +1072,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;
@@ -1044,17 +1082,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 || status != SSH2_FX_OK)
+                       len = 0;
+               else do
                        len = read(local_fd, data, conn->transfer_buflen);
                while ((len == -1) && (errno == EINTR || errno == EAGAIN));
 
@@ -1101,50 +1141,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);
+                       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);
+                       debug3("In write loop, ack for %u %u bytes at %lld",
+                           ack->id, ack->len, (long long)ack->offset);
                        ++ackid;
                        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;
 }
This page took 0.382973 seconds and 4 git commands to generate.