From d8accc0aa72656ba63d50937165c5ae49db1dcd6 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 15 May 2014 13:46:25 +1000 Subject: [PATCH] - logan@cvs.openbsd.org 2014/04/21 14:36:16 [sftp-client.c sftp-client.h sftp.c] Implement sftp upload resume support. OK from djm@, with input from guenther@, mlarkin@ and okan@ --- ChangeLog | 5 +++++ sftp-client.c | 41 +++++++++++++++++++++++++++++++---------- sftp-client.h | 6 +++--- sftp.c | 38 +++++++++++++++++++++++--------------- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index ff9c3cb46..011e8f995 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,11 @@ [dns.c dns.h ssh-keygen.c] Add support for SSHFP DNS records for ED25519 key types. OK from djm@ + - logan@cvs.openbsd.org 2014/04/21 14:36:16 + [sftp-client.c sftp-client.h sftp.c] + Implement sftp upload resume support. + OK from djm@, with input from guenther@, mlarkin@ and + okan@ 20140430 - (dtucker) [defines.h] Define __GNUC_PREREQ__ macro if we don't already diff --git a/sftp-client.c b/sftp-client.c index 2f5907c85..990b58d14 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.c,v 1.114 2014/01/31 16:39:19 tedu Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.115 2014/04/21 14:36:16 logan Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1420,7 +1420,7 @@ download_dir(struct sftp_conn *conn, char *src, char *dst, int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, - int preserve_flag, int fsync_flag) + int preserve_flag, int resume, int fsync_flag) { int local_fd; int status = SSH2_FX_OK; @@ -1429,7 +1429,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, char *handle, *data; Buffer msg; struct stat sb; - Attrib a; + Attrib a, *c = NULL; u_int32_t startid; u_int32_t ackid; struct outstanding_ack { @@ -1467,6 +1467,26 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + if (resume) { + /* Get remote file size if it exists */ + if ((c = do_stat(conn, remote_path, 0)) == NULL) { + close(local_fd); + return -1; + } + + if ((off_t)c->size >= sb.st_size) { + error("destination file bigger or same size as " + "source file"); + close(local_fd); + return -1; + } + + if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { + close(local_fd); + return -1; + } + } + buffer_init(&msg); /* Send open request */ @@ -1474,7 +1494,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, buffer_put_char(&msg, SSH2_FXP_OPEN); buffer_put_int(&msg, id); buffer_put_cstring(&msg, remote_path); - buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); + buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| + (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC)); encode_attrib(&msg, &a); send_msg(conn, &msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); @@ -1493,7 +1514,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, data = xmalloc(conn->transfer_buflen); /* Read from local and write to remote */ - offset = progress_counter = 0; + offset = progress_counter = (resume ? c->size : 0); if (showprogress) start_progress_meter(local_path, sb.st_size, &progress_counter); @@ -1608,7 +1629,7 @@ 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 depth, - int preserve_flag, int print_flag, int fsync_flag) + int preserve_flag, int print_flag, int resume, int fsync_flag) { int ret = 0, status; DIR *dirp; @@ -1677,12 +1698,12 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, continue; if (upload_dir_internal(conn, new_src, new_dst, - depth + 1, preserve_flag, print_flag, + depth + 1, preserve_flag, print_flag, resume, fsync_flag) == -1) ret = -1; } else if (S_ISREG(sb.st_mode)) { if (do_upload(conn, new_src, new_dst, - preserve_flag, fsync_flag) == -1) { + preserve_flag, resume, fsync_flag) == -1) { error("Uploading of file %s to %s failed!", new_src, new_dst); ret = -1; @@ -1701,7 +1722,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, int upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag, - int print_flag, int fsync_flag) + int print_flag, int resume, int fsync_flag) { char *dst_canon; int ret; @@ -1712,7 +1733,7 @@ upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag, } ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, - print_flag, fsync_flag); + print_flag, resume, fsync_flag); free(dst_canon); return ret; diff --git a/sftp-client.h b/sftp-client.h index ba92ad01a..967840b9c 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.24 2013/10/17 00:30:13 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.25 2014/04/21 14:36:16 logan Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller @@ -120,13 +120,13 @@ int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, * Upload 'local_path' to 'remote_path'. Preserve permissions and times * if 'pflag' is set */ -int do_upload(struct sftp_conn *, char *, char *, int, int); +int do_upload(struct sftp_conn *, char *, char *, int, int, int); /* * Recursively upload 'local_directory' to 'remote_directory'. Preserve * times if 'pflag' is set */ -int upload_dir(struct sftp_conn *, char *, char *, int, int, int); +int upload_dir(struct sftp_conn *, char *, char *, int, int, int, int); /* Concatenate paths, taking care of slashes. Caller must free result. */ char *path_append(char *, char *); diff --git a/sftp.c b/sftp.c index ad1f8c84d..e74bed5e0 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */ +/* $OpenBSD: sftp.c,v 1.159 2014/04/21 14:36:16 logan Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -88,7 +88,7 @@ int showprogress = 1; /* When this option is set, we always recursively download/upload directories */ int global_rflag = 0; -/* When this option is set, we resume download if possible */ +/* When this option is set, we resume download or upload if possible */ int global_aflag = 0; /* When this option is set, the file transfers will always preserve times */ @@ -159,6 +159,7 @@ enum sftp_command { I_VERSION, I_PROGRESS, I_REGET, + I_REPUT }; struct CMD { @@ -201,6 +202,7 @@ static const struct CMD cmds[] = { { "quit", I_QUIT, NOARGS }, { "reget", I_REGET, REMOTE }, { "rename", I_RENAME, REMOTE }, + { "reput", I_REPUT, LOCAL }, { "rm", I_RM, REMOTE }, { "rmdir", I_RMDIR, REMOTE }, { "symlink", I_SYMLINK, REMOTE }, @@ -250,6 +252,7 @@ help(void) "exit Quit sftp\n" "get [-Ppr] remote [local] Download file\n" "reget remote [local] Resume download file\n" + "reput [local] remote Resume upload file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" @@ -660,7 +663,7 @@ out: static int process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, - int pflag, int rflag, int fflag) + int pflag, int rflag, int resume, int fflag) { char *tmp_dst = NULL; char *abs_dst = NULL; @@ -723,16 +726,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, } free(tmp); - if (!quiet) + resume |= global_aflag; + if (!quiet && resume) + printf("Resuming upload of %s to %s\n", g.gl_pathv[i], + abs_dst); + else if (!quiet && !resume) printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (upload_dir(conn, g.gl_pathv[i], abs_dst, - pflag || global_pflag, 1, + pflag || global_pflag, 1, resume, fflag || global_fflag) == -1) err = -1; } else { if (do_upload(conn, g.gl_pathv[i], abs_dst, - pflag || global_pflag, + pflag || global_pflag, resume, fflag || global_fflag) == -1) err = -1; } @@ -1186,8 +1193,9 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, } static int -parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag, - int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag, +parse_args(const char **cpp, int *ignore_errors, int *aflag, + int *fflag, int *hflag, int *iflag, int *lflag, int *pflag, + int *rflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; @@ -1239,6 +1247,7 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag, switch (cmdnum) { case I_GET: case I_REGET: + case I_REPUT: case I_PUT: if ((optidx = parse_getput_flags(cmd, argv, argc, aflag, fflag, pflag, rflag)) == -1) @@ -1256,11 +1265,6 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag, /* Destination is not globbed */ undo_glob_escape(*path2); } - if (*aflag && cmdnum == I_PUT) { - /* XXX implement resume for uploads */ - error("Resume is not supported for uploads"); - return -1; - } break; case I_LINK: if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) @@ -1382,7 +1386,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; - int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0; + int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, + iflag = 0; int lflag = 0, pflag = 0, rflag = 0, sflag = 0; int cmdnum, i; unsigned long n_arg = 0; @@ -1415,9 +1420,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, err = process_get(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; + case I_REPUT: + aflag = 1; + /* FALLTHROUGH */ case I_PUT: err = process_put(conn, path1, path2, *pwd, pflag, - rflag, fflag); + rflag, aflag, fflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd);