summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2013-07-25 11:56:52 +1000
committerDamien Miller <djm@mindrot.org>2013-07-25 11:56:52 +1000
commit0d032419ee6e1968fc1cb187af63bf3b77b506ea (patch)
treece2788365040e9ea188bd60c8bec87d410814017
parent98e27dcf581647b5bbe9780e8f59685d942d8ea3 (diff)
- djm@cvs.openbsd.org 2013/07/25 00:56:52
[sftp-client.c sftp-client.h sftp.1 sftp.c] sftp support for resuming partial downloads; patch mostly by Loganaden Velvindron/AfriNIC with some tweaks by me; feedback and ok dtucker@
-rw-r--r--ChangeLog4
-rw-r--r--sftp-client.c75
-rw-r--r--sftp-client.h6
-rw-r--r--sftp.128
-rw-r--r--sftp.c76
5 files changed, 133 insertions, 56 deletions
diff --git a/ChangeLog b/ChangeLog
index f799663fd..9552a9b81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -15,6 +15,10 @@
15 [ssh.c] 15 [ssh.c]
16 daemonise backgrounded (ControlPersist'ed) multiplexing master to ensure 16 daemonise backgrounded (ControlPersist'ed) multiplexing master to ensure
17 it is fully detached from its controlling terminal. based on debugging 17 it is fully detached from its controlling terminal. based on debugging
18 - djm@cvs.openbsd.org 2013/07/25 00:56:52
19 [sftp-client.c sftp-client.h sftp.1 sftp.c]
20 sftp support for resuming partial downloads; patch mostly by Loganaden
21 Velvindron/AfriNIC with some tweaks by me; feedback and ok dtucker@
18 22
1920130720 2320130720
20 - (djm) OpenBSD CVS Sync 24 - (djm) OpenBSD CVS Sync
diff --git a/sftp-client.c b/sftp-client.c
index ab035c713..cb4efd3ea 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.100 2013/06/01 22:34:50 dtucker Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.101 2013/07/25 00:56:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -112,7 +112,7 @@ send_msg(struct sftp_conn *conn, Buffer *m)
112 iov[1].iov_len = buffer_len(m); 112 iov[1].iov_len = buffer_len(m);
113 113
114 if (atomiciov6(writev, conn->fd_out, iov, 2, 114 if (atomiciov6(writev, conn->fd_out, iov, 2,
115 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != 115 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
116 buffer_len(m) + sizeof(mlen)) 116 buffer_len(m) + sizeof(mlen))
117 fatal("Couldn't send packet: %s", strerror(errno)); 117 fatal("Couldn't send packet: %s", strerror(errno));
118 118
@@ -988,16 +988,17 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
988 988
989int 989int
990do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 990do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
991 Attrib *a, int pflag) 991 Attrib *a, int pflag, int resume)
992{ 992{
993 Attrib junk; 993 Attrib junk;
994 Buffer msg; 994 Buffer msg;
995 char *handle; 995 char *handle;
996 int local_fd, status = 0, write_error; 996 int local_fd = -1, status = 0, write_error;
997 int read_error, write_errno; 997 int read_error, write_errno, reordered = 0;
998 u_int64_t offset, size; 998 u_int64_t offset = 0, size, highwater;
999 u_int handle_len, mode, type, id, buflen, num_req, max_req; 999 u_int handle_len, mode, type, id, buflen, num_req, max_req;
1000 off_t progress_counter; 1000 off_t progress_counter;
1001 struct stat st;
1001 struct request { 1002 struct request {
1002 u_int id; 1003 u_int id;
1003 u_int len; 1004 u_int len;
@@ -1050,21 +1051,36 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1050 return(-1); 1051 return(-1);
1051 } 1052 }
1052 1053
1053 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, 1054 local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? : O_TRUNC),
1054 mode | S_IWUSR); 1055 mode | S_IWUSR);
1055 if (local_fd == -1) { 1056 if (local_fd == -1) {
1056 error("Couldn't open local file \"%s\" for writing: %s", 1057 error("Couldn't open local file \"%s\" for writing: %s",
1057 local_path, strerror(errno)); 1058 local_path, strerror(errno));
1058 do_close(conn, handle, handle_len); 1059 goto fail;
1059 buffer_free(&msg); 1060 }
1060 free(handle); 1061 offset = highwater = 0;
1061 return(-1); 1062 if (resume) {
1063 if (fstat(local_fd, &st) == -1) {
1064 error("Unable to stat local file \"%s\": %s",
1065 local_path, strerror(errno));
1066 goto fail;
1067 }
1068 if ((size_t)st.st_size > size) {
1069 error("Unable to resume download of \"%s\": "
1070 "local file is larger than remote", local_path);
1071 fail:
1072 do_close(conn, handle, handle_len);
1073 buffer_free(&msg);
1074 free(handle);
1075 return -1;
1076 }
1077 offset = highwater = st.st_size;
1062 } 1078 }
1063 1079
1064 /* Read from remote and write to local */ 1080 /* Read from remote and write to local */
1065 write_error = read_error = write_errno = num_req = offset = 0; 1081 write_error = read_error = write_errno = num_req = 0;
1066 max_req = 1; 1082 max_req = 1;
1067 progress_counter = 0; 1083 progress_counter = offset;
1068 1084
1069 if (showprogress && size != 0) 1085 if (showprogress && size != 0)
1070 start_progress_meter(remote_path, size, &progress_counter); 1086 start_progress_meter(remote_path, size, &progress_counter);
@@ -1139,6 +1155,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1139 write_error = 1; 1155 write_error = 1;
1140 max_req = 0; 1156 max_req = 0;
1141 } 1157 }
1158 else if (!reordered && req->offset <= highwater)
1159 highwater = req->offset + len;
1160 else if (!reordered && req->offset > highwater)
1161 reordered = 1;
1142 progress_counter += len; 1162 progress_counter += len;
1143 free(data); 1163 free(data);
1144 1164
@@ -1187,7 +1207,15 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1187 /* Sanity check */ 1207 /* Sanity check */
1188 if (TAILQ_FIRST(&requests) != NULL) 1208 if (TAILQ_FIRST(&requests) != NULL)
1189 fatal("Transfer complete, but requests still in queue"); 1209 fatal("Transfer complete, but requests still in queue");
1190 1210 /* Truncate at highest contiguous point to avoid holes on interrupt */
1211 if (read_error || write_error || interrupted) {
1212 if (reordered && resume) {
1213 error("Unable to resume download of \"%s\": "
1214 "server reordered requests", local_path);
1215 }
1216 debug("truncating at %llu", (unsigned long long)highwater);
1217 ftruncate(local_fd, highwater);
1218 }
1191 if (read_error) { 1219 if (read_error) {
1192 error("Couldn't read from remote file \"%s\" : %s", 1220 error("Couldn't read from remote file \"%s\" : %s",
1193 remote_path, fx2txt(status)); 1221 remote_path, fx2txt(status));
@@ -1199,7 +1227,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1199 do_close(conn, handle, handle_len); 1227 do_close(conn, handle, handle_len);
1200 } else { 1228 } else {
1201 status = do_close(conn, handle, handle_len); 1229 status = do_close(conn, handle, handle_len);
1202 1230 if (interrupted)
1231 status = -1;
1203 /* Override umask and utimes if asked */ 1232 /* Override umask and utimes if asked */
1204#ifdef HAVE_FCHMOD 1233#ifdef HAVE_FCHMOD
1205 if (pflag && fchmod(local_fd, mode) == -1) 1234 if (pflag && fchmod(local_fd, mode) == -1)
@@ -1227,7 +1256,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1227 1256
1228static int 1257static int
1229download_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1258download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1230 Attrib *dirattrib, int pflag, int printflag, int depth) 1259 Attrib *dirattrib, int pflag, int printflag, int depth, int resume)
1231{ 1260{
1232 int i, ret = 0; 1261 int i, ret = 0;
1233 SFTP_DIRENT **dir_entries; 1262 SFTP_DIRENT **dir_entries;
@@ -1280,11 +1309,11 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1280 continue; 1309 continue;
1281 if (download_dir_internal(conn, new_src, new_dst, 1310 if (download_dir_internal(conn, new_src, new_dst,
1282 &(dir_entries[i]->a), pflag, printflag, 1311 &(dir_entries[i]->a), pflag, printflag,
1283 depth + 1) == -1) 1312 depth + 1, resume) == -1)
1284 ret = -1; 1313 ret = -1;
1285 } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1314 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1286 if (do_download(conn, new_src, new_dst, 1315 if (do_download(conn, new_src, new_dst,
1287 &(dir_entries[i]->a), pflag) == -1) { 1316 &(dir_entries[i]->a), pflag, resume) == -1) {
1288 error("Download of file %s to %s failed", 1317 error("Download of file %s to %s failed",
1289 new_src, new_dst); 1318 new_src, new_dst);
1290 ret = -1; 1319 ret = -1;
@@ -1317,7 +1346,7 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1317 1346
1318int 1347int
1319download_dir(struct sftp_conn *conn, char *src, char *dst, 1348download_dir(struct sftp_conn *conn, char *src, char *dst,
1320 Attrib *dirattrib, int pflag, int printflag) 1349 Attrib *dirattrib, int pflag, int printflag, int resume)
1321{ 1350{
1322 char *src_canon; 1351 char *src_canon;
1323 int ret; 1352 int ret;
@@ -1328,7 +1357,7 @@ download_dir(struct sftp_conn *conn, char *src, char *dst,
1328 } 1357 }
1329 1358
1330 ret = download_dir_internal(conn, src_canon, dst, 1359 ret = download_dir_internal(conn, src_canon, dst,
1331 dirattrib, pflag, printflag, 0); 1360 dirattrib, pflag, printflag, 0, resume);
1332 free(src_canon); 1361 free(src_canon);
1333 return ret; 1362 return ret;
1334} 1363}
@@ -1553,7 +1582,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1553 a.perm &= 01777; 1582 a.perm &= 01777;
1554 if (!pflag) 1583 if (!pflag)
1555 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1584 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1556 1585
1557 status = do_mkdir(conn, dst, &a, 0); 1586 status = do_mkdir(conn, dst, &a, 0);
1558 /* 1587 /*
1559 * we lack a portable status for errno EEXIST, 1588 * we lack a portable status for errno EEXIST,
@@ -1563,7 +1592,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1563 if (status != SSH2_FX_OK) { 1592 if (status != SSH2_FX_OK) {
1564 if (status != SSH2_FX_FAILURE) 1593 if (status != SSH2_FX_FAILURE)
1565 return -1; 1594 return -1;
1566 if (do_stat(conn, dst, 0) == NULL) 1595 if (do_stat(conn, dst, 0) == NULL)
1567 return -1; 1596 return -1;
1568 } 1597 }
1569 1598
@@ -1571,7 +1600,7 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1571 error("Failed to open dir \"%s\": %s", src, strerror(errno)); 1600 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1572 return -1; 1601 return -1;
1573 } 1602 }
1574 1603
1575 while (((dp = readdir(dirp)) != NULL) && !interrupted) { 1604 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1576 if (dp->d_ino == 0) 1605 if (dp->d_ino == 0)
1577 continue; 1606 continue;
diff --git a/sftp-client.h b/sftp-client.h
index aef54ef49..111a998c8 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.20 2010/12/04 00:18:01 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.21 2013/07/25 00:56:51 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -106,13 +106,13 @@ int do_symlink(struct sftp_conn *, char *, char *);
106 * Download 'remote_path' to 'local_path'. Preserve permissions and times 106 * Download 'remote_path' to 'local_path'. Preserve permissions and times
107 * if 'pflag' is set 107 * if 'pflag' is set
108 */ 108 */
109int do_download(struct sftp_conn *, char *, char *, Attrib *, int); 109int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int);
110 110
111/* 111/*
112 * Recursively download 'remote_directory' to 'local_directory'. Preserve 112 * Recursively download 'remote_directory' to 'local_directory'. Preserve
113 * times if 'pflag' is set 113 * times if 'pflag' is set
114 */ 114 */
115int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int); 115int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int, int);
116 116
117/* 117/*
118 * Upload 'local_path' to 'remote_path'. Preserve permissions and times 118 * Upload 'local_path' to 'remote_path'. Preserve permissions and times
diff --git a/sftp.1 b/sftp.1
index bcb472144..2577fe875 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.91 2011/09/05 05:56:13 djm Exp $ 1.\" $OpenBSD: sftp.1,v 1.92 2013/07/25 00:56:51 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: September 5 2011 $ 25.Dd $Mdocdate: July 25 2013 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -129,7 +129,7 @@ may be used to indicate standard input.
129.Nm 129.Nm
130will abort if any of the following 130will abort if any of the following
131commands fail: 131commands fail:
132.Ic get , put , rename , ln , 132.Ic get , put , reget , rename , ln ,
133.Ic rm , mkdir , chdir , ls , 133.Ic rm , mkdir , chdir , ls ,
134.Ic lchdir , chmod , chown , 134.Ic lchdir , chmod , chown ,
135.Ic chgrp , lpwd , df , symlink , 135.Ic chgrp , lpwd , df , symlink ,
@@ -343,7 +343,7 @@ extension.
343Quit 343Quit
344.Nm sftp . 344.Nm sftp .
345.It Xo Ic get 345.It Xo Ic get
346.Op Fl Ppr 346.Op Fl aPpr
347.Ar remote-path 347.Ar remote-path
348.Op Ar local-path 348.Op Ar local-path
349.Xc 349.Xc
@@ -363,6 +363,14 @@ is specified, then
363.Ar local-path 363.Ar local-path
364must specify a directory. 364must specify a directory.
365.Pp 365.Pp
366If the
367.Fl a
368flag is specified, then attempt to resume partial transfers of existing files.
369Note that resumption assumes that any partial copy of the local file matches
370the remote copy.
371If the remote file differs from the partial local copy then the resultant file
372is likely to be corrupt.
373.Pp
366If either the 374If either the
367.Fl P 375.Fl P
368or 376or
@@ -503,6 +511,18 @@ Display remote working directory.
503.It Ic quit 511.It Ic quit
504Quit 512Quit
505.Nm sftp . 513.Nm sftp .
514.It Xo Ic reget
515.Op Fl Ppr
516.Ar remote-path
517.Op Ar local-path
518.Xc
519Resume download of
520.Ar remote-path .
521Equivalent to
522.Ic get
523with the
524.Fl a
525flag set.
506.It Ic rename Ar oldpath Ar newpath 526.It Ic rename Ar oldpath Ar newpath
507Rename remote file from 527Rename remote file from
508.Ar oldpath 528.Ar oldpath
diff --git a/sftp.c b/sftp.c
index f0daaefa4..969328de4 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.147 2013/07/12 00:20:00 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.148 2013/07/25 00:56:52 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -88,6 +88,9 @@ int showprogress = 1;
88/* When this option is set, we always recursively download/upload directories */ 88/* When this option is set, we always recursively download/upload directories */
89int global_rflag = 0; 89int global_rflag = 0;
90 90
91/* When this option is set, we resume download if possible */
92int global_aflag = 0;
93
91/* When this option is set, the file transfers will always preserve times */ 94/* When this option is set, the file transfers will always preserve times */
92int global_pflag = 0; 95int global_pflag = 0;
93 96
@@ -151,6 +154,7 @@ extern char *__progname;
151#define I_SYMLINK 21 154#define I_SYMLINK 21
152#define I_VERSION 22 155#define I_VERSION 22
153#define I_PROGRESS 23 156#define I_PROGRESS 23
157#define I_REGET 26
154 158
155struct CMD { 159struct CMD {
156 const char *c; 160 const char *c;
@@ -190,6 +194,7 @@ static const struct CMD cmds[] = {
190 { "put", I_PUT, LOCAL }, 194 { "put", I_PUT, LOCAL },
191 { "pwd", I_PWD, REMOTE }, 195 { "pwd", I_PWD, REMOTE },
192 { "quit", I_QUIT, NOARGS }, 196 { "quit", I_QUIT, NOARGS },
197 { "reget", I_REGET, REMOTE },
193 { "rename", I_RENAME, REMOTE }, 198 { "rename", I_RENAME, REMOTE },
194 { "rm", I_RM, REMOTE }, 199 { "rm", I_RM, REMOTE },
195 { "rmdir", I_RMDIR, REMOTE }, 200 { "rmdir", I_RMDIR, REMOTE },
@@ -239,6 +244,7 @@ help(void)
239 " filesystem containing 'path'\n" 244 " filesystem containing 'path'\n"
240 "exit Quit sftp\n" 245 "exit Quit sftp\n"
241 "get [-Ppr] remote [local] Download file\n" 246 "get [-Ppr] remote [local] Download file\n"
247 "reget remote [local] Resume download file\n"
242 "help Display this help text\n" 248 "help Display this help text\n"
243 "lcd path Change local directory to 'path'\n" 249 "lcd path Change local directory to 'path'\n"
244 "lls [ls-options [path]] Display local directory listing\n" 250 "lls [ls-options [path]] Display local directory listing\n"
@@ -350,8 +356,8 @@ make_absolute(char *p, char *pwd)
350} 356}
351 357
352static int 358static int
353parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, 359parse_getput_flags(const char *cmd, char **argv, int argc,
354 int *rflag) 360 int *aflag, int *pflag, int *rflag)
355{ 361{
356 extern int opterr, optind, optopt, optreset; 362 extern int opterr, optind, optopt, optreset;
357 int ch; 363 int ch;
@@ -359,9 +365,12 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
359 optind = optreset = 1; 365 optind = optreset = 1;
360 opterr = 0; 366 opterr = 0;
361 367
362 *rflag = *pflag = 0; 368 *aflag = *rflag = *pflag = 0;
363 while ((ch = getopt(argc, argv, "PpRr")) != -1) { 369 while ((ch = getopt(argc, argv, "aPpRr")) != -1) {
364 switch (ch) { 370 switch (ch) {
371 case 'a':
372 *aflag = 1;
373 break;
365 case 'p': 374 case 'p':
366 case 'P': 375 case 'P':
367 *pflag = 1; 376 *pflag = 1;
@@ -519,7 +528,7 @@ pathname_is_dir(char *pathname)
519 528
520static int 529static int
521process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, 530process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
522 int pflag, int rflag) 531 int pflag, int rflag, int resume)
523{ 532{
524 char *abs_src = NULL; 533 char *abs_src = NULL;
525 char *abs_dst = NULL; 534 char *abs_dst = NULL;
@@ -571,15 +580,18 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
571 } 580 }
572 free(tmp); 581 free(tmp);
573 582
574 if (!quiet) 583 resume |= global_aflag;
584 if (!quiet && resume)
585 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
586 else if (!quiet && !resume)
575 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 587 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
576 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 588 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
577 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 589 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
578 pflag || global_pflag, 1) == -1) 590 pflag || global_pflag, 1, resume) == -1)
579 err = -1; 591 err = -1;
580 } else { 592 } else {
581 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 593 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
582 pflag || global_pflag) == -1) 594 pflag || global_pflag, resume) == -1)
583 err = -1; 595 err = -1;
584 } 596 }
585 free(abs_dst); 597 free(abs_dst);
@@ -1118,8 +1130,9 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1118} 1130}
1119 1131
1120static int 1132static int
1121parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 1133parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1122 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) 1134 int *pflag, int *rflag, int *sflag, unsigned long *n_arg,
1135 char **path1, char **path2)
1123{ 1136{
1124 const char *cmd, *cp = *cpp; 1137 const char *cmd, *cp = *cpp;
1125 char *cp2, **argv; 1138 char *cp2, **argv;
@@ -1163,14 +1176,15 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1163 } 1176 }
1164 1177
1165 /* Get arguments and parse flags */ 1178 /* Get arguments and parse flags */
1166 *lflag = *pflag = *rflag = *hflag = *n_arg = 0; 1179 *aflag = *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1167 *path1 = *path2 = NULL; 1180 *path1 = *path2 = NULL;
1168 optidx = 1; 1181 optidx = 1;
1169 switch (cmdnum) { 1182 switch (cmdnum) {
1170 case I_GET: 1183 case I_GET:
1184 case I_REGET:
1171 case I_PUT: 1185 case I_PUT:
1172 if ((optidx = parse_getput_flags(cmd, argv, argc, 1186 if ((optidx = parse_getput_flags(cmd, argv, argc,
1173 pflag, rflag)) == -1) 1187 aflag, pflag, rflag)) == -1)
1174 return -1; 1188 return -1;
1175 /* Get first pathname (mandatory) */ 1189 /* Get first pathname (mandatory) */
1176 if (argc - optidx < 1) { 1190 if (argc - optidx < 1) {
@@ -1185,6 +1199,11 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1185 /* Destination is not globbed */ 1199 /* Destination is not globbed */
1186 undo_glob_escape(*path2); 1200 undo_glob_escape(*path2);
1187 } 1201 }
1202 if (*aflag && cmdnum == I_PUT) {
1203 /* XXX implement resume for uploads */
1204 error("Resume is not supported for uploads");
1205 return -1;
1206 }
1188 break; 1207 break;
1189 case I_LINK: 1208 case I_LINK:
1190 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) 1209 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
@@ -1293,7 +1312,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1293 int err_abort) 1312 int err_abort)
1294{ 1313{
1295 char *path1, *path2, *tmp; 1314 char *path1, *path2, *tmp;
1296 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0; 1315 int aflag = 0, hflag = 0, iflag = 0, lflag = 0, pflag = 0;
1316 int rflag = 0, sflag = 0;
1297 int cmdnum, i; 1317 int cmdnum, i;
1298 unsigned long n_arg = 0; 1318 unsigned long n_arg = 0;
1299 Attrib a, *aa; 1319 Attrib a, *aa;
@@ -1302,9 +1322,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1302 glob_t g; 1322 glob_t g;
1303 1323
1304 path1 = path2 = NULL; 1324 path1 = path2 = NULL;
1305 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, 1325 cmdnum = parse_args(&cmd, &aflag, &hflag, &iflag, &lflag, &pflag,
1306 &sflag, &n_arg, &path1, &path2); 1326 &rflag, &sflag, &n_arg, &path1, &path2);
1307
1308 if (iflag != 0) 1327 if (iflag != 0)
1309 err_abort = 0; 1328 err_abort = 0;
1310 1329
@@ -1319,8 +1338,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1319 /* Unrecognized command */ 1338 /* Unrecognized command */
1320 err = -1; 1339 err = -1;
1321 break; 1340 break;
1341 case I_REGET:
1342 aflag = 1;
1343 /* FALLTHROUGH */
1322 case I_GET: 1344 case I_GET:
1323 err = process_get(conn, path1, path2, *pwd, pflag, rflag); 1345 err = process_get(conn, path1, path2, *pwd, pflag,
1346 rflag, aflag);
1324 break; 1347 break;
1325 case I_PUT: 1348 case I_PUT:
1326 err = process_put(conn, path1, path2, *pwd, pflag, rflag); 1349 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
@@ -1949,12 +1972,10 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1949 } 1972 }
1950 } else { 1973 } else {
1951 /* XXX this is wrong wrt quoting */ 1974 /* XXX this is wrong wrt quoting */
1952 if (file2 == NULL) 1975 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
1953 snprintf(cmd, sizeof cmd, "get %s", dir); 1976 global_aflag ? " -a" : "", dir,
1954 else 1977 file2 == NULL ? "" : " ",
1955 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1978 file2 == NULL ? "" : file2);
1956 file2);
1957
1958 err = parse_dispatch_command(conn, cmd, 1979 err = parse_dispatch_command(conn, cmd,
1959 &remote_path, 1); 1980 &remote_path, 1);
1960 free(dir); 1981 free(dir);
@@ -2143,7 +2164,7 @@ main(int argc, char **argv)
2143 infile = stdin; 2164 infile = stdin;
2144 2165
2145 while ((ch = getopt(argc, argv, 2166 while ((ch = getopt(argc, argv,
2146 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { 2167 "1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2147 switch (ch) { 2168 switch (ch) {
2148 /* Passed through to ssh(1) */ 2169 /* Passed through to ssh(1) */
2149 case '4': 2170 case '4':
@@ -2183,6 +2204,9 @@ main(int argc, char **argv)
2183 case '2': 2204 case '2':
2184 sshver = 2; 2205 sshver = 2;
2185 break; 2206 break;
2207 case 'a':
2208 global_aflag = 1;
2209 break;
2186 case 'B': 2210 case 'B':
2187 copy_buffer_len = strtol(optarg, &cp, 10); 2211 copy_buffer_len = strtol(optarg, &cp, 10);
2188 if (copy_buffer_len == 0 || *cp != '\0') 2212 if (copy_buffer_len == 0 || *cp != '\0')