]> andersk Git - openssh.git/blobdiff - sftp-client.c
*** empty log message ***
[openssh.git] / sftp-client.c
index 2f9faff18d1987cf4c16b48493a2329b13cd08e2..4ad6be8bdb5d7f01514d5900a6ba7514f08212d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
+ * Copyright (c) 2001-2003 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,9 +28,9 @@
 /* XXX: copy between two remote sites */
 
 #include "includes.h"
-RCSID("$OpenBSD: sftp-client.c,v 1.31 2002/04/06 00:30:08 djm Exp $");
+RCSID("$OpenBSD: sftp-client.c,v 1.42 2003/03/05 22:33:43 markus Exp $");
 
-#include "openbsd-compat/fake-queue.h"
+#include "openbsd-compat/sys-queue.h"
 
 #include "buffer.h"
 #include "bufaux.h"
@@ -38,14 +38,20 @@ RCSID("$OpenBSD: sftp-client.c,v 1.31 2002/04/06 00:30:08 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 +64,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));
+
+       /* Send length first */
+       PUT_32BIT(mlen, buffer_len(m));
+       if (atomicio(write, fd, mlen, sizeof(mlen)) <= 0)
+               fatal("Couldn't send packet: %s", strerror(errno));
 
-       len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
-       if (len <= 0)
+       if (atomicio(write, fd, buffer_ptr(m), buffer_len(m)) <= 0)
                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];
+       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)
-               fatal("Received message too long %d", msg_len);
+       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
@@ -113,7 +116,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 +132,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 +148,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 +174,14 @@ 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));
                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 +203,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);
 
@@ -212,7 +215,7 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
                        error("Couldn't stat remote file: %s", fx2txt(status));
                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 +227,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 +243,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 +298,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)
@@ -339,7 +343,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
 
                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 +358,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 +374,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);
@@ -413,12 +418,6 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
        return(0);
 }
 
-int
-do_ls(struct sftp_conn *conn, char *path)
-{
-       return(do_lsreaddir(conn, path, 1, NULL));
-}
-
 int
 do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
 {
@@ -508,7 +507,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));
        }
 
@@ -584,7 +583,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 +591,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);
@@ -665,7 +664,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);
@@ -690,7 +689,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 +697,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);
@@ -746,6 +745,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;
@@ -763,13 +763,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 +790,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,7 +798,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));
@@ -810,6 +811,16 @@ 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) {
+               if (size)
+                       start_progress_meter(remote_path, size,
+                           &progress_counter);
+               else
+                       printf("Fetching %s to %s\n", remote_path, local_path);
+       }
+
        while (num_req > 0 || max_req > 0) {
                char *data;
                u_int len;
@@ -835,7 +846,7 @@ 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);
@@ -862,14 +873,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(write, 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,11 +919,14 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
                        }
                        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");
@@ -935,7 +950,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
                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 +958,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);
@@ -988,6 +1003,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 +1026,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);
 
@@ -1022,6 +1042,11 @@ 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);
+       else
+               printf("Uploading %s to %s\n", local_path, remote_path);
+
        for (;;) {
                int len;
 
@@ -1051,8 +1076,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;
 
@@ -1081,23 +1106,27 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
                            ack = TAILQ_NEXT(ack, tq))
                                ;
                        if (ack == NULL)
-                               fatal("Can't find request for ID %d", r_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));
+                                   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 %d bytes at %llu",
+                       debug3("In write loop, ack for %u %u bytes at %llu",
                           ack->id, ack->len, (unsigned long long)ack->offset);
                        ++ackid;
-                       free(ack);
+                       xfree(ack);
                }
                offset += len;
        }
+       if (showprogress)
+               stop_progress_meter();
        xfree(data);
 
        if (close(local_fd) == -1) {
This page took 0.080789 seconds and 4 git commands to generate.