Index: sftp.c
===================================================================
RCS file: /cvs/openssh/sftp.c,v
retrieving revision 1.146
diff -u -p -r1.146 sftp.c
--- sftp.c	22 Apr 2012 01:25:47 -0000	1.146
+++ sftp.c	28 Jun 2012 04:30:11 -0000
@@ -86,6 +86,9 @@ 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 */
+int cresume = 0;
+
 /* When this option is set, the file transfers will always preserve times */
 int global_pflag = 0;
 
@@ -149,6 +152,7 @@ extern char *__progname;
 #define I_SYMLINK	21
 #define I_VERSION	22
 #define I_PROGRESS	23
+#define I_REGET		26
 
 struct CMD {
 	const char *c;
@@ -188,6 +192,7 @@ static const struct CMD cmds[] = {
 	{ "put",	I_PUT,		LOCAL	},
 	{ "pwd",	I_PWD,		REMOTE	},
 	{ "quit",	I_QUIT,		NOARGS	},
+	{ "reget",	I_REGET,	REMOTE	},
 	{ "rename",	I_RENAME,	REMOTE	},
 	{ "rm",		I_RM,		REMOTE	},
 	{ "rmdir",	I_RMDIR,	REMOTE	},
@@ -237,6 +242,7 @@ help(void)
 	    "                                   filesystem containing 'path'\n"
 	    "exit                               Quit sftp\n"
 	    "get [-Ppr] remote [local]          Download file\n"
+	    "reget remote [local]		Resume download 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"
@@ -517,7 +523,7 @@ pathname_is_dir(char *pathname)
 
 static int
 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
-    int pflag, int rflag)
+    int pflag, int rflag, int resume)
 {
 	char *abs_src = NULL;
 	char *abs_dst = NULL;
@@ -568,15 +574,19 @@ process_get(struct sftp_conn *conn, char
 			abs_dst = xstrdup(filename);
 		}
 		xfree(tmp);
-
-		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
+		if (cresume)
+			resume = cresume;
+		if (resume)
+			printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
+		else
+			printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
 		if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
 			if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 
 			    pflag || global_pflag, 1) == -1)
 				err = -1;
 		} else {
 			if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
-			    pflag || global_pflag) == -1)
+			    pflag || global_pflag, resume) == -1)
 				err = -1;
 		}
 		xfree(abs_dst);
