Use ">=" instead of "== || >" for file size comparison as pointed out by Okan Demirmen.
Index: sftp-client.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/sftp-client.c,v retrieving revision 1.114 diff -u -p -u -p -r1.114 sftp-client.c --- sftp-client.c 31 Jan 2014 16:39:19 -0000 1.114 +++ sftp-client.c 16 Apr 2014 14:42:04 -0000 @@ -1409,7 +1409,7 @@ download_dir(struct sftp_conn *conn, cha 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 * 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 * 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 * 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 * 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 * 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 *co 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 *co 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 } 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: sftp-client.h =================================================================== RCS file: /cvs/src/usr.bin/ssh/sftp-client.h,v retrieving revision 1.24 diff -u -p -u -p -r1.24 sftp-client.h --- sftp-client.h 17 Oct 2013 00:30:13 -0000 1.24 +++ sftp-client.h 16 Apr 2014 14:42:04 -0000 @@ -120,13 +120,13 @@ int download_dir(struct sftp_conn *, cha * 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: sftp.c =================================================================== RCS file: /cvs/src/usr.bin/ssh/sftp.c,v retrieving revision 1.158 diff -u -p -u -p -r1.158 sftp.c --- sftp.c 20 Nov 2013 20:54:10 -0000 1.158 +++ sftp.c 16 Apr 2014 14:42:06 -0000 @@ -72,6 +72,9 @@ int global_rflag = 0; /* When this option is set, we resume download if possible */ int global_aflag = 0; +/* When this option is set, we resume upload if possible */ +int global_ruflag = 0; + /* When this option is set, the file transfers will always preserve times */ int global_pflag = 0; @@ -138,6 +141,7 @@ enum sftp_command { I_VERSION, I_PROGRESS, I_REGET, + I_REPUT }; struct CMD { @@ -180,6 +184,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 +234,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" @@ -341,7 +347,7 @@ make_absolute(char *p, char *pwd) static int parse_getput_flags(const char *cmd, char **argv, int argc, - int *aflag, int *fflag, int *pflag, int *rflag) + int *aflag, int *ruflag, int *fflag, int *pflag, int *rflag) { extern int opterr, optind, optopt, optreset; int ch; @@ -349,8 +355,8 @@ parse_getput_flags(const char *cmd, char optind = optreset = 1; opterr = 0; - *aflag = *fflag = *rflag = *pflag = 0; - while ((ch = getopt(argc, argv, "afPpRr")) != -1) { + *aflag = *ruflag = *fflag = *rflag = *pflag = 0; + while ((ch = getopt(argc, argv, "afPpuRr")) != -1) { switch (ch) { case 'a': *aflag = 1; @@ -366,6 +372,9 @@ parse_getput_flags(const char *cmd, char case 'R': *rflag = 1; break; + case 'u': + *ruflag = 1; + break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; @@ -639,7 +648,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 +711,19 @@ process_put(struct sftp_conn *conn, char } free(tmp); - if (!quiet) + 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 +1177,9 @@ makeargv(const char *arg, int *argcp, in } 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 *ruflag, + 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; @@ -1212,15 +1225,16 @@ parse_args(const char **cpp, int *ignore /* Get arguments and parse flags */ *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0; - *rflag = *sflag = 0; + *rflag = *ruflag = *sflag = 0; *path1 = *path2 = NULL; optidx = 1; 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) + aflag, ruflag, fflag, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { @@ -1235,11 +1249,6 @@ parse_args(const char **cpp, int *ignore /* 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 +1370,8 @@ parse_dispatch_command(struct sftp_conn 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, ruflag = 0, fflag = 0, hflag = 0, + iflag = 0; int lflag = 0, pflag = 0, rflag = 0, sflag = 0; int cmdnum, i; unsigned long n_arg = 0; @@ -1371,7 +1381,8 @@ parse_dispatch_command(struct sftp_conn glob_t g; path1 = path2 = NULL; - cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag, + cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &ruflag, &fflag, + &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2); if (ignore_errors != 0) err_abort = 0; @@ -1394,9 +1405,12 @@ parse_dispatch_command(struct sftp_conn err = process_get(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; + case I_REPUT: + ruflag = 1; + /* FALLTHROUGH */ case I_PUT: err = process_put(conn, path1, path2, *pwd, pflag, - rflag, fflag); + rflag, ruflag, fflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); @@ -2201,7 +2215,7 @@ main(int argc, char **argv) infile = stdin; while ((ch = getopt(argc, argv, - "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { + "1246afhpqruvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case '4': @@ -2292,6 +2306,9 @@ main(int argc, char **argv) case 'S': ssh_program = optarg; replacearg(&args, 0, "%s", ssh_program); + break; + case 'u': + global_ruflag = 1; break; case 'h': default: