diff options
author | Damien Miller <djm@mindrot.org> | 2013-07-25 11:56:52 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2013-07-25 11:56:52 +1000 |
commit | 0d032419ee6e1968fc1cb187af63bf3b77b506ea (patch) | |
tree | ce2788365040e9ea188bd60c8bec87d410814017 /sftp.c | |
parent | 98e27dcf581647b5bbe9780e8f59685d942d8ea3 (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@
Diffstat (limited to 'sftp.c')
-rw-r--r-- | sftp.c | 76 |
1 files changed, 50 insertions, 26 deletions
@@ -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') |