diff options
Diffstat (limited to 'sftp-int.c')
-rw-r--r-- | sftp-int.c | 203 |
1 files changed, 182 insertions, 21 deletions
diff --git a/sftp-int.c b/sftp-int.c index b13e5da5d..6a2012910 100644 --- a/sftp-int.c +++ b/sftp-int.c | |||
@@ -22,11 +22,10 @@ | |||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | */ | 23 | */ |
24 | 24 | ||
25 | /* XXX: globbed ls */ | ||
26 | /* XXX: recursive operations */ | 25 | /* XXX: recursive operations */ |
27 | 26 | ||
28 | #include "includes.h" | 27 | #include "includes.h" |
29 | RCSID("$OpenBSD: sftp-int.c,v 1.47 2002/06/23 09:30:14 deraadt Exp $"); | 28 | RCSID("$OpenBSD: sftp-int.c,v 1.49 2002/09/12 00:13:06 djm Exp $"); |
30 | 29 | ||
31 | #include "buffer.h" | 30 | #include "buffer.h" |
32 | #include "xmalloc.h" | 31 | #include "xmalloc.h" |
@@ -201,6 +200,25 @@ local_do_ls(const char *args) | |||
201 | } | 200 | } |
202 | } | 201 | } |
203 | 202 | ||
203 | /* Strip one path (usually the pwd) from the start of another */ | ||
204 | static char * | ||
205 | path_strip(char *path, char *strip) | ||
206 | { | ||
207 | size_t len; | ||
208 | |||
209 | if (strip == NULL) | ||
210 | return (xstrdup(path)); | ||
211 | |||
212 | len = strlen(strip); | ||
213 | if (strip != NULL && strncmp(path, strip, len) == 0) { | ||
214 | if (strip[len - 1] != '/' && path[len] == '/') | ||
215 | len++; | ||
216 | return (xstrdup(path + len)); | ||
217 | } | ||
218 | |||
219 | return (xstrdup(path)); | ||
220 | } | ||
221 | |||
204 | static char * | 222 | static char * |
205 | path_append(char *p1, char *p2) | 223 | path_append(char *p1, char *p2) |
206 | { | 224 | { |
@@ -209,7 +227,7 @@ path_append(char *p1, char *p2) | |||
209 | 227 | ||
210 | ret = xmalloc(len); | 228 | ret = xmalloc(len); |
211 | strlcpy(ret, p1, len); | 229 | strlcpy(ret, p1, len); |
212 | if (strcmp(p1, "/") != 0) | 230 | if (p1[strlen(p1) - 1] != '/') |
213 | strlcat(ret, "/", len); | 231 | strlcat(ret, "/", len); |
214 | strlcat(ret, p2, len); | 232 | strlcat(ret, p2, len); |
215 | 233 | ||
@@ -274,6 +292,29 @@ parse_getput_flags(const char **cpp, int *pflag) | |||
274 | } | 292 | } |
275 | 293 | ||
276 | static int | 294 | static int |
295 | parse_ls_flags(const char **cpp, int *lflag) | ||
296 | { | ||
297 | const char *cp = *cpp; | ||
298 | |||
299 | /* Check for flags */ | ||
300 | if (cp++[0] == '-') { | ||
301 | for(; strchr(WHITESPACE, *cp) == NULL; cp++) { | ||
302 | switch (*cp) { | ||
303 | case 'l': | ||
304 | *lflag = 1; | ||
305 | break; | ||
306 | default: | ||
307 | error("Invalid flag -%c", *cp); | ||
308 | return(-1); | ||
309 | } | ||
310 | } | ||
311 | *cpp = cp + strspn(cp, WHITESPACE); | ||
312 | } | ||
313 | |||
314 | return(0); | ||
315 | } | ||
316 | |||
317 | static int | ||
277 | get_pathname(const char **cpp, char **path) | 318 | get_pathname(const char **cpp, char **path) |
278 | { | 319 | { |
279 | const char *cp = *cpp, *end; | 320 | const char *cp = *cpp, *end; |
@@ -504,8 +545,129 @@ out: | |||
504 | } | 545 | } |
505 | 546 | ||
506 | static int | 547 | static int |
507 | parse_args(const char **cpp, int *pflag, unsigned long *n_arg, | 548 | sdirent_comp(const void *aa, const void *bb) |
508 | char **path1, char **path2) | 549 | { |
550 | SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; | ||
551 | SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; | ||
552 | |||
553 | return (strcmp(a->filename, b->filename)); | ||
554 | } | ||
555 | |||
556 | /* sftp ls.1 replacement for directories */ | ||
557 | static int | ||
558 | do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) | ||
559 | { | ||
560 | int n; | ||
561 | SFTP_DIRENT **d; | ||
562 | |||
563 | if ((n = do_readdir(conn, path, &d)) != 0) | ||
564 | return (n); | ||
565 | |||
566 | /* Count entries for sort */ | ||
567 | for (n = 0; d[n] != NULL; n++) | ||
568 | ; | ||
569 | |||
570 | qsort(d, n, sizeof(*d), sdirent_comp); | ||
571 | |||
572 | for (n = 0; d[n] != NULL; n++) { | ||
573 | char *tmp, *fname; | ||
574 | |||
575 | tmp = path_append(path, d[n]->filename); | ||
576 | fname = path_strip(tmp, strip_path); | ||
577 | xfree(tmp); | ||
578 | |||
579 | if (lflag) { | ||
580 | char *lname; | ||
581 | struct stat sb; | ||
582 | |||
583 | memset(&sb, 0, sizeof(sb)); | ||
584 | attrib_to_stat(&d[n]->a, &sb); | ||
585 | lname = ls_file(fname, &sb, 1); | ||
586 | printf("%s\n", lname); | ||
587 | xfree(lname); | ||
588 | } else { | ||
589 | /* XXX - multicolumn display would be nice here */ | ||
590 | printf("%s\n", fname); | ||
591 | } | ||
592 | |||
593 | xfree(fname); | ||
594 | } | ||
595 | |||
596 | free_sftp_dirents(d); | ||
597 | return (0); | ||
598 | } | ||
599 | |||
600 | /* sftp ls.1 replacement which handles path globs */ | ||
601 | static int | ||
602 | do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | ||
603 | int lflag) | ||
604 | { | ||
605 | glob_t g; | ||
606 | int i; | ||
607 | Attrib *a; | ||
608 | struct stat sb; | ||
609 | |||
610 | memset(&g, 0, sizeof(g)); | ||
611 | |||
612 | if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, | ||
613 | NULL, &g)) { | ||
614 | error("Can't ls: \"%s\" not found", path); | ||
615 | return (-1); | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * If the glob returns a single match, which is the same as the | ||
620 | * input glob, and it is a directory, then just list its contents | ||
621 | */ | ||
622 | if (g.gl_pathc == 1 && | ||
623 | strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) { | ||
624 | if ((a = do_lstat(conn, path, 1)) == NULL) { | ||
625 | globfree(&g); | ||
626 | return (-1); | ||
627 | } | ||
628 | if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && | ||
629 | S_ISDIR(a->perm)) { | ||
630 | globfree(&g); | ||
631 | return (do_ls_dir(conn, path, strip_path, lflag)); | ||
632 | } | ||
633 | } | ||
634 | |||
635 | for (i = 0; g.gl_pathv[i]; i++) { | ||
636 | char *fname, *lname; | ||
637 | |||
638 | fname = path_strip(g.gl_pathv[i], strip_path); | ||
639 | |||
640 | if (lflag) { | ||
641 | /* | ||
642 | * XXX: this is slow - 1 roundtrip per path | ||
643 | * A solution to this is to fork glob() and | ||
644 | * build a sftp specific version which keeps the | ||
645 | * attribs (which currently get thrown away) | ||
646 | * that the server returns as well as the filenames. | ||
647 | */ | ||
648 | memset(&sb, 0, sizeof(sb)); | ||
649 | a = do_lstat(conn, g.gl_pathv[i], 1); | ||
650 | if (a != NULL) | ||
651 | attrib_to_stat(a, &sb); | ||
652 | lname = ls_file(fname, &sb, 1); | ||
653 | printf("%s\n", lname); | ||
654 | xfree(lname); | ||
655 | } else { | ||
656 | /* XXX - multicolumn display would be nice here */ | ||
657 | printf("%s\n", fname); | ||
658 | } | ||
659 | xfree(fname); | ||
660 | } | ||
661 | |||
662 | if (g.gl_pathc) | ||
663 | globfree(&g); | ||
664 | |||
665 | return (0); | ||
666 | } | ||
667 | |||
668 | static int | ||
669 | parse_args(const char **cpp, int *pflag, int *lflag, | ||
670 | unsigned long *n_arg, char **path1, char **path2) | ||
509 | { | 671 | { |
510 | const char *cmd, *cp = *cpp; | 672 | const char *cmd, *cp = *cpp; |
511 | char *cp2; | 673 | char *cp2; |
@@ -545,7 +707,7 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, | |||
545 | } | 707 | } |
546 | 708 | ||
547 | /* Get arguments and parse flags */ | 709 | /* Get arguments and parse flags */ |
548 | *pflag = *n_arg = 0; | 710 | *lflag = *pflag = *n_arg = 0; |
549 | *path1 = *path2 = NULL; | 711 | *path1 = *path2 = NULL; |
550 | switch (cmdnum) { | 712 | switch (cmdnum) { |
551 | case I_GET: | 713 | case I_GET: |
@@ -592,6 +754,8 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, | |||
592 | } | 754 | } |
593 | break; | 755 | break; |
594 | case I_LS: | 756 | case I_LS: |
757 | if (parse_ls_flags(&cp, lflag)) | ||
758 | return(-1); | ||
595 | /* Path is optional */ | 759 | /* Path is optional */ |
596 | if (get_pathname(&cp, path1)) | 760 | if (get_pathname(&cp, path1)) |
597 | return(-1); | 761 | return(-1); |
@@ -652,7 +816,7 @@ static int | |||
652 | parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) | 816 | parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) |
653 | { | 817 | { |
654 | char *path1, *path2, *tmp; | 818 | char *path1, *path2, *tmp; |
655 | int pflag, cmdnum, i; | 819 | int pflag, lflag, cmdnum, i; |
656 | unsigned long n_arg; | 820 | unsigned long n_arg; |
657 | Attrib a, *aa; | 821 | Attrib a, *aa; |
658 | char path_buf[MAXPATHLEN]; | 822 | char path_buf[MAXPATHLEN]; |
@@ -660,7 +824,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) | |||
660 | glob_t g; | 824 | glob_t g; |
661 | 825 | ||
662 | path1 = path2 = NULL; | 826 | path1 = path2 = NULL; |
663 | cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); | 827 | cmdnum = parse_args(&cmd, &pflag, &lflag, &n_arg, |
828 | &path1, &path2); | ||
664 | 829 | ||
665 | memset(&g, 0, sizeof(g)); | 830 | memset(&g, 0, sizeof(g)); |
666 | 831 | ||
@@ -732,22 +897,18 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) | |||
732 | break; | 897 | break; |
733 | case I_LS: | 898 | case I_LS: |
734 | if (!path1) { | 899 | if (!path1) { |
735 | do_ls(conn, *pwd); | 900 | do_globbed_ls(conn, *pwd, *pwd, lflag); |
736 | break; | 901 | break; |
737 | } | 902 | } |
903 | |||
904 | /* Strip pwd off beginning of non-absolute paths */ | ||
905 | tmp = NULL; | ||
906 | if (*path1 != '/') | ||
907 | tmp = *pwd; | ||
908 | |||
738 | path1 = make_absolute(path1, *pwd); | 909 | path1 = make_absolute(path1, *pwd); |
739 | if ((tmp = do_realpath(conn, path1)) == NULL) | 910 | |
740 | break; | 911 | do_globbed_ls(conn, path1, tmp, lflag); |
741 | xfree(path1); | ||
742 | path1 = tmp; | ||
743 | if ((aa = do_stat(conn, path1, 0)) == NULL) | ||
744 | break; | ||
745 | if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && | ||
746 | !S_ISDIR(aa->perm)) { | ||
747 | error("Can't ls: \"%s\" is not a directory", path1); | ||
748 | break; | ||
749 | } | ||
750 | do_ls(conn, path1); | ||
751 | break; | 912 | break; |
752 | case I_LCHDIR: | 913 | case I_LCHDIR: |
753 | if (chdir(path1) == -1) { | 914 | if (chdir(path1) == -1) { |