diff --git a/ChangeLog b/ChangeLog index f29bfd731..f6588cca3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,12 @@ kernel in kern(9), and remove it from OpenSSH. ok deraadt@, djm@ NB. re-added under openbsd-compat/ for portable OpenSSH + - djm@cvs.openbsd.org 2010/09/25 09:30:16 + [sftp.c configure.ac openbsd-compat/glob.c openbsd-compat/glob.h] + make use of new glob(3) GLOB_KEEPSTAT extension to save extra server + rountrips to fetch per-file stat(2) information. + NB. update openbsd-compat/ glob(3) implementation from OpenBSD libc to + match. 20100924 - (djm) OpenBSD CVS Sync diff --git a/configure.ac b/configure.ac index 9b67e3d47..4deb0fe2e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id: configure.ac,v 1.453 2010/10/07 10:25:28 djm Exp $ +# $Id: configure.ac,v 1.454 2010/10/07 10:39:17 djm Exp $ # # Copyright (c) 1999-2004 Damien Miller # @@ -15,7 +15,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT(OpenSSH, Portable, openssh-unix-dev@mindrot.org) -AC_REVISION($Revision: 1.453 $) +AC_REVISION($Revision: 1.454 $) AC_CONFIG_SRCDIR([ssh.c]) AC_CONFIG_HEADER(config.h) @@ -1118,6 +1118,28 @@ AC_TRY_COMPILE( ] ) +# Check for g.gl_statv glob() extension +AC_MSG_CHECKING(for gl_statv and GLOB_KEEPSTAT extensions for glob) +AC_TRY_COMPILE( + [ #include <glob.h> ], + [ +#ifndef GLOB_KEEPSTAT +#error "glob does not support GLOB_KEEPSTAT extension" +#endif +glob_t g; +g.gl_statv = NULL; +], + [ + AC_DEFINE(GLOB_HAS_GL_STATV, 1, + [Define if your system glob() function has + gl_statv options in glob_t]) + AC_MSG_RESULT(yes) + ], + [ + AC_MSG_RESULT(no) + ] +) + AC_CHECK_DECLS(GLOB_NOMATCH, , , [#include <glob.h>]) AC_MSG_CHECKING([whether struct dirent allocates space for d_name]) diff --git a/openbsd-compat/charclass.h b/openbsd-compat/charclass.h new file mode 100644 index 000000000..91f517447 --- /dev/null +++ b/openbsd-compat/charclass.h @@ -0,0 +1,31 @@ +/* + * Public domain, 2008, Todd C. Miller <Todd.Miller@courtesan.com> + * + * $OpenBSD: charclass.h,v 1.1 2008/10/01 23:04:13 millert Exp $ + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/charclass.h */ + +/* + * POSIX character class support for fnmatch() and glob(). + */ +static struct cclass { + const char *name; + int (*isctype)(int); +} cclasses[] = { + { "alnum", isalnum }, + { "alpha", isalpha }, + { "blank", isblank }, + { "cntrl", iscntrl }, + { "digit", isdigit }, + { "graph", isgraph }, + { "lower", islower }, + { "print", isprint }, + { "punct", ispunct }, + { "space", isspace }, + { "upper", isupper }, + { "xdigit", isxdigit }, + { NULL, NULL } +}; + +#define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1) diff --git a/openbsd-compat/glob.c b/openbsd-compat/glob.c index 74b506403..7bbe6c71a 100644 --- a/openbsd-compat/glob.c +++ b/openbsd-compat/glob.c @@ -1,4 +1,4 @@ -/* $OpenBSD: glob.c,v 1.26 2005/11/28 17:50:12 deraadt Exp $ */ +/* $OpenBSD: glob.c,v 1.33 2010/09/26 22:15:39 djm Exp $ */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,36 +33,6 @@ /* OPENBSD ORIGINAL: lib/libc/gen/glob.c */ -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> - -#include <dirent.h> -#include <ctype.h> -#include <errno.h> -#include <pwd.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ - !defined(GLOB_HAS_GL_MATCHC) || \ - !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ - defined(BROKEN_GLOB) - -static long -get_arg_max(void) -{ -#ifdef ARG_MAX - return(ARG_MAX); -#elif defined(HAVE_SYSCONF) && defined(_SC_ARG_MAX) - return(sysconf(_SC_ARG_MAX)); -#else - return(256); /* XXX: arbitrary */ -#endif -} - /* * glob(3) -- a superset of the one defined in POSIX 1003.2. * @@ -88,6 +58,37 @@ get_arg_max(void) * Number of matches in the current invocation of glob. */ +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <ctype.h> +#include <errno.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ + !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ + defined(BROKEN_GLOB) + +static long +get_arg_max(void) +{ +#ifdef ARG_MAX + return(ARG_MAX); +#elif defined(HAVE_SYSCONF) && defined(_SC_ARG_MAX) + return(sysconf(_SC_ARG_MAX)); +#else + return(256); /* XXX: arbitrary */ +#endif +} + +#include "charclass.h" #define DOLLAR '$' #define DOT '.' @@ -100,7 +101,6 @@ get_arg_max(void) #define RBRACKET ']' #define SEP '/' #define STAR '*' -#undef TILDE /* Some platforms may already define it */ #define TILDE '~' #define UNDERSCORE '_' #define LBRACE '{' @@ -137,6 +137,7 @@ typedef char Char; #define M_ONE META('?') #define M_RNG META('-') #define M_SET META('[') +#define M_CLASS META(':') #define ismeta(c) (((c)&M_QUOTE) != 0) @@ -144,7 +145,8 @@ static int compare(const void *, const void *); static int g_Ctoc(const Char *, char *, u_int); static int g_lstat(Char *, struct stat *, glob_t *); static DIR *g_opendir(Char *, glob_t *); -static Char *g_strchr(Char *, int); +static Char *g_strchr(const Char *, int); +static int g_strncmp(const Char *, const char *, size_t); static int g_stat(Char *, struct stat *, glob_t *); static int glob0(const Char *, glob_t *); static int glob1(Char *, Char *, glob_t *, size_t *); @@ -152,11 +154,11 @@ static int glob2(Char *, Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *); static int glob3(Char *, Char *, Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *); -static int globextend(const Char *, glob_t *, size_t *); +static int globextend(const Char *, glob_t *, size_t *, struct stat *); static const Char * globtilde(const Char *, Char *, size_t, glob_t *); static int globexp1(const Char *, glob_t *); -static int globexp2(const Char *, const Char *, glob_t *, int *); +static int globexp2(const Char *, const Char *, glob_t *); static int match(Char *, Char *, Char *); #ifdef DEBUG static void qprintf(const char *, Char *); @@ -174,6 +176,7 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; + pglob->gl_statv = NULL; if (!(flags & GLOB_DOOFFS)) pglob->gl_offs = 0; } @@ -215,15 +218,13 @@ static int globexp1(const Char *pattern, glob_t *pglob) { const Char* ptr = pattern; - int rv; /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) return glob0(pattern, pglob); - while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL) - if (!globexp2(ptr, pattern, pglob, &rv)) - return rv; + if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) + return globexp2(ptr, pattern, pglob); return glob0(pattern, pglob); } @@ -235,9 +236,9 @@ globexp1(const Char *pattern, glob_t *pglob) * If it fails then it tries to glob the rest of the pattern and returns. */ static int -globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob) { - int i; + int i, rv; Char *lm, *ls; const Char *pe, *pm, *pl; Char patbuf[MAXPATHLEN]; @@ -270,10 +271,8 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) } /* Non matching braces; just glob the pattern */ - if (i != 0 || *pe == EOS) { - *rv = glob0(patbuf, pglob); - return 0; - } + if (i != 0 || *pe == EOS) + return glob0(patbuf, pglob); for (i = 0, pl = pm = ptr; pm <= pe; pm++) { switch (*pm) { @@ -319,7 +318,9 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) #ifdef DEBUG qprintf("globexp2:", patbuf); #endif - *rv = globexp1(patbuf, pglob); + rv = globexp1(patbuf, pglob); + if (rv && rv != GLOB_NOMATCH) + return rv; /* move after the comma, to the next string */ pl = pm + 1; @@ -330,7 +331,6 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) break; } } - *rv = 0; return 0; } @@ -399,6 +399,47 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) return patbuf; } +static int +g_strncmp(const Char *s1, const char *s2, size_t n) +{ + int rv = 0; + + while (n--) { + rv = *(Char *)s1 - *(const unsigned char *)s2++; + if (rv) + break; + if (*s1++ == '\0') + break; + } + return rv; +} + +static int +g_charclass(const Char **patternp, Char **bufnextp) +{ + const Char *pattern = *patternp + 1; + Char *bufnext = *bufnextp; + const Char *colon; + struct cclass *cc; + size_t len; + + if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') + return 1; /* not a character class */ + + len = (size_t)(colon - pattern); + for (cc = cclasses; cc->name != NULL; cc++) { + if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') + break; + } + if (cc->name == NULL) + return -1; /* invalid character class */ + *bufnext++ = M_CLASS; + *bufnext++ = (Char)(cc - &cclasses[0]); + *bufnextp = bufnext; + *patternp += len + 3; + + return 0; +} /* * The main glob() routine: compiles the pattern (optionally processing @@ -427,7 +468,7 @@ glob0(const Char *pattern, glob_t *pglob) if (c == NOT) ++qpatnext; if (*qpatnext == EOS || - g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) { + g_strchr(qpatnext+1, RBRACKET) == NULL) { *bufnext++ = LBRACKET; if (c == NOT) --qpatnext; @@ -438,6 +479,20 @@ glob0(const Char *pattern, glob_t *pglob) *bufnext++ = M_NOT; c = *qpatnext++; do { + if (c == LBRACKET && *qpatnext == ':') { + do { + err = g_charclass(&qpatnext, + &bufnext); + if (err) + break; + c = *qpatnext++; + } while (c == LBRACKET && *qpatnext == ':'); + if (err == -1 && + !(pglob->gl_flags & GLOB_NOCHECK)) + return GLOB_NOMATCH; + if (c == RBRACKET) + break; + } *bufnext++ = CHAR(c); if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) { @@ -484,7 +539,7 @@ glob0(const Char *pattern, glob_t *pglob) if ((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))) - return(globextend(pattern, pglob, &limit)); + return(globextend(pattern, pglob, &limit, NULL)); else return(GLOB_NOMATCH); } @@ -547,7 +602,7 @@ glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, *pathend = EOS; } ++pglob->gl_matchc; - return(globextend(pathbuf, pglob, limitp)); + return(globextend(pathbuf, pglob, limitp, &sb)); } /* Find end of next segment, copy tentatively to pathend. */ @@ -670,25 +725,40 @@ glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(const Char *path, glob_t *pglob, size_t *limitp) +globextend(const Char *path, glob_t *pglob, size_t *limitp, struct stat *sb) { char **pathv; - int i; - u_int newsize, len; - char *copy; + ssize_t i; + size_t newn, len; + char *copy = NULL; const Char *p; + struct stat **statv; - newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); - pathv = pglob->gl_pathv ? realloc((char *)pglob->gl_pathv, newsize) : - malloc(newsize); - if (pathv == NULL) { + newn = 2 + pglob->gl_pathc + pglob->gl_offs; + if (SIZE_MAX / sizeof(*pathv) <= newn || + SIZE_MAX / sizeof(*statv) <= newn) { + nospace: + for (i = pglob->gl_offs; i < newn - 2; i++) { + if (pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_pathv[i]); + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 && + pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_statv[i]); + } if (pglob->gl_pathv) { free(pglob->gl_pathv); pglob->gl_pathv = NULL; } + if (pglob->gl_statv) { + free(pglob->gl_statv); + pglob->gl_statv = NULL; + } return(GLOB_NOSPACE); } + pathv = realloc(pglob->gl_pathv, newn * sizeof(*pathv)); + if (pathv == NULL) + goto nospace; if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { /* first time around -- clear initial gl_offs items */ pathv += pglob->gl_offs; @@ -697,6 +767,29 @@ globextend(const Char *path, glob_t *pglob, size_t *limitp) } pglob->gl_pathv = pathv; + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) { + statv = realloc(pglob->gl_statv, newn * sizeof(*statv)); + if (statv == NULL) + goto nospace; + if (pglob->gl_statv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + statv += pglob->gl_offs; + for (i = pglob->gl_offs; --i >= 0; ) + *--statv = NULL; + } + pglob->gl_statv = statv; + if (sb == NULL) + statv[pglob->gl_offs + pglob->gl_pathc] = NULL; + else { + if ((statv[pglob->gl_offs + pglob->gl_pathc] = + malloc(sizeof(**statv))) == NULL) + goto copy_error; + memcpy(statv[pglob->gl_offs + pglob->gl_pathc], sb, + sizeof(*sb)); + } + statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; + } + for (p = path; *p++;) ; len = (size_t)(p - path); @@ -711,11 +804,11 @@ globextend(const Char *path, glob_t *pglob, size_t *limitp) pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; if ((pglob->gl_flags & GLOB_LIMIT) && - newsize + *limitp >= (u_int) get_arg_max()) { + (newn * sizeof(*pathv)) + *limitp >= ARG_MAX) { errno = 0; return(GLOB_NOSPACE); } - + copy_error: return(copy == NULL ? GLOB_NOSPACE : 0); } @@ -751,13 +844,21 @@ match(Char *name, Char *pat, Char *patend) return(0); if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) ++pat; - while (((c = *pat++) & M_MASK) != M_END) + while (((c = *pat++) & M_MASK) != M_END) { + if ((c & M_MASK) == M_CLASS) { + int idx = *pat & M_MASK; + if (idx < NCCLASSES && + cclasses[idx].isctype(k)) + ok = 1; + ++pat; + } if ((*pat & M_MASK) == M_RNG) { if (c <= k && k <= pat[1]) ok = 1; pat += 2; } else if (c == k) ok = 1; + } if (ok == negate_range) return(0); break; @@ -785,6 +886,14 @@ globfree(glob_t *pglob) free(pglob->gl_pathv); pglob->gl_pathv = NULL; } + if (pglob->gl_statv != NULL) { + for (i = 0; i < pglob->gl_pathc; i++) { + if (pglob->gl_statv[i] != NULL) + free(pglob->gl_statv[i]); + } + free(pglob->gl_statv); + pglob->gl_statv = NULL; + } } static DIR * @@ -830,11 +939,11 @@ g_stat(Char *fn, struct stat *sb, glob_t *pglob) } static Char * -g_strchr(Char *str, int ch) +g_strchr(const Char *str, int ch) { do { if (*str == ch) - return (str); + return ((Char *)str); } while (*str++); return (NULL); } @@ -870,5 +979,4 @@ qprintf(const char *str, Char *s) #endif #endif /* !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || - !defined(GLOB_HAS_GL_MATCHC) */ - + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) */ diff --git a/openbsd-compat/glob.h b/openbsd-compat/glob.h index a2b36f974..8ea391306 100644 --- a/openbsd-compat/glob.h +++ b/openbsd-compat/glob.h @@ -1,4 +1,4 @@ -/* $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $ */ +/* $OpenBSD: glob.h,v 1.11 2010/09/24 13:32:55 djm Exp $ */ /* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */ /* @@ -38,13 +38,16 @@ /* OPENBSD ORIGINAL: include/glob.h */ #if !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC) || \ - !defined(GLOB_HAS_GL_MATCHC) || \ + !defined(GLOB_HAS_GL_MATCHC) || !define(GLOB_HAS_GL_STATV) \ !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ defined(BROKEN_GLOB) #ifndef _GLOB_H_ #define _GLOB_H_ +#include <sys/cdefs.h> +#include <sys/stat.h> + struct stat; typedef struct { int gl_pathc; /* Count of total paths so far. */ @@ -52,6 +55,7 @@ typedef struct { int gl_offs; /* Reserved at beginning of gl_pathv. */ int gl_flags; /* Copy of flags parameter to glob. */ char **gl_pathv; /* List of paths matching pattern. */ + struct stat **gl_statv; /* Stat entries corresponding to gl_pathv */ /* Copy of errfunc parameter to glob. */ int (*gl_errfunc)(const char *, int); @@ -75,12 +79,10 @@ typedef struct { #define GLOB_NOSORT 0x0020 /* Don't sort. */ #define GLOB_NOESCAPE 0x1000 /* Disable backslash escaping. */ -/* Error values returned by glob(3) */ #define GLOB_NOSPACE (-1) /* Malloc call failed. */ #define GLOB_ABORTED (-2) /* Unignored error. */ #define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */ #define GLOB_NOSYS (-4) /* Function not supported. */ -#define GLOB_ABEND GLOB_ABORTED #define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ #define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ @@ -89,6 +91,8 @@ typedef struct { #define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ #define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ #define GLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */ +#define GLOB_KEEPSTAT 0x4000 /* Retain stat data for paths in gl_statv. */ +#define GLOB_ABEND GLOB_ABORTED /* backward compatibility */ int glob(const char *, int, int (*)(const char *, int), glob_t *); void globfree(glob_t *); @@ -96,5 +100,5 @@ void globfree(glob_t *); #endif /* !_GLOB_H_ */ #endif /* !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC) || - !defined(GLOB_HAS_GL_MATCHC */ + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOH_HAS_GL_STATV) */ diff --git a/sftp.c b/sftp.c index f6cadd113..46bee1982 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.127 2010/09/23 13:34:43 jmc Exp $ */ +/* $OpenBSD: sftp.c,v 1.128 2010/09/25 09:30:16 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -761,15 +761,18 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, glob_t g; u_int i, c = 1, colspace = 0, columns = 1; Attrib *a = NULL; + int err; + char *fname, *lname; memset(&g, 0, sizeof(g)); - if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, - NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { + if (remote_glob(conn, path, + GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT, NULL, &g) || + (g.gl_pathc && !g.gl_matchc)) { if (g.gl_pathc) globfree(&g); error("Can't ls: \"%s\" not found", path); - return (-1); + return -1; } if (interrupted) @@ -779,19 +782,11 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, * If the glob returns a single match and it is a directory, * then just list its contents. */ - if (g.gl_matchc == 1) { - if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { - globfree(&g); - return (-1); - } - if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && - S_ISDIR(a->perm)) { - int err; - - err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); - globfree(&g); - return (err); - } + if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && + S_ISDIR(g.gl_statv[0]->st_mode)) { + err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); + globfree(&g); + return err; } if (!(lflag & LS_SHORT_VIEW)) { @@ -811,27 +806,14 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, } for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { - char *fname; - fname = path_strip(g.gl_pathv[i], strip_path); - if (lflag & LS_LONG_VIEW) { - char *lname; - struct stat sb; - - /* - * 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)); - if (a == NULL) - a = do_lstat(conn, g.gl_pathv[i], 1); - if (a != NULL) - attrib_to_stat(a, &sb); - lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); + if (g.gl_statv[i] == NULL) { + error("no stat information for %s", fname); + continue; + } + lname = ls_file(fname, g.gl_statv[i], 1, + (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else { @@ -852,7 +834,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, if (g.gl_pathc) globfree(&g); - return (0); + return 0; } static int