diff options
-rw-r--r-- | usr.bin/ssh/sftp-client.c | 8 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-client.h | 5 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-common.c | 64 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-common.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-glob.c | 21 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-glob.h | 5 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-int.c | 204 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server.c | 46 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.1 | 12 |
9 files changed, 265 insertions, 104 deletions
diff --git a/usr.bin/ssh/sftp-client.c b/usr.bin/ssh/sftp-client.c index 9741e022dce..0bea66c9500 100644 --- a/usr.bin/ssh/sftp-client.c +++ b/usr.bin/ssh/sftp-client.c @@ -28,7 +28,7 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.34 2002/06/27 10:35:47 deraadt Exp $"); +RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm Exp $"); #include <sys/queue.h> @@ -415,12 +415,6 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, } int -do_ls(struct sftp_conn *conn, char *path) -{ - return(do_lsreaddir(conn, path, 1, NULL)); -} - -int do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) { return(do_lsreaddir(conn, path, 0, dir)); diff --git a/usr.bin/ssh/sftp-client.h b/usr.bin/ssh/sftp-client.h index b0617116816..98e08ffa70d 100644 --- a/usr.bin/ssh/sftp-client.h +++ b/usr.bin/ssh/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.10 2002/06/23 09:30:14 deraadt Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.11 2002/09/11 22:41:50 djm Exp $ */ /* * Copyright (c) 2001,2002 Damien Miller. All rights reserved. @@ -48,9 +48,6 @@ u_int sftp_proto_version(struct sftp_conn *); /* Close file referred to by 'handle' */ int do_close(struct sftp_conn *, char *, u_int); -/* List contents of directory 'path' to stdout */ -int do_ls(struct sftp_conn *, char *); - /* Read contents of 'path' to NULL-terminated array 'dir' */ int do_readdir(struct sftp_conn *, char *, SFTP_DIRENT ***); diff --git a/usr.bin/ssh/sftp-common.c b/usr.bin/ssh/sftp-common.c index 6bed0ab8a06..082345486b4 100644 --- a/usr.bin/ssh/sftp-common.c +++ b/usr.bin/ssh/sftp-common.c @@ -24,7 +24,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sftp-common.c,v 1.6 2002/06/23 09:30:14 deraadt Exp $"); +RCSID("$OpenBSD: sftp-common.c,v 1.7 2002/09/11 22:41:50 djm Exp $"); #include "buffer.h" #include "bufaux.h" @@ -65,6 +65,26 @@ stat_to_attrib(struct stat *st, Attrib *a) a->mtime = st->st_mtime; } +/* Convert from filexfer attribs to struct stat */ +void +attrib_to_stat(Attrib *a, struct stat *st) +{ + memset(st, 0, sizeof(*st)); + + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + st->st_size = a->size; + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + st->st_uid = a->uid; + st->st_gid = a->gid; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + st->st_mode = a->perm; + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + st->st_atime = a->atime; + st->st_mtime = a->mtime; + } +} + /* Decode attributes in buffer */ Attrib * decode_attrib(Buffer *b) @@ -149,3 +169,45 @@ fx2txt(int status) } /* NOTREACHED */ } + +/* + * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh + */ +char * +ls_file(char *name, struct stat *st, int remote) +{ + int ulen, glen, sz = 0; + struct passwd *pw; + struct group *gr; + struct tm *ltime = localtime(&st->st_mtime); + char *user, *group; + char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + + strmode(st->st_mode, mode); + if (!remote && (pw = getpwuid(st->st_uid)) != NULL) { + user = pw->pw_name; + } else { + snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); + user = ubuf; + } + if (!remote && (gr = getgrgid(st->st_gid)) != NULL) { + group = gr->gr_name; + } else { + snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); + group = gbuf; + } + if (ltime != NULL) { + if (time(NULL) - st->st_mtime < (365*24*60*60)/2) + sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); + else + sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); + } + if (sz == 0) + tbuf[0] = '\0'; + ulen = MAX(strlen(user), 8); + glen = MAX(strlen(group), 8); + snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, + st->st_nlink, ulen, user, glen, group, + (u_int64_t)st->st_size, tbuf, name); + return xstrdup(buf); +} diff --git a/usr.bin/ssh/sftp-common.h b/usr.bin/ssh/sftp-common.h index 4c126bf1065..201611cc4bc 100644 --- a/usr.bin/ssh/sftp-common.h +++ b/usr.bin/ssh/sftp-common.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.h,v 1.3 2001/06/26 17:27:24 markus Exp $ */ +/* $OpenBSD: sftp-common.h,v 1.4 2002/09/11 22:41:50 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -40,7 +40,9 @@ struct Attrib { void attrib_clear(Attrib *); void stat_to_attrib(struct stat *, Attrib *); +void attrib_to_stat(Attrib *, struct stat *); Attrib *decode_attrib(Buffer *); void encode_attrib(Buffer *, Attrib *); +char *ls_file(char *, struct stat *, int); const char *fx2txt(int); diff --git a/usr.bin/ssh/sftp-glob.c b/usr.bin/ssh/sftp-glob.c index b9d4e28961f..3f8b8bd5287 100644 --- a/usr.bin/ssh/sftp-glob.c +++ b/usr.bin/ssh/sftp-glob.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sftp-glob.c,v 1.12 2002/07/04 04:15:33 deraadt Exp $"); +RCSID("$OpenBSD: sftp-glob.c,v 1.13 2002/09/11 22:41:50 djm Exp $"); #include <glob.h> @@ -85,25 +85,6 @@ fudge_closedir(struct SFTP_OPENDIR *od) xfree(od); } -static void -attrib_to_stat(Attrib *a, struct stat *st) -{ - memset(st, 0, sizeof(*st)); - - if (a->flags & SSH2_FILEXFER_ATTR_SIZE) - st->st_size = a->size; - if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { - st->st_uid = a->uid; - st->st_gid = a->gid; - } - if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) - st->st_mode = a->perm; - if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { - st->st_atime = a->atime; - st->st_mtime = a->mtime; - } -} - static int fudge_lstat(const char *path, struct stat *st) { diff --git a/usr.bin/ssh/sftp-glob.h b/usr.bin/ssh/sftp-glob.h index 9c754912c22..f879e8719b4 100644 --- a/usr.bin/ssh/sftp-glob.h +++ b/usr.bin/ssh/sftp-glob.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-glob.h,v 1.7 2002/03/19 10:49:35 markus Exp $ */ +/* $OpenBSD: sftp-glob.h,v 1.8 2002/09/11 22:41:50 djm Exp $ */ /* * Copyright (c) 2001,2002 Damien Miller. All rights reserved. @@ -31,8 +31,7 @@ #include "sftp-client.h" -int -remote_glob(struct sftp_conn *, const char *, int, +int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); #endif diff --git a/usr.bin/ssh/sftp-int.c b/usr.bin/ssh/sftp-int.c index 3d8503585be..02a1e297ba2 100644 --- a/usr.bin/ssh/sftp-int.c +++ b/usr.bin/ssh/sftp-int.c @@ -22,11 +22,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* XXX: globbed ls */ /* XXX: recursive operations */ #include "includes.h" -RCSID("$OpenBSD: sftp-int.c,v 1.47 2002/06/23 09:30:14 deraadt Exp $"); +RCSID("$OpenBSD: sftp-int.c,v 1.48 2002/09/11 22:41:50 djm Exp $"); #include <glob.h> @@ -203,6 +202,26 @@ local_do_ls(const char *args) } } +/* Strip one path (usually the pwd) from the start of another */ +static char * +path_strip(char *path, char *strip) +{ + size_t len; + char *ret; + + if (strip == NULL) + return (xstrdup(path)); + + len = strlen(strip); + if (strip != NULL && strncmp(path, strip, len) == 0) { + if (strip[len - 1] != '/' && path[len] == '/') + len++; + return (xstrdup(path + len)); + } + + return (xstrdup(path)); +} + static char * path_append(char *p1, char *p2) { @@ -211,7 +230,7 @@ path_append(char *p1, char *p2) ret = xmalloc(len); strlcpy(ret, p1, len); - if (strcmp(p1, "/") != 0) + if (p1[strlen(p1) - 1] != '/') strlcat(ret, "/", len); strlcat(ret, p2, len); @@ -276,6 +295,29 @@ parse_getput_flags(const char **cpp, int *pflag) } static int +parse_ls_flags(const char **cpp, int *lflag) +{ + const char *cp = *cpp; + + /* Check for flags */ + if (cp++[0] == '-') { + for(; strchr(WHITESPACE, *cp) == NULL; cp++) { + switch (*cp) { + case 'l': + *lflag = 1; + break; + default: + error("Invalid flag -%c", *cp); + return(-1); + } + } + *cpp = cp + strspn(cp, WHITESPACE); + } + + return(0); +} + +static int get_pathname(const char **cpp, char **path) { const char *cp = *cpp, *end; @@ -506,8 +548,129 @@ out: } static int -parse_args(const char **cpp, int *pflag, unsigned long *n_arg, - char **path1, char **path2) +sdirent_comp(const void *aa, const void *bb) +{ + SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; + SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; + + return (strcmp(a->filename, b->filename)); +} + +/* sftp ls.1 replacement for directories */ +static int +do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) +{ + int n; + SFTP_DIRENT **d; + + if ((n = do_readdir(conn, path, &d)) != 0) + return (n); + + /* Count entries for sort */ + for (n = 0; d[n] != NULL; n++) + ; + + qsort(d, n, sizeof(*d), sdirent_comp); + + for (n = 0; d[n] != NULL; n++) { + char *tmp, *fname; + + tmp = path_append(path, d[n]->filename); + fname = path_strip(tmp, strip_path); + xfree(tmp); + + if (lflag) { + char *lname; + struct stat sb; + + memset(&sb, 0, sizeof(sb)); + attrib_to_stat(&d[n]->a, &sb); + lname = ls_file(fname, &sb, 1); + printf("%s\n", lname); + xfree(lname); + } else { + /* XXX - multicolumn display would be nice here */ + printf("%s\n", fname); + } + + xfree(fname); + } + + free_sftp_dirents(d); + return (0); +} + +/* sftp ls.1 replacement which handles path globs */ +static int +do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, + int lflag) +{ + glob_t g; + int i; + Attrib *a; + struct stat sb; + + memset(&g, 0, sizeof(g)); + + if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, + NULL, &g)) { + error("Can't ls: \"%s\" not found", path); + return (-1); + } + + /* + * If the glob returns a single match, which is the same as the + * input glob, and it is a directory, then just list its contents + */ + if (g.gl_pathc == 1 && + strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) { + if ((a = do_lstat(conn, path, 1)) == NULL) { + globfree(&g); + return (-1); + } + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + S_ISDIR(a->perm)) { + globfree(&g); + return (do_ls_dir(conn, path, strip_path, lflag)); + } + } + + for (i = 0; g.gl_pathv[i]; i++) { + char *fname, *lname; + + fname = path_strip(g.gl_pathv[i], strip_path); + + if (lflag) { + /* + * XXX: this is slow - 1 roundtrip per path + * A solution to this is to fork glob() and + * build a sftp specific version which keeps the + * attribs (which currently get thrown away) + * that the server returns as well as the filenames. + */ + memset(&sb, 0, sizeof(sb)); + a = do_lstat(conn, g.gl_pathv[i], 1); + if (a != NULL) + attrib_to_stat(a, &sb); + lname = ls_file(fname, &sb, 1); + printf("%s\n", lname); + xfree(lname); + } else { + /* XXX - multicolumn display would be nice here */ + printf("%s\n", fname); + } + xfree(fname); + } + + if (g.gl_pathc) + globfree(&g); + + return (0); +} + +static int +parse_args(const char **cpp, int *pflag, int *lflag, + unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2; @@ -547,7 +710,7 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, } /* Get arguments and parse flags */ - *pflag = *n_arg = 0; + *lflag = *pflag = *n_arg = 0; *path1 = *path2 = NULL; switch (cmdnum) { case I_GET: @@ -594,6 +757,8 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, } break; case I_LS: + if (parse_ls_flags(&cp, lflag)) + return(-1); /* Path is optional */ if (get_pathname(&cp, path1)) return(-1); @@ -654,7 +819,7 @@ static int parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) { char *path1, *path2, *tmp; - int pflag, cmdnum, i; + int pflag, lflag, cmdnum, i; unsigned long n_arg; Attrib a, *aa; char path_buf[MAXPATHLEN]; @@ -662,7 +827,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) glob_t g; path1 = path2 = NULL; - cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); + cmdnum = parse_args(&cmd, &pflag, &lflag, &n_arg, + &path1, &path2); memset(&g, 0, sizeof(g)); @@ -734,22 +900,18 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd) break; case I_LS: if (!path1) { - do_ls(conn, *pwd); + do_globbed_ls(conn, *pwd, *pwd, lflag); break; } + + /* Strip pwd off beginning of non-absolute paths */ + tmp = NULL; + if (*path1 != '/') + tmp = *pwd; + path1 = make_absolute(path1, *pwd); - if ((tmp = do_realpath(conn, path1)) == NULL) - break; - xfree(path1); - path1 = tmp; - if ((aa = do_stat(conn, path1, 0)) == NULL) - break; - if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && - !S_ISDIR(aa->perm)) { - error("Can't ls: \"%s\" is not a directory", path1); - break; - } - do_ls(conn, path1); + + do_globbed_ls(conn, path1, tmp, lflag); break; case I_LCHDIR: if (chdir(path1) == -1) { diff --git a/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index 6bd8a2b050b..e3c3e70c808 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/sftp-server.c @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: sftp-server.c,v 1.37 2002/06/24 17:57:20 deraadt Exp $"); +RCSID("$OpenBSD: sftp-server.c,v 1.38 2002/09/11 22:41:50 djm Exp $"); #include "buffer.h" #include "bufaux.h" @@ -675,48 +675,6 @@ process_opendir(void) xfree(path); } -/* - * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh - */ -static char * -ls_file(char *name, struct stat *st) -{ - int ulen, glen, sz = 0; - struct passwd *pw; - struct group *gr; - struct tm *ltime = localtime(&st->st_mtime); - char *user, *group; - char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; - - strmode(st->st_mode, mode); - if ((pw = getpwuid(st->st_uid)) != NULL) { - user = pw->pw_name; - } else { - snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); - user = ubuf; - } - if ((gr = getgrgid(st->st_gid)) != NULL) { - group = gr->gr_name; - } else { - snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); - group = gbuf; - } - if (ltime != NULL) { - if (time(NULL) - st->st_mtime < (365*24*60*60)/2) - sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); - else - sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); - } - if (sz == 0) - tbuf[0] = '\0'; - ulen = MAX(strlen(user), 8); - glen = MAX(strlen(group), 8); - snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, - st->st_nlink, ulen, user, glen, group, - (unsigned long long)st->st_size, tbuf, name); - return xstrdup(buf); -} - static void process_readdir(void) { @@ -752,7 +710,7 @@ process_readdir(void) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); - stats[count].long_name = ls_file(dp->d_name, &st); + stats[count].long_name = ls_file(dp->d_name, &st, 0); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ diff --git a/usr.bin/ssh/sftp.1 b/usr.bin/ssh/sftp.1 index 0e6d741a9ca..33ceb659640 100644 --- a/usr.bin/ssh/sftp.1 +++ b/usr.bin/ssh/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.35 2002/06/20 20:00:05 stevesk Exp $ +.\" $OpenBSD: sftp.1,v 1.36 2002/09/11 22:41:50 djm Exp $ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" @@ -203,12 +203,18 @@ to .Ar newpath . .It Ic lpwd Print local working directory. -.It Ic ls Op Ar path +.It Xo Ic ls +.Op Ar flags +.Op Ar path +.Xc Display remote directory listing of either .Ar path or current directory if .Ar path -is not specified. +is not specified. If the +.Fl l +flag is specified, then display additional details including permissions +and ownership information. .It Ic lumask Ar umask Set local umask to .Ar umask . |