diff options
author | des <des@FreeBSD.org> | 2014-01-30 10:56:49 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2014-01-30 10:56:49 +0000 |
commit | 45d0197dd79eceffb5bbc29f75199eb09af5a5f9 (patch) | |
tree | 6c519c109631364b652faced88b665da4b6058ae /sftp-client.c | |
parent | 31fac2f7108f657f455d571b94ed1a5c86d075b2 (diff) | |
download | FreeBSD-src-45d0197dd79eceffb5bbc29f75199eb09af5a5f9.zip FreeBSD-src-45d0197dd79eceffb5bbc29f75199eb09af5a5f9.tar.gz |
Vendor import of OpenSSH 6.5p1.
Diffstat (limited to 'sftp-client.c')
-rw-r--r-- | sftp-client.c | 193 |
1 files changed, 128 insertions, 65 deletions
diff --git a/sftp-client.c b/sftp-client.c index 2f97937..fc035f2 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.c,v 1.108 2013/11/08 00:39:15 djm Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.113 2014/01/17 00:21:06 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -42,6 +42,7 @@ #include <signal.h> #include <stdarg.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -76,6 +77,7 @@ struct sftp_conn { #define SFTP_EXT_STATVFS 0x00000002 #define SFTP_EXT_FSTATVFS 0x00000004 #define SFTP_EXT_HARDLINK 0x00000008 +#define SFTP_EXT_FSYNC 0x00000010 u_int exts; u_int64_t limit_kbps; struct bwlimit bwlimit_in, bwlimit_out; @@ -337,7 +339,8 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, Buffer msg; struct sftp_conn *ret; - ret = xmalloc(sizeof(*ret)); + ret = xcalloc(1, sizeof(*ret)); + ret->msg_id = 1; ret->fd_in = fd_in; ret->fd_out = fd_out; ret->transfer_buflen = transfer_buflen; @@ -387,6 +390,10 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, strcmp(value, "1") == 0) { ret->exts |= SFTP_EXT_HARDLINK; known = 1; + } else if (strcmp(name, "fsync@openssh.com") == 0 && + strcmp(value, "1") == 0) { + ret->exts |= SFTP_EXT_FSYNC; + known = 1; } if (known) { debug2("Server supports extension \"%s\" revision %s", @@ -447,12 +454,16 @@ do_close(struct sftp_conn *conn, char *handle, u_int handle_len) static int -do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, +do_lsreaddir(struct sftp_conn *conn, char *path, int print_flag, SFTP_DIRENT ***dir) { Buffer msg; u_int count, type, id, handle_len, i, expected_id, ents = 0; char *handle; + int status = SSH2_FX_FAILURE; + + if (dir) + *dir = NULL; id = conn->msg_id++; @@ -499,20 +510,12 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { - int status = buffer_get_int(&msg); - + status = buffer_get_int(&msg); debug3("Received SSH2_FXP_STATUS %d", status); - - if (status == SSH2_FX_EOF) { + if (status == SSH2_FX_EOF) break; - } else { - error("Couldn't read directory: %s", - fx2txt(status)); - do_close(conn, handle, handle_len); - free(handle); - buffer_free(&msg); - return(status); - } + error("Couldn't read directory: %s", fx2txt(status)); + goto out; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); @@ -529,7 +532,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); - if (printflag) + if (print_flag) printf("%s\n", longname); /* @@ -540,10 +543,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, if (strchr(filename, '/') != NULL) { error("Server sent suspect path \"%s\" " "during readdir of \"%s\"", filename, path); - goto next; - } - - if (dir) { + } else if (dir) { *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xcalloc(1, sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); @@ -551,24 +551,29 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, memcpy(&(*dir)[ents]->a, a, sizeof(*a)); (*dir)[++ents] = NULL; } - next: free(filename); free(longname); } } + status = 0; + out: buffer_free(&msg); do_close(conn, handle, handle_len); free(handle); - /* Don't return partial matches on interrupt */ - if (interrupted && dir != NULL && *dir != NULL) { + if (status != 0 && dir != NULL) { + /* Don't return results on error */ + free_sftp_dirents(*dir); + *dir = NULL; + } else if (interrupted && dir != NULL && *dir != NULL) { + /* Don't return partial matches on interrupt */ free_sftp_dirents(*dir); *dir = xcalloc(1, sizeof(**dir)); **dir = NULL; } - return 0; + return status; } int @@ -581,6 +586,8 @@ void free_sftp_dirents(SFTP_DIRENT **s) { int i; + if (s == NULL) + return; for (i = 0; s[i]; i++) { free(s[i]->filename); free(s[i]->longname); @@ -605,7 +612,7 @@ do_rm(struct sftp_conn *conn, char *path) } int -do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) +do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int print_flag) { u_int status, id; @@ -614,7 +621,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) strlen(path), a); status = get_status(conn, id); - if (status != SSH2_FX_OK && printflag) + if (status != SSH2_FX_OK && print_flag) error("Couldn't create directory: %s", fx2txt(status)); return(status); @@ -742,7 +749,7 @@ do_realpath(struct sftp_conn *conn, char *path) if (type == SSH2_FXP_STATUS) { u_int status = buffer_get_int(&msg); - error("Couldn't canonicalise: %s", fx2txt(status)); + error("Couldn't canonicalize: %s", fx2txt(status)); buffer_free(&msg); return NULL; } else if (type != SSH2_FXP_NAME) @@ -768,16 +775,18 @@ do_realpath(struct sftp_conn *conn, char *path) } int -do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) +do_rename(struct sftp_conn *conn, char *oldpath, char *newpath, + int force_legacy) { Buffer msg; u_int status, id; + int use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; buffer_init(&msg); /* Send rename request */ id = conn->msg_id++; - if ((conn->exts & SFTP_EXT_POSIX_RENAME)) { + if (use_ext) { buffer_put_char(&msg, SSH2_FXP_EXTENDED); buffer_put_int(&msg, id); buffer_put_cstring(&msg, "posix-rename@openssh.com"); @@ -789,8 +798,8 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) buffer_put_cstring(&msg, newpath); send_msg(conn, &msg); debug3("Sent message %s \"%s\" -> \"%s\"", - (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : - "SSH2_FXP_RENAME", oldpath, newpath); + use_ext ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME", + oldpath, newpath); buffer_free(&msg); status = get_status(conn, id); @@ -866,6 +875,36 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) return(status); } +int +do_fsync(struct sftp_conn *conn, char *handle, u_int handle_len) +{ + Buffer msg; + u_int status, id; + + /* Silently return if the extension is not supported */ + if ((conn->exts & SFTP_EXT_FSYNC) == 0) + return -1; + + buffer_init(&msg); + + /* Send fsync request */ + id = conn->msg_id++; + + buffer_put_char(&msg, SSH2_FXP_EXTENDED); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, "fsync@openssh.com"); + buffer_put_string(&msg, handle, handle_len); + send_msg(conn, &msg); + debug3("Sent message fsync@openssh.com I:%u", id); + buffer_free(&msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("Couldn't sync file: %s", fx2txt(status)); + + return status; +} + #ifdef notyet char * do_readlink(struct sftp_conn *conn, char *path) @@ -988,7 +1027,7 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, int do_download(struct sftp_conn *conn, char *remote_path, char *local_path, - Attrib *a, int pflag, int resume) + Attrib *a, int preserve_flag, int resume_flag, int fsync_flag) { Attrib junk; Buffer msg; @@ -1051,27 +1090,33 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, return(-1); } - local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? 0 : O_TRUNC), - mode | S_IWUSR); + local_fd = open(local_path, + O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); if (local_fd == -1) { error("Couldn't open local file \"%s\" for writing: %s", local_path, strerror(errno)); goto fail; } offset = highwater = 0; - if (resume) { + if (resume_flag) { if (fstat(local_fd, &st) == -1) { error("Unable to stat local file \"%s\": %s", local_path, strerror(errno)); goto fail; } - if ((size_t)st.st_size > size) { + if (st.st_size < 0) { + error("\"%s\" has negative size", local_path); + goto fail; + } + if ((u_int64_t)st.st_size > size) { error("Unable to resume download of \"%s\": " "local file is larger than remote", local_path); fail: do_close(conn, handle, handle_len); buffer_free(&msg); free(handle); + if (local_fd != -1) + close(local_fd); return -1; } offset = highwater = st.st_size; @@ -1209,7 +1254,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, fatal("Transfer complete, but requests still in queue"); /* Truncate at highest contiguous point to avoid holes on interrupt */ if (read_error || write_error || interrupted) { - if (reordered && resume) { + if (reordered && resume_flag) { error("Unable to resume download of \"%s\": " "server reordered requests", local_path); } @@ -1219,6 +1264,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, if (read_error) { error("Couldn't read from remote file \"%s\" : %s", remote_path, fx2txt(status)); + status = -1; do_close(conn, handle, handle_len); } else if (write_error) { error("Couldn't write to \"%s\": %s", local_path, @@ -1227,17 +1273,18 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, do_close(conn, handle, handle_len); } else { status = do_close(conn, handle, handle_len); - if (interrupted) + if (interrupted || status != SSH2_FX_OK) status = -1; /* Override umask and utimes if asked */ #ifdef HAVE_FCHMOD - if (pflag && fchmod(local_fd, mode) == -1) + if (preserve_flag && fchmod(local_fd, mode) == -1) #else - if (pflag && chmod(local_path, mode) == -1) + if (preserve_flag && 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)) { + if (preserve_flag && + (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[1].tv_sec = a->mtime; @@ -1246,6 +1293,12 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, error("Can't set times on \"%s\": %s", local_path, strerror(errno)); } + if (fsync_flag) { + debug("syncing \"%s\"", local_path); + if (fsync(local_fd) == -1) + error("Couldn't sync file \"%s\": %s", + local_path, strerror(errno)); + } } close(local_fd); buffer_free(&msg); @@ -1255,8 +1308,9 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, } static int -download_dir_internal(struct sftp_conn *conn, char *src, char *dst, - Attrib *dirattrib, int pflag, int printflag, int depth, int resume) +download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, + Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, + int fsync_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; @@ -1277,7 +1331,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, error("\"%s\" is not a directory", src); return -1; } - if (printflag) + if (print_flag) printf("Retrieving %s\n", src); if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) @@ -1308,12 +1362,13 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, strcmp(filename, "..") == 0) continue; if (download_dir_internal(conn, new_src, new_dst, - &(dir_entries[i]->a), pflag, printflag, - depth + 1, resume) == -1) + depth + 1, &(dir_entries[i]->a), preserve_flag, + print_flag, resume_flag, fsync_flag) == -1) ret = -1; } else if (S_ISREG(dir_entries[i]->a.perm) ) { if (do_download(conn, new_src, new_dst, - &(dir_entries[i]->a), pflag, resume) == -1) { + &(dir_entries[i]->a), preserve_flag, + resume_flag, fsync_flag) == -1) { error("Download of file %s to %s failed", new_src, new_dst); ret = -1; @@ -1325,7 +1380,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, free(new_src); } - if (pflag) { + if (preserve_flag) { if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { struct timeval tv[2]; tv[0].tv_sec = dirattrib->atime; @@ -1346,25 +1401,26 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int download_dir(struct sftp_conn *conn, char *src, char *dst, - Attrib *dirattrib, int pflag, int printflag, int resume) + Attrib *dirattrib, int preserve_flag, int print_flag, + int resume_flag, int fsync_flag) { char *src_canon; int ret; if ((src_canon = do_realpath(conn, src)) == NULL) { - error("Unable to canonicalise path \"%s\"", src); + error("Unable to canonicalize path \"%s\"", src); return -1; } - ret = download_dir_internal(conn, src_canon, dst, - dirattrib, pflag, printflag, 0, resume); + ret = download_dir_internal(conn, src_canon, dst, 0, + dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag); free(src_canon); return ret; } int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, - int pflag) + int preserve_flag, int fsync_flag) { int local_fd; int status = SSH2_FX_OK; @@ -1408,7 +1464,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 0777; - if (!pflag) + if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; buffer_init(&msg); @@ -1537,9 +1593,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, } /* Override umask and utimes if asked */ - if (pflag) + if (preserve_flag) do_fsetstat(conn, handle, handle_len, &a); + if (fsync_flag) + (void)do_fsync(conn, handle, handle_len); + if (do_close(conn, handle, handle_len) != SSH2_FX_OK) status = -1; free(handle); @@ -1548,8 +1607,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, } static int -upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, - int pflag, int printflag, int depth) +upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, + int preserve_flag, int print_flag, int fsync_flag) { int ret = 0, status; DIR *dirp; @@ -1572,7 +1631,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, error("\"%s\" is not a directory", src); return -1; } - if (printflag) + if (print_flag) printf("Entering %s\n", src); attrib_clear(&a); @@ -1580,7 +1639,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 01777; - if (!pflag) + if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; status = do_mkdir(conn, dst, &a, 0); @@ -1618,10 +1677,12 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, continue; if (upload_dir_internal(conn, new_src, new_dst, - pflag, printflag, depth + 1) == -1) + depth + 1, preserve_flag, print_flag, + fsync_flag) == -1) ret = -1; } else if (S_ISREG(sb.st_mode)) { - if (do_upload(conn, new_src, new_dst, pflag) == -1) { + if (do_upload(conn, new_src, new_dst, + preserve_flag, fsync_flag) == -1) { error("Uploading of file %s to %s failed!", new_src, new_dst); ret = -1; @@ -1639,18 +1700,20 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, } int -upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, - int pflag) +upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag, + int print_flag, int fsync_flag) { char *dst_canon; int ret; if ((dst_canon = do_realpath(conn, dst)) == NULL) { - error("Unable to canonicalise path \"%s\"", dst); + error("Unable to canonicalize path \"%s\"", dst); return -1; } - ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); + ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, + print_flag, fsync_flag); + free(dst_canon); return ret; } |