diff options
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | sftp-client.c | 75 | ||||
-rw-r--r-- | sftp-client.h | 6 | ||||
-rw-r--r-- | sftp.1 | 28 | ||||
-rw-r--r-- | sftp.c | 76 |
5 files changed, 133 insertions, 56 deletions
@@ -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 | ||
19 | 20130720 | 23 | 20130720 |
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 | ||
989 | int | 989 | int |
990 | do_download(struct sftp_conn *conn, char *remote_path, char *local_path, | 990 | do_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 | ||
1228 | static int | 1257 | static int |
1229 | download_dir_internal(struct sftp_conn *conn, char *src, char *dst, | 1258 | download_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 | ||
1318 | int | 1347 | int |
1319 | download_dir(struct sftp_conn *conn, char *src, char *dst, | 1348 | download_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 | */ |
109 | int do_download(struct sftp_conn *, char *, char *, Attrib *, int); | 109 | int 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 | */ |
115 | int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int); | 115 | int 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 |
@@ -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 |
130 | will abort if any of the following | 130 | will abort if any of the following |
131 | commands fail: | 131 | commands 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. | |||
343 | Quit | 343 | Quit |
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 |
364 | must specify a directory. | 364 | must specify a directory. |
365 | .Pp | 365 | .Pp |
366 | If the | ||
367 | .Fl a | ||
368 | flag is specified, then attempt to resume partial transfers of existing files. | ||
369 | Note that resumption assumes that any partial copy of the local file matches | ||
370 | the remote copy. | ||
371 | If the remote file differs from the partial local copy then the resultant file | ||
372 | is likely to be corrupt. | ||
373 | .Pp | ||
366 | If either the | 374 | If either the |
367 | .Fl P | 375 | .Fl P |
368 | or | 376 | or |
@@ -503,6 +511,18 @@ Display remote working directory. | |||
503 | .It Ic quit | 511 | .It Ic quit |
504 | Quit | 512 | Quit |
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 | ||
519 | Resume download of | ||
520 | .Ar remote-path . | ||
521 | Equivalent to | ||
522 | .Ic get | ||
523 | with the | ||
524 | .Fl a | ||
525 | flag set. | ||
506 | .It Ic rename Ar oldpath Ar newpath | 526 | .It Ic rename Ar oldpath Ar newpath |
507 | Rename remote file from | 527 | Rename remote file from |
508 | .Ar oldpath | 528 | .Ar oldpath |
@@ -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 */ |
89 | int global_rflag = 0; | 89 | int global_rflag = 0; |
90 | 90 | ||
91 | /* When this option is set, we resume download if possible */ | ||
92 | int 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 */ |
92 | int global_pflag = 0; | 95 | int 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 | ||
155 | struct CMD { | 159 | struct 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 | ||
352 | static int | 358 | static int |
353 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, | 359 | parse_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 | ||
520 | static int | 529 | static int |
521 | process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, | 530 | process_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 | ||
1120 | static int | 1132 | static int |
1121 | parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, | 1133 | parse_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') |