@@ -1163,6 +1173,7 @@ parse_args(const char **cpp, int *pflag,
 	optidx = 1;
 	switch (cmdnum) {
 	case I_GET:
+	case I_REGET:
 	case I_PUT:
 		if ((optidx = parse_getput_flags(cmd, argv, argc,
 		    pflag, rflag)) == -1)
@@ -1288,7 +1299,8 @@ parse_dispatch_command(struct sftp_conn 
     int err_abort)
 {
 	char *path1, *path2, *tmp;
-	int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
+	int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0; 
+	int resume = 0;
 	int cmdnum, i;
 	unsigned long n_arg = 0;
 	Attrib a, *aa;
@@ -1315,7 +1327,14 @@ parse_dispatch_command(struct sftp_conn 
 		err = -1;
 		break;
 	case I_GET:
-		err = process_get(conn, path1, path2, *pwd, pflag, rflag);
+		resume = 0;
+		err = process_get(conn, path1, path2, *pwd, pflag, rflag,
+			 	  resume);
+		break;
+	case I_REGET:
+		resume = 1;
+		err = process_get(conn, path1, path2, *pwd, pflag, rflag,
+				  resume);
 		break;
 	case I_PUT:
 		err = process_put(conn, path1, path2, *pwd, pflag, rflag);
@@ -1920,10 +1939,14 @@ interactive_loop(struct sftp_conn *conn,
 		} else {
 			if (file2 == NULL)
 				snprintf(cmd, sizeof cmd, "get %s", dir);
-			else
-				snprintf(cmd, sizeof cmd, "get %s %s", dir,
-				    file2);
-
+			else {
+				if (cresume)
+					snprintf(cmd, sizeof cmd,
+					"reget %s %s", dir, file2);
+				else
+					snprintf(cmd, sizeof cmd,
+					"get %s %s", dir, file2);
+			}
 			err = parse_dispatch_command(conn, cmd,
 			    &remote_path, 1);
 			xfree(dir);
@@ -2111,7 +2134,7 @@ main(int argc, char **argv)
 	infile = stdin;
 
 	while ((ch = getopt(argc, argv,
-	    "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
+	    "1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
 		switch (ch) {
 		/* Passed through to ssh(1) */
 		case '4':
@@ -2148,6 +2171,9 @@ main(int argc, char **argv)
 			break;
 		case '2':
 			sshver = 2;
+			break;
+		case 'a':
+			cresume = 1;
 			break;
 		case 'B':
 			copy_buffer_len = strtol(optarg, &cp, 10);
Index: sftp-client.h
===================================================================
RCS file: /cvs/openssh/sftp-client.h,v
retrieving revision 1.20
diff -u -p -r1.20 sftp-client.h
--- sftp-client.h	4 Dec 2010 22:02:48 -0000	1.20
+++ sftp-client.h	28 Jun 2012 04:30:11 -0000
@@ -106,7 +106,7 @@ int do_symlink(struct sftp_conn *, char 
  * Download 'remote_path' to 'local_path'. Preserve permissions and times
  * if 'pflag' is set
  */
-int do_download(struct sftp_conn *, char *, char *, Attrib *, int);
+int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int);
 
 /*
  * Recursively download 'remote_directory' to 'local_directory'. Preserve 
Index: sftp-client.c
===================================================================
RCS file: /cvs/openssh/sftp-client.c,v
retrieving revision 1.107
diff -u -p -r1.107 sftp-client.c
--- sftp-client.c	22 Sep 2011 11:42:45 -0000	1.107
+++ sftp-client.c	28 Jun 2012 04:30:12 -0000
@@ -987,16 +987,17 @@ send_read_request(struct sftp_conn *conn
 
 int
 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
-    Attrib *a, int pflag)
+    Attrib *a, int pflag, int resume)
 {
 	Attrib junk;
 	Buffer msg;
 	char *handle;
-	int local_fd, status = 0, write_error;
+	int local_fd = -1, status = 0, write_error, bigger;
 	int read_error, write_errno;
-	u_int64_t offset, size;
+	u_int64_t offset = 0, size, highwater;
 	u_int handle_len, mode, type, id, buflen, num_req, max_req;
 	off_t progress_counter;
+	struct stat st;
 	struct request {
 		u_int id;
 		u_int len;
@@ -1049,11 +1050,36 @@ do_download(struct sftp_conn *conn, char
 		return(-1);
 	}
 
-	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
-	    mode | S_IWRITE);
+	if (resume) {
+		if (stat(local_path, &st) == -1) {
+			offset = 0;
+			highwater = 0;
+			local_fd = open(local_path, O_WRONLY | O_CREAT,
+					mode | S_IWRITE);
+		}
+		else {
+			if ((size_t)st.st_size > size) {
+				bigger = 1;
+				local_fd = -1;
+			}
+			else {
+				offset = st.st_size;
+				highwater = st.st_size;	
+				local_fd = open(local_path,
+						O_WRONLY | O_CREAT,
+						mode | S_IWRITE);
+			}
+		}
+	}
+	else
+		local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
+				mode | S_IWRITE);
 	if (local_fd == -1) {
-		error("Couldn't open local file \"%s\" for writing: %s",
-		    local_path, strerror(errno));
+		if (bigger)
+			error("destination file bigger than source file");
+		else
+			error("Couldn't open local file \"%s\" for writing: %s",
+		    	      local_path, strerror(errno));
 		do_close(conn, handle, handle_len);
 		buffer_free(&msg);
 		xfree(handle);
@@ -1061,9 +1087,9 @@ do_download(struct sftp_conn *conn, char
 	}
 
 	/* Read from remote and write to local */
-	write_error = read_error = write_errno = num_req = offset = 0;
+	write_error = read_error = write_errno = num_req = 0;
 	max_req = 1;
-	progress_counter = 0;
+	progress_counter = offset;
 
 	if (showprogress && size != 0)
 		start_progress_meter(remote_path, size, &progress_counter);
@@ -1138,6 +1164,9 @@ do_download(struct sftp_conn *conn, char
 				write_error = 1;
 				max_req = 0;
 			}
+			else if (req->offset <= highwater)
+				highwater = req->offset + len;
+
 			progress_counter += len;
 			xfree(data);
 
@@ -1186,6 +1215,11 @@ do_download(struct sftp_conn *conn, char
 	/* Sanity check */
 	if (TAILQ_FIRST(&requests) != NULL)
 		fatal("Transfer complete, but requests still in queue");
+	/* Truncate at highest contiguous point to avoid holes on interrupt */
+	if (read_error || write_error || interrupted) {
+		debug("truncating at %llu", highwater);
+		ftruncate(local_fd, highwater);
+	}
 
 	if (read_error) {
 		error("Couldn't read from remote file \"%s\" : %s",
@@ -1232,6 +1266,7 @@ download_dir_internal(struct sftp_conn *
 	SFTP_DIRENT **dir_entries;
 	char *filename, *new_src, *new_dst;
 	mode_t mode = 0777;
+	int resume = 0;
 
 	if (depth >= MAX_DIR_DEPTH) {
 		error("Maximum directory depth exceeded: %d levels", depth);
@@ -1283,7 +1318,7 @@ download_dir_internal(struct sftp_conn *
 				ret = -1;
 		} else if (S_ISREG(dir_entries[i]->a.perm) ) {
 			if (do_download(conn, new_src, new_dst,
-			    &(dir_entries[i]->a), pflag) == -1) {
+			    &(dir_entries[i]->a), pflag, resume) == -1) {
 				error("Download of file %s to %s failed",
 				    new_src, new_dst);
 				ret = -1;
@@ -1637,4 +1672,3 @@ path_append(char *p1, char *p2)
 
 	return(ret);
 }
-
