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