X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/2b87da3b1fb7c5ca907cb65aa048fada4ad29803..762f5ea20dc6932e32901990938229c79a711031:/sftp-client.c diff --git a/sftp-client.c b/sftp-client.c index c64a43f7..ca5a4859 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -29,16 +29,14 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); +RCSID("$OpenBSD: sftp-client.c,v 1.19 2001/12/19 07:18:56 deraadt Exp $"); -#include "ssh.h" #include "buffer.h" #include "bufaux.h" #include "getput.h" #include "xmalloc.h" #include "log.h" #include "atomicio.h" -#include "pathnames.h" #include "sftp.h" #include "sftp-common.h" @@ -48,7 +46,10 @@ RCSID("$OpenBSD: sftp-client.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); /* XXX: what should this be? */ #define COPY_SIZE 8192 -void +/* Message ID */ +static u_int msg_id = 1; + +static void send_msg(int fd, Buffer *m) { int mlen = buffer_len(m); @@ -67,14 +68,16 @@ send_msg(int fd, Buffer *m) buffer_free(&oqueue); } -void +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 != 4) + if (len == 0) + fatal("Connection closed"); + else if (len == -1) fatal("Couldn't read packet: %s", strerror(errno)); msg_len = GET_32BIT(buf); @@ -83,7 +86,9 @@ get_msg(int fd, Buffer *m) while (msg_len) { len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); - if (len <= 0) + if (len == 0) + fatal("Connection closed"); + else if (len == -1) fatal("Couldn't read packet: %s", strerror(errno)); msg_len -= len; @@ -91,7 +96,7 @@ get_msg(int fd, Buffer *m) } } -void +static void send_string_request(int fd, u_int id, u_int code, char *s, u_int len) { @@ -106,7 +111,7 @@ send_string_request(int fd, u_int id, u_int code, char *s, buffer_free(&msg); } -void +static void send_string_attrs_request(int fd, u_int id, u_int code, char *s, u_int len, Attrib *a) { @@ -122,7 +127,7 @@ send_string_attrs_request(int fd, u_int id, u_int code, char *s, buffer_free(&msg); } -u_int +static u_int get_status(int fd, int expected_id) { Buffer msg; @@ -147,7 +152,7 @@ get_status(int fd, int expected_id) return(status); } -char * +static char * get_handle(int fd, u_int expected_id, u_int *len) { Buffer msg; @@ -176,8 +181,8 @@ get_handle(int fd, u_int expected_id, u_int *len) return(handle); } -Attrib * -get_decode_stat(int fd, u_int expected_id) +static Attrib * +get_decode_stat(int fd, u_int expected_id, int quiet) { Buffer msg; u_int type, id; @@ -195,7 +200,10 @@ get_decode_stat(int fd, u_int expected_id) if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); - error("Couldn't stat remote file: %s", fx2txt(status)); + if (quiet) + debug("Couldn't stat remote file: %s", fx2txt(status)); + else + 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", @@ -244,7 +252,8 @@ do_init(int fd_in, int fd_out) } buffer_free(&msg); - return(0); + + return(version); } int @@ -255,7 +264,7 @@ do_close(int fd_in, int fd_out, char *handle, u_int handle_len) buffer_init(&msg); - id = arc4random(); + id = msg_id++; buffer_put_char(&msg, SSH2_FXP_CLOSE); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); @@ -271,14 +280,16 @@ do_close(int fd_in, int fd_out, char *handle, u_int handle_len) return(status); } -int -do_ls(int fd_in, int fd_out, char *path) + +static int +do_lsreaddir(int fd_in, int fd_out, char *path, int printflag, + SFTP_DIRENT ***dir) { Buffer msg; - u_int type, id, handle_len, i, expected_id; + u_int type, id, handle_len, i, expected_id, ents = 0; char *handle; - id = arc4random(); + id = msg_id++; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_OPENDIR); @@ -292,10 +303,16 @@ do_ls(int fd_in, int fd_out, char *path) if (handle == NULL) return(-1); - for(;;) { + if (dir) { + ents = 0; + *dir = xmalloc(sizeof(**dir)); + (*dir)[0] = NULL; + } + + for (;;) { int count; - expected_id = ++id; + id = expected_id = msg_id++; debug3("Sending SSH2_FXP_READDIR I:%d", id); @@ -328,15 +345,17 @@ do_ls(int fd_in, int fd_out, char *path) error("Couldn't read directory: %s", fx2txt(status)); do_close(fd_in, fd_out, handle, handle_len); - return(NULL); + return(status); } } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); - debug3("Received %i SSH2_FXP_NAME responses", count); - for(i = 0; i < count; i++) { + if (count == 0) + break; + debug3("Received %d SSH2_FXP_NAME responses", count); + for (i = 0; i < count; i++) { char *filename, *longname; Attrib *a; @@ -344,7 +363,18 @@ do_ls(int fd_in, int fd_out, char *path) longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); - printf("%s\n", longname); + if (printflag) + printf("%s\n", longname); + + if (dir) { + *dir = xrealloc(*dir, sizeof(**dir) * + (ents + 2)); + (*dir)[ents] = xmalloc(sizeof(***dir)); + (*dir)[ents]->filename = xstrdup(filename); + (*dir)[ents]->longname = xstrdup(longname); + memcpy(&(*dir)[ents]->a, a, sizeof(*a)); + (*dir)[++ents] = NULL; + } xfree(filename); xfree(longname); @@ -358,6 +388,30 @@ do_ls(int fd_in, int fd_out, char *path) return(0); } +int +do_ls(int fd_in, int fd_out, char *path) +{ + return(do_lsreaddir(fd_in, fd_out, path, 1, NULL)); +} + +int +do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir) +{ + return(do_lsreaddir(fd_in, fd_out, path, 0, dir)); +} + +void free_sftp_dirents(SFTP_DIRENT **s) +{ + int i; + + for (i = 0; s[i]; i++) { + xfree(s[i]->filename); + xfree(s[i]->longname); + xfree(s[i]); + } + xfree(s); +} + int do_rm(int fd_in, int fd_out, char *path) { @@ -365,7 +419,7 @@ do_rm(int fd_in, int fd_out, char *path) debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); - id = arc4random(); + id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); status = get_status(fd_in, id); if (status != SSH2_FX_OK) @@ -378,7 +432,7 @@ do_mkdir(int fd_in, int fd_out, char *path, Attrib *a) { u_int status, id; - id = arc4random(); + id = msg_id++; send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path, strlen(path), a); @@ -394,7 +448,7 @@ do_rmdir(int fd_in, int fd_out, char *path) { u_int status, id; - id = arc4random(); + id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); status = get_status(fd_in, id); @@ -405,34 +459,33 @@ do_rmdir(int fd_in, int fd_out, char *path) } Attrib * -do_stat(int fd_in, int fd_out, char *path) +do_stat(int fd_in, int fd_out, char *path, int quiet) { u_int id; - id = arc4random(); + id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path)); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } Attrib * -do_lstat(int fd_in, int fd_out, char *path) +do_lstat(int fd_in, int fd_out, char *path, int quiet) { u_int id; - id = arc4random(); + id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } Attrib * -do_fstat(int fd_in, int fd_out, char *handle, - u_int handle_len) +do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet) { u_int id; - id = arc4random(); + id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } int @@ -440,7 +493,7 @@ do_setstat(int fd_in, int fd_out, char *path, Attrib *a) { u_int status, id; - id = arc4random(); + id = msg_id++; send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path, strlen(path), a); @@ -458,7 +511,7 @@ do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len, { u_int status, id; - id = arc4random(); + id = msg_id++; send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle, handle_len, a); @@ -477,9 +530,8 @@ do_realpath(int fd_in, int fd_out, char *path) char *filename, *longname; Attrib *a; - expected_id = id = arc4random(); - send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, - strlen(path)); + expected_id = id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path)); buffer_init(&msg); @@ -525,7 +577,7 @@ do_rename(int fd_in, int fd_out, char *oldpath, char *newpath) buffer_init(&msg); /* Send rename request */ - id = arc4random(); + id = msg_id++; buffer_put_char(&msg, SSH2_FXP_RENAME); buffer_put_int(&msg, id); buffer_put_cstring(&msg, oldpath); @@ -543,6 +595,79 @@ do_rename(int fd_in, int fd_out, char *oldpath, char *newpath) return(status); } +int +do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath) +{ + Buffer msg; + u_int status, id; + + buffer_init(&msg); + + /* Send rename request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_SYMLINK); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, oldpath); + buffer_put_cstring(&msg, newpath); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, + newpath); + buffer_free(&msg); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, + fx2txt(status)); + + return(status); +} + +char * +do_readlink(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, expected_id, count, id; + char *filename, *longname; + Attrib *a; + + expected_id = id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path)); + + buffer_init(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status = buffer_get_int(&msg); + + error("Couldn't readlink: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + debug3("SSH_FXP_READLINK %s -> %s", path, filename); + + xfree(longname); + + buffer_free(&msg); + + return(filename); +} + int do_download(int fd_in, int fd_out, char *remote_path, char *local_path, int pflag) @@ -553,8 +678,9 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, char *handle; Buffer msg; Attrib junk, *a; + int status; - a = do_stat(fd_in, fd_out, remote_path); + a = do_stat(fd_in, fd_out, remote_path, 0); if (a == NULL) return(-1); @@ -564,31 +690,23 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, else mode = 0666; + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + (a->perm & S_IFDIR)) { + error("Cannot download a directory: %s", remote_path); + return(-1); + } + local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); if (local_fd == -1) { error("Couldn't open local file \"%s\" for writing: %s", local_path, strerror(errno)); - return(errno); - } - - /* Override umask and utimes if asked */ - if (pflag && fchmod(local_fd, mode) == -1) - error("Couldn't set mode on \"%s\": %s", local_path, - strerror(errno)); - if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { - struct timeval tv; - - tv.tv_sec = a->atime; - tv.tv_usec = a->mtime; - if (utimes(local_path, &tv) == -1) - error("Can't set times on \"%s\": %s", local_path, - strerror(errno)); + return(-1); } buffer_init(&msg); /* Send open request */ - id = arc4random(); + id = msg_id++; buffer_put_char(&msg, SSH2_FXP_OPEN); buffer_put_int(&msg, id); buffer_put_cstring(&msg, remote_path); @@ -607,11 +725,11 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, /* Read from remote and write to local */ offset = 0; - for(;;) { + for (;;) { u_int len; char *data; - expected_id = ++id; + id = expected_id = msg_id++; buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_READ); @@ -621,7 +739,7 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, buffer_put_int(&msg, COPY_SIZE); send_msg(fd_out, &msg); debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u", - id, offset, COPY_SIZE); + id, (u_int64_t)offset, COPY_SIZE); buffer_clear(&msg); @@ -632,19 +750,16 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, if (id != expected_id) fatal("ID mismatch (%d != %d)", id, expected_id); if (type == SSH2_FXP_STATUS) { - int status = buffer_get_int(&msg); + status = buffer_get_int(&msg); if (status == SSH2_FX_EOF) break; else { error("Couldn't read from remote " "file \"%s\" : %s", remote_path, - fx2txt(status)); + fx2txt(status)); do_close(fd_in, fd_out, handle, handle_len); - xfree(handle); - close(local_fd); - buffer_free(&msg); - return(status); + goto done; } } else if (type != SSH2_FXP_DATA) { fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", @@ -656,26 +771,45 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, fatal("Received more data than asked for %d > %d", len, COPY_SIZE); - debug3("In read loop, got %d offset %lld", len, offset); + debug3("In read loop, got %d offset %llu", len, + (u_int64_t)offset); if (atomicio(write, local_fd, data, len) != len) { error("Couldn't write to \"%s\": %s", local_path, strerror(errno)); do_close(fd_in, fd_out, handle, handle_len); - xfree(handle); - close(local_fd); + status = -1; xfree(data); - buffer_free(&msg); - return(-1); + goto done; } offset += len; xfree(data); } - xfree(handle); - buffer_free(&msg); - close(local_fd); + status = do_close(fd_in, fd_out, handle, handle_len); - return(do_close(fd_in, fd_out, handle, handle_len)); + /* Override umask and utimes if asked */ +#ifdef HAVE_FCHMOD + if (pflag && fchmod(local_fd, mode) == -1) +#else + if (pflag && chmod(local_path, mode) == -1) +#endif /* HAVE_FCHMOD */ + error("Couldn't set mode on \"%s\": %s", local_path, + strerror(errno)); + if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { + struct timeval tv[2]; + tv[0].tv_sec = a->atime; + tv[1].tv_sec = a->mtime; + 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)); + } + +done: + close(local_fd); + buffer_free(&msg); + xfree(handle); + return status; } int @@ -689,6 +823,7 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, Buffer msg; struct stat sb; Attrib a; + int status; if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { error("Couldn't open local file \"%s\" for reading: %s", @@ -712,7 +847,7 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, buffer_init(&msg); /* Send open request */ - id = arc4random(); + id = msg_id++; buffer_put_char(&msg, SSH2_FXP_OPEN); buffer_put_int(&msg, id); buffer_put_cstring(&msg, remote_path); @@ -730,16 +865,11 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, return(-1); } - /* Override umask and utimes if asked */ - if (pflag) - do_fsetstat(fd_in, fd_out, handle, handle_len, &a); - /* Read from local and write to remote */ offset = 0; - for(;;) { + for (;;) { int len; char data[COPY_SIZE]; - u_int status; /* * Can't use atomicio here because it returns 0 on EOF, thus losing @@ -763,30 +893,39 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, buffer_put_string(&msg, data, len); send_msg(fd_out, &msg); debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", - id, offset, len); + id, (u_int64_t)offset, len); status = get_status(fd_in, id); if (status != SSH2_FX_OK) { error("Couldn't write to remote file \"%s\": %s", remote_path, fx2txt(status)); do_close(fd_in, fd_out, handle, handle_len); - xfree(handle); close(local_fd); - return(-1); + goto done; } - debug3("In write loop, got %d offset %lld", len, offset); + debug3("In write loop, got %d offset %llu", len, + (u_int64_t)offset); offset += len; } - xfree(handle); - buffer_free(&msg); if (close(local_fd) == -1) { error("Couldn't close local file \"%s\": %s", local_path, strerror(errno)); do_close(fd_in, fd_out, handle, handle_len); - return(-1); + status = -1; + goto done; } - return(do_close(fd_in, fd_out, handle, handle_len)); + /* Override umask and utimes if asked */ + if (pflag) + do_fsetstat(fd_in, fd_out, handle, handle_len, &a); + + status = do_close(fd_in, fd_out, handle, handle_len); + +done: + xfree(handle); + buffer_free(&msg); + return status; } +