Implement sftp upload resume support.
authorlogan <logan@openbsd.org>
Mon, 21 Apr 2014 14:36:16 +0000 (14:36 +0000)
committerlogan <logan@openbsd.org>
Mon, 21 Apr 2014 14:36:16 +0000 (14:36 +0000)
OK from djm@, with input from guenther@, mlarkin@ and
okan@

usr.bin/ssh/sftp-client.c
usr.bin/ssh/sftp-client.h
usr.bin/ssh/sftp.c

index 3225306..5102562 100644 (file)
@@ -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 <djm@openbsd.org>
  *
@@ -1409,7 +1409,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;
@@ -1418,7 +1418,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 {
@@ -1456,6 +1456,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 */
@@ -1463,7 +1483,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);
@@ -1482,7 +1503,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);
@@ -1596,7 +1617,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;
@@ -1665,12 +1686,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;
@@ -1689,7 +1710,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;
@@ -1700,7 +1721,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;
index ba92ad0..967840b 100644 (file)
@@ -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 <djm@openbsd.org>
@@ -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 *);
index 61ad778..6d84926 100644 (file)
@@ -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 <djm@openbsd.org>
  *
@@ -69,7 +69,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 */
@@ -138,6 +138,7 @@ enum sftp_command {
        I_VERSION,
        I_PROGRESS,
        I_REGET,
+       I_REPUT
 };
 
 struct CMD {
@@ -180,6 +181,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  },
@@ -229,6 +231,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"
@@ -639,7 +642,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;
@@ -702,16 +705,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;
                }
@@ -1165,8 +1172,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;
@@ -1218,6 +1226,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)
@@ -1235,11 +1244,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)
@@ -1361,7 +1365,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;
@@ -1394,9 +1399,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);