summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2009-10-07 08:37:48 +1100
committerDarren Tucker <dtucker@zip.com.au>2009-10-07 08:37:48 +1100
commit1b0dd175377c86abb7f3a3da2e9b1a89f36c7a1a (patch)
treed6b5f501c4c9890ed91ce4fd86be583213d7e8e6 /sftp.c
parent1477ea162c05c09b4b5ebc19ac588fbf469349dc (diff)
- djm@cvs.openbsd.org 2009/08/18 18:36:21
[sftp-client.h sftp.1 sftp-client.c sftp.c] recursive transfer support for get/put and on the commandline work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code with some tweaks by me; "go for it" deraadt@
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c219
1 files changed, 113 insertions, 106 deletions
diff --git a/sftp.c b/sftp.c
index 0123fd72c..75b16b27e 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.110 2009/08/13 13:39:54 jmc Exp $ */ 1/* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 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 *
@@ -35,6 +35,9 @@
35#ifdef HAVE_PATHS_H 35#ifdef HAVE_PATHS_H
36# include <paths.h> 36# include <paths.h>
37#endif 37#endif
38#ifdef HAVE_LIBGEN_H
39#include <libgen.h>
40#endif
38#ifdef USE_LIBEDIT 41#ifdef USE_LIBEDIT
39#include <histedit.h> 42#include <histedit.h>
40#else 43#else
@@ -83,6 +86,12 @@ static pid_t sshpid = -1;
83/* This is set to 0 if the progressmeter is not desired. */ 86/* This is set to 0 if the progressmeter is not desired. */
84int showprogress = 1; 87int showprogress = 1;
85 88
89/* When this option is set, we always recursively download/upload directories */
90int global_rflag = 0;
91
92/* When this option is set, the file transfers will always preserve times */
93int global_pflag = 0;
94
86/* SIGINT received during command processing */ 95/* SIGINT received during command processing */
87volatile sig_atomic_t interrupted = 0; 96volatile sig_atomic_t interrupted = 0;
88 97
@@ -216,7 +225,7 @@ help(void)
216 "df [-hi] [path] Display statistics for current directory or\n" 225 "df [-hi] [path] Display statistics for current directory or\n"
217 " filesystem containing 'path'\n" 226 " filesystem containing 'path'\n"
218 "exit Quit sftp\n" 227 "exit Quit sftp\n"
219 "get [-P] remote-path [local-path] Download file\n" 228 "get [-Pr] remote-path [local-path] Download file\n"
220 "help Display this help text\n" 229 "help Display this help text\n"
221 "lcd path Change local directory to 'path'\n" 230 "lcd path Change local directory to 'path'\n"
222 "lls [ls-options [path]] Display local directory listing\n" 231 "lls [ls-options [path]] Display local directory listing\n"
@@ -227,7 +236,7 @@ help(void)
227 "lumask umask Set local umask to 'umask'\n" 236 "lumask umask Set local umask to 'umask'\n"
228 "mkdir path Create remote directory\n" 237 "mkdir path Create remote directory\n"
229 "progress Toggle display of progress meter\n" 238 "progress Toggle display of progress meter\n"
230 "put [-P] local-path [remote-path] Upload file\n" 239 "put [-Pr] local-path [remote-path] Upload file\n"
231 "pwd Display remote working directory\n" 240 "pwd Display remote working directory\n"
232 "quit Quit sftp\n" 241 "quit Quit sftp\n"
233 "rename oldpath newpath Rename remote file\n" 242 "rename oldpath newpath Rename remote file\n"
@@ -314,21 +323,6 @@ path_strip(char *path, char *strip)
314} 323}
315 324
316static char * 325static char *
317path_append(char *p1, char *p2)
318{
319 char *ret;
320 size_t len = strlen(p1) + strlen(p2) + 2;
321
322 ret = xmalloc(len);
323 strlcpy(ret, p1, len);
324 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
325 strlcat(ret, "/", len);
326 strlcat(ret, p2, len);
327
328 return(ret);
329}
330
331static char *
332make_absolute(char *p, char *pwd) 326make_absolute(char *p, char *pwd)
333{ 327{
334 char *abs_str; 328 char *abs_str;
@@ -343,27 +337,8 @@ make_absolute(char *p, char *pwd)
343} 337}
344 338
345static int 339static int
346infer_path(const char *p, char **ifp) 340parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
347{ 341 int *rflag)
348 char *cp;
349
350 cp = strrchr(p, '/');
351 if (cp == NULL) {
352 *ifp = xstrdup(p);
353 return(0);
354 }
355
356 if (!cp[1]) {
357 error("Invalid path");
358 return(-1);
359 }
360
361 *ifp = xstrdup(cp + 1);
362 return(0);
363}
364
365static int
366parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
367{ 342{
368 extern int opterr, optind, optopt, optreset; 343 extern int opterr, optind, optopt, optreset;
369 int ch; 344 int ch;
@@ -371,13 +346,17 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
371 optind = optreset = 1; 346 optind = optreset = 1;
372 opterr = 0; 347 opterr = 0;
373 348
374 *pflag = 0; 349 *rflag = *pflag = 0;
375 while ((ch = getopt(argc, argv, "Pp")) != -1) { 350 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
376 switch (ch) { 351 switch (ch) {
377 case 'p': 352 case 'p':
378 case 'P': 353 case 'P':
379 *pflag = 1; 354 *pflag = 1;
380 break; 355 break;
356 case 'r':
357 case 'R':
358 *rflag = 1;
359 break;
381 default: 360 default:
382 error("%s: Invalid flag -%c", cmd, optopt); 361 error("%s: Invalid flag -%c", cmd, optopt);
383 return -1; 362 return -1;
@@ -489,62 +468,79 @@ remote_is_dir(struct sftp_conn *conn, char *path)
489 return(S_ISDIR(a->perm)); 468 return(S_ISDIR(a->perm));
490} 469}
491 470
471/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
472static int
473pathname_is_dir(char *pathname)
474{
475 size_t l = strlen(pathname);
476
477 return l > 0 && pathname[l - 1] == '/';
478}
479
492static int 480static int
493process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 481process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
482 int pflag, int rflag)
494{ 483{
495 char *abs_src = NULL; 484 char *abs_src = NULL;
496 char *abs_dst = NULL; 485 char *abs_dst = NULL;
497 char *tmp;
498 glob_t g; 486 glob_t g;
499 int err = 0; 487 char *filename, *tmp=NULL;
500 int i; 488 int i, err = 0;
501 489
502 abs_src = xstrdup(src); 490 abs_src = xstrdup(src);
503 abs_src = make_absolute(abs_src, pwd); 491 abs_src = make_absolute(abs_src, pwd);
504
505 memset(&g, 0, sizeof(g)); 492 memset(&g, 0, sizeof(g));
493
506 debug3("Looking up %s", abs_src); 494 debug3("Looking up %s", abs_src);
507 if (remote_glob(conn, abs_src, 0, NULL, &g)) { 495 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
508 error("File \"%s\" not found.", abs_src); 496 error("File \"%s\" not found.", abs_src);
509 err = -1; 497 err = -1;
510 goto out; 498 goto out;
511 } 499 }
512 500
513 /* If multiple matches, dst must be a directory or unspecified */ 501 /*
514 if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 502 * If multiple matches then dst must be a directory or
515 error("Multiple files match, but \"%s\" is not a directory", 503 * unspecified.
516 dst); 504 */
505 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
506 error("Multiple source paths, but destination "
507 "\"%s\" is not a directory", dst);
517 err = -1; 508 err = -1;
518 goto out; 509 goto out;
519 } 510 }
520 511
521 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 512 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
522 if (infer_path(g.gl_pathv[i], &tmp)) { 513 tmp = xstrdup(g.gl_pathv[i]);
514 if ((filename = basename(tmp)) == NULL) {
515 error("basename %s: %s", tmp, strerror(errno));
516 xfree(tmp);
523 err = -1; 517 err = -1;
524 goto out; 518 goto out;
525 } 519 }
526 520
527 if (g.gl_matchc == 1 && dst) { 521 if (g.gl_matchc == 1 && dst) {
528 /* If directory specified, append filename */
529 xfree(tmp);
530 if (is_dir(dst)) { 522 if (is_dir(dst)) {
531 if (infer_path(g.gl_pathv[0], &tmp)) { 523 abs_dst = path_append(dst, filename);
532 err = 1; 524 } else {
533 goto out;
534 }
535 abs_dst = path_append(dst, tmp);
536 xfree(tmp);
537 } else
538 abs_dst = xstrdup(dst); 525 abs_dst = xstrdup(dst);
526 }
539 } else if (dst) { 527 } else if (dst) {
540 abs_dst = path_append(dst, tmp); 528 abs_dst = path_append(dst, filename);
541 xfree(tmp); 529 } else {
542 } else 530 abs_dst = xstrdup(filename);
543 abs_dst = tmp; 531 }
532 xfree(tmp);
544 533
545 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 534 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
546 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 535 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
547 err = -1; 536 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
537 pflag || global_pflag, 1) == -1)
538 err = -1;
539 } else {
540 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
541 pflag || global_pflag) == -1)
542 err = -1;
543 }
548 xfree(abs_dst); 544 xfree(abs_dst);
549 abs_dst = NULL; 545 abs_dst = NULL;
550 } 546 }
@@ -556,14 +552,15 @@ out:
556} 552}
557 553
558static int 554static int
559process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 555process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
556 int pflag, int rflag)
560{ 557{
561 char *tmp_dst = NULL; 558 char *tmp_dst = NULL;
562 char *abs_dst = NULL; 559 char *abs_dst = NULL;
563 char *tmp; 560 char *tmp = NULL, *filename = NULL;
564 glob_t g; 561 glob_t g;
565 int err = 0; 562 int err = 0;
566 int i; 563 int i, dst_is_dir = 1;
567 struct stat sb; 564 struct stat sb;
568 565
569 if (dst) { 566 if (dst) {
@@ -573,16 +570,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
573 570
574 memset(&g, 0, sizeof(g)); 571 memset(&g, 0, sizeof(g));
575 debug3("Looking up %s", src); 572 debug3("Looking up %s", src);
576 if (glob(src, GLOB_NOCHECK, NULL, &g)) { 573 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
577 error("File \"%s\" not found.", src); 574 error("File \"%s\" not found.", src);
578 err = -1; 575 err = -1;
579 goto out; 576 goto out;
580 } 577 }
581 578
579 /* If we aren't fetching to pwd then stash this status for later */
580 if (tmp_dst != NULL)
581 dst_is_dir = remote_is_dir(conn, tmp_dst);
582
582 /* If multiple matches, dst may be directory or unspecified */ 583 /* If multiple matches, dst may be directory or unspecified */
583 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 584 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
584 error("Multiple files match, but \"%s\" is not a directory", 585 error("Multiple paths match, but destination "
585 tmp_dst); 586 "\"%s\" is not a directory", tmp_dst);
586 err = -1; 587 err = -1;
587 goto out; 588 goto out;
588 } 589 }
@@ -593,38 +594,38 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
593 error("stat %s: %s", g.gl_pathv[i], strerror(errno)); 594 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
594 continue; 595 continue;
595 } 596 }
596 597
597 if (!S_ISREG(sb.st_mode)) { 598 tmp = xstrdup(g.gl_pathv[i]);
598 error("skipping non-regular file %s", 599 if ((filename = basename(tmp)) == NULL) {
599 g.gl_pathv[i]); 600 error("basename %s: %s", tmp, strerror(errno));
600 continue; 601 xfree(tmp);
601 }
602 if (infer_path(g.gl_pathv[i], &tmp)) {
603 err = -1; 602 err = -1;
604 goto out; 603 goto out;
605 } 604 }
606 605
607 if (g.gl_matchc == 1 && tmp_dst) { 606 if (g.gl_matchc == 1 && tmp_dst) {
608 /* If directory specified, append filename */ 607 /* If directory specified, append filename */
609 if (remote_is_dir(conn, tmp_dst)) { 608 if (dst_is_dir)
610 if (infer_path(g.gl_pathv[0], &tmp)) { 609 abs_dst = path_append(tmp_dst, filename);
611 err = 1; 610 else
612 goto out;
613 }
614 abs_dst = path_append(tmp_dst, tmp);
615 xfree(tmp);
616 } else
617 abs_dst = xstrdup(tmp_dst); 611 abs_dst = xstrdup(tmp_dst);
618
619 } else if (tmp_dst) { 612 } else if (tmp_dst) {
620 abs_dst = path_append(tmp_dst, tmp); 613 abs_dst = path_append(tmp_dst, filename);
621 xfree(tmp); 614 } else {
622 } else 615 abs_dst = make_absolute(xstrdup(filename), pwd);
623 abs_dst = make_absolute(tmp, pwd); 616 }
617 xfree(tmp);
624 618
625 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 619 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
626 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 620 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
627 err = -1; 621 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
622 pflag || global_pflag, 1) == -1)
623 err = -1;
624 } else {
625 if (do_upload(conn, g.gl_pathv[i], abs_dst,
626 pflag || global_pflag) == -1)
627 err = -1;
628 }
628 } 629 }
629 630
630out: 631out:
@@ -1065,7 +1066,7 @@ makeargv(const char *arg, int *argcp)
1065} 1066}
1066 1067
1067static int 1068static int
1068parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, 1069parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,
1069 unsigned long *n_arg, char **path1, char **path2) 1070 unsigned long *n_arg, char **path1, char **path2)
1070{ 1071{
1071 const char *cmd, *cp = *cpp; 1072 const char *cmd, *cp = *cpp;
@@ -1109,13 +1110,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
1109 } 1110 }
1110 1111
1111 /* Get arguments and parse flags */ 1112 /* Get arguments and parse flags */
1112 *lflag = *pflag = *hflag = *n_arg = 0; 1113 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1113 *path1 = *path2 = NULL; 1114 *path1 = *path2 = NULL;
1114 optidx = 1; 1115 optidx = 1;
1115 switch (cmdnum) { 1116 switch (cmdnum) {
1116 case I_GET: 1117 case I_GET:
1117 case I_PUT: 1118 case I_PUT:
1118 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) 1119 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1119 return -1; 1120 return -1;
1120 /* Get first pathname (mandatory) */ 1121 /* Get first pathname (mandatory) */
1121 if (argc - optidx < 1) { 1122 if (argc - optidx < 1) {
@@ -1235,7 +1236,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1235 int err_abort) 1236 int err_abort)
1236{ 1237{
1237 char *path1, *path2, *tmp; 1238 char *path1, *path2, *tmp;
1238 int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; 1239 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1239 unsigned long n_arg = 0; 1240 unsigned long n_arg = 0;
1240 Attrib a, *aa; 1241 Attrib a, *aa;
1241 char path_buf[MAXPATHLEN]; 1242 char path_buf[MAXPATHLEN];
@@ -1243,7 +1244,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1243 glob_t g; 1244 glob_t g;
1244 1245
1245 path1 = path2 = NULL; 1246 path1 = path2 = NULL;
1246 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, 1247 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1247 &path1, &path2); 1248 &path1, &path2);
1248 1249
1249 if (iflag != 0) 1250 if (iflag != 0)
@@ -1261,10 +1262,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1261 err = -1; 1262 err = -1;
1262 break; 1263 break;
1263 case I_GET: 1264 case I_GET:
1264 err = process_get(conn, path1, path2, *pwd, pflag); 1265 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1265 break; 1266 break;
1266 case I_PUT: 1267 case I_PUT:
1267 err = process_put(conn, path1, path2, *pwd, pflag); 1268 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1268 break; 1269 break;
1269 case I_RENAME: 1270 case I_RENAME:
1270 path1 = make_absolute(path1, *pwd); 1271 path1 = make_absolute(path1, *pwd);
@@ -1290,7 +1291,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1290 attrib_clear(&a); 1291 attrib_clear(&a);
1291 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1292 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1292 a.perm = 0777; 1293 a.perm = 0777;
1293 err = do_mkdir(conn, path1, &a); 1294 err = do_mkdir(conn, path1, &a, 1);
1294 break; 1295 break;
1295 case I_RMDIR: 1296 case I_RMDIR:
1296 path1 = make_absolute(path1, *pwd); 1297 path1 = make_absolute(path1, *pwd);
@@ -1668,7 +1669,7 @@ usage(void)
1668 extern char *__progname; 1669 extern char *__progname;
1669 1670
1670 fprintf(stderr, 1671 fprintf(stderr,
1671 "usage: %s [-1246Cqv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 1672 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
1672 " [-D sftp_server_path] [-F ssh_config] " 1673 " [-D sftp_server_path] [-F ssh_config] "
1673 "[-i identity_file]\n" 1674 "[-i identity_file]\n"
1674 " [-o ssh_option] [-P port] [-R num_requests] " 1675 " [-o ssh_option] [-P port] [-R num_requests] "
@@ -1710,7 +1711,7 @@ main(int argc, char **argv)
1710 infile = stdin; 1711 infile = stdin;
1711 1712
1712 while ((ch = getopt(argc, argv, 1713 while ((ch = getopt(argc, argv,
1713 "1246hqvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { 1714 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
1714 switch (ch) { 1715 switch (ch) {
1715 /* Passed through to ssh(1) */ 1716 /* Passed through to ssh(1) */
1716 case '4': 1717 case '4':
@@ -1764,9 +1765,15 @@ main(int argc, char **argv)
1764 batchmode = 1; 1765 batchmode = 1;
1765 addargs(&args, "-obatchmode yes"); 1766 addargs(&args, "-obatchmode yes");
1766 break; 1767 break;
1768 case 'p':
1769 global_pflag = 1;
1770 break;
1767 case 'D': 1771 case 'D':
1768 sftp_direct = optarg; 1772 sftp_direct = optarg;
1769 break; 1773 break;
1774 case 'r':
1775 global_rflag = 1;
1776 break;
1770 case 'R': 1777 case 'R':
1771 num_requests = strtol(optarg, &cp, 10); 1778 num_requests = strtol(optarg, &cp, 10);
1772 if (num_requests == 0 || *cp != '\0') 1779 if (num_requests == 0 || *cp != '\0')