]> andersk Git - openssh.git/blobdiff - sftp-client.c
- djm@cvs.openbsd.org 2004/03/30 12:41:56
[openssh.git] / sftp-client.c
index f7759681396bd64fd91ef5a83d45938cffe863fe..bf2eb43accd9667ef816e30b49f9b1b85e175a8f 100644 (file)
@@ -1,25 +1,17 @@
 /*
- * Copyright (c) 2001,2002 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 */
@@ -28,9 +20,9 @@
 /* XXX: copy between two remote sites */
 
 #include "includes.h"
-RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm Exp $");
+RCSID("$OpenBSD: sftp-client.c,v 1.48 2004/03/30 12:41:56 djm Exp $");
 
-#include "openbsd-compat/fake-queue.h"
+#include "openbsd-compat/sys-queue.h"
 
 #include "buffer.h"
 #include "bufaux.h"
@@ -38,14 +30,20 @@ RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm 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 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)
+
 struct sftp_conn {
        int fd_in;
        int fd_out;
@@ -58,48 +56,45 @@ struct sftp_conn {
 static void
 send_msg(int fd, Buffer *m)
 {
-       int mlen = buffer_len(m);
-       int len;
-       Buffer oqueue;
+       u_char mlen[4];
 
-       buffer_init(&oqueue);
-       buffer_put_int(&oqueue, mlen);
-       buffer_append(&oqueue, buffer_ptr(m), mlen);
-       buffer_consume(m, mlen);
+       if (buffer_len(m) > MAX_MSG_LENGTH)
+               fatal("Outbound message too long %u", buffer_len(m));
 
-       len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
-       if (len <= 0)
+       /* 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));
 
-       buffer_free(&oqueue);
+       if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0)
+               fatal("Couldn't send packet: %s", strerror(errno));
+
+       buffer_clear(m);
 }
 
 static void
 get_msg(int fd, Buffer *m)
 {
-       u_int len, msg_len;
-       unsigned char buf[4096];
+       ssize_t len;
+       u_int msg_len;
 
-       len = atomicio(read, fd, buf, 4);
+       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));
 
-       msg_len = GET_32BIT(buf);
-       if (msg_len > 256 * 1024)
+       msg_len = buffer_get_int(m);
+       if (msg_len > MAX_MSG_LENGTH)
                fatal("Received message too long %u", msg_len);
 
-       while (msg_len) {
-               len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
-               if (len == 0)
-                       fatal("Connection closed");
-               else if (len == -1)
-                       fatal("Couldn't read packet: %s", strerror(errno));
-
-               msg_len -= len;
-               buffer_append(m, buf, 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));
 }
 
 static void
@@ -371,6 +366,7 @@ 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)
@@ -503,7 +499,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));
        }
 
@@ -647,7 +643,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);
@@ -660,7 +656,7 @@ 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);
@@ -741,6 +737,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
        int read_error, write_errno;
        u_int64_t offset, size;
        u_int handle_len, mode, type, id, buflen;
+       off_t progress_counter;
        struct request {
                u_int id;
                u_int len;
@@ -758,13 +755,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);
        }
 
@@ -793,7 +790,8 @@ 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));
@@ -805,6 +803,11 @@ 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;
@@ -857,14 +860,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 "
-                                     "%u > %u", 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) {
@@ -907,6 +911,9 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
                }
        }
 
+       if (showprogress && size)
+               stop_progress_meter();
+
        /* Sanity check */
        if (TAILQ_FIRST(&requests) != NULL)
                fatal("Transfer complete, but requests still in queue");
@@ -926,11 +933,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;
@@ -938,7 +945,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);
@@ -983,6 +990,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;
@@ -1017,6 +1029,9 @@ 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;
 
@@ -1047,7 +1062,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
                        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);
+                           id, (unsigned long long)offset, len);
                } else if (TAILQ_FIRST(&acks) == NULL)
                        break;
 
@@ -1081,9 +1096,11 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
 
                        if (status != SSH2_FX_OK) {
                                error("Couldn't write to remote file \"%s\": %s",
-                                     remote_path, fx2txt(status));
+                                   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",
@@ -1093,6 +1110,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
                }
                offset += len;
        }
+       if (showprogress)
+               stop_progress_meter();
        xfree(data);
 
        if (close(local_fd) == -1) {
This page took 0.043519 seconds and 4 git commands to generate.