Hi,
In sftp we use a few of the BSD extensions to glob(3), most notably the
alternate open/readdir and stat functions so we can replace local fs
access with remote operations tunnelled over sftp.
In sftp these remote operations are slow, so we would like to do as few
of them as possible. Unfortunately, glob(3) throws away some information
that we have to refetch - it performs lstat() operations in the course
of building its output, but doesn't save them anywhere. sftp happens
to need these and so has to perform slow remote operations to retrieve
them.
This patch adds a new BSD_VISIBLE extension to retain these data in an
glob->gl_statv array that corresponds to the the main glob->gl_pathv
output.
This changes the glob structure so it would need to go in along with
other major number-cranking changes.
-d
Index: include/glob.h
===================================================================
RCS file: /cvs/src/include/glob.h,v
retrieving revision 1.10
diff -u -p -r1.10 glob.h
--- include/glob.h 13 Dec 2005 00:35:22 -0000 1.10
+++ include/glob.h 23 Sep 2010 00:15:26 -0000
@@ -39,6 +39,7 @@
#define _GLOB_H_
#include <sys/cdefs.h>
+#include <sys/stat.h>
struct stat;
typedef struct {
@@ -47,6 +48,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);
@@ -83,6 +85,7 @@ 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 */
#endif
Index: lib/libc/gen/glob.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/glob.c,v
retrieving revision 1.31
diff -u -p -r1.31 glob.c
--- lib/libc/gen/glob.c 19 May 2010 14:53:06 -0000 1.31
+++ lib/libc/gen/glob.c 23 Sep 2010 00:15:26 -0000
@@ -135,7 +135,7 @@ static int glob2(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 *);
@@ -157,6 +157,7 @@ glob(const char *pattern, int flags, 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;
}
@@ -516,7 +517,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);
}
@@ -579,7 +580,7 @@ glob2(Char *pathbuf, Char *pathbuf_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. */
@@ -702,24 +703,33 @@ glob3(Char *pathbuf, Char *pathbuf_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;
+ size_t newn, len;
char *copy;
const Char *p;
+ struct stat *statv;
- newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
- pathv = realloc((char *)pglob->gl_pathv, newsize);
- if (pathv == NULL) {
+ newn = 2 + pglob->gl_pathc + pglob->gl_offs;
+ if (SIZE_MAX / sizeof(*pathv) <= newn ||
+ SIZE_MAX / sizeof(*statv) <= newn) {
+ nospace:
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((char *)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;
@@ -728,6 +738,22 @@ globextend(const Char *path, glob_t *pgl
}
pglob->gl_pathv = pathv;
+ if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) {
+ statv = realloc((char *)pglob->gl_statv, newn * sizeof(*statv));
+ if (statv == NULL)
+ goto nospace;
+ if (pglob->gl_statv == NULL && pglob->gl_offs > 0)
+ bzero(statv, newn * sizeof(*statv));
+ pglob->gl_statv = statv;
+ if (sb == NULL) {
+ bzero(&statv[pglob->gl_offs + pglob->gl_pathc],
+ sizeof(*statv));
+ } else
+ statv[pglob->gl_offs + pglob->gl_pathc] = *sb;
+ bzero(&statv[pglob->gl_offs + pglob->gl_pathc + 1],
+ sizeof(*statv));
+ }
+
for (p = path; *p++;)
;
len = (size_t)(p - path);
@@ -742,7 +768,7 @@ globextend(const Char *path, glob_t *pgl
pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
if ((pglob->gl_flags & GLOB_LIMIT) &&
- newsize + *limitp >= ARG_MAX) {
+ (newn * sizeof(*pathv)) + *limitp >= ARG_MAX) {
errno = 0;
return(GLOB_NOSPACE);
}
@@ -823,6 +849,10 @@ globfree(glob_t *pglob)
free(*pp);
free(pglob->gl_pathv);
pglob->gl_pathv = NULL;
+ }
+ if (pglob->gl_statv != NULL) {
+ free(pglob->gl_statv);
+ pglob->gl_statv = NULL;
}
}
Index: regress/lib/libc/glob/Makefile
===================================================================
RCS file: /cvs/src/regress/lib/libc/glob/Makefile,v
retrieving revision 1.2
diff -u -p -r1.2 Makefile
--- regress/lib/libc/glob/Makefile 18 Feb 2009 15:17:55 -0000 1.2
+++ regress/lib/libc/glob/Makefile 23 Sep 2010 00:15:26 -0000
@@ -5,6 +5,9 @@ PROG= globtest
run-regress-${PROG}:
mkdir -p `sed 's@/[^/]*$$@@' ${.CURDIR}/files | sort -u`
touch `cat ${.CURDIR}/files`
+ chmod 0755 `grep '/r[^/]*$$' ${.CURDIR}/files`
+ chmod 0444 `grep '/s[^/]*$$' ${.CURDIR}/files`
+ chmod 0711 `grep '/t[^/]*$$' ${.CURDIR}/files`
./${PROG} ${.CURDIR}/${PROG}.in
clean:
Index: regress/lib/libc/glob/globtest.c
===================================================================
RCS file: /cvs/src/regress/lib/libc/glob/globtest.c,v
retrieving revision 1.1
diff -u -p -r1.1 globtest.c
--- regress/lib/libc/glob/globtest.c 1 Oct 2008 23:04:36 -0000 1.1
+++ regress/lib/libc/glob/globtest.c 23 Sep 2010 00:15:26 -0000
@@ -17,6 +17,7 @@ struct gl_entry {
int nresults;
char pattern[1024];
char *results[MAX_RESULTS];
+ long modes[MAX_RESULTS];
};
int test_glob(struct gl_entry *);
@@ -27,7 +28,7 @@ main(int argc, char **argv)
FILE *fp = stdin;
char *buf, *cp, *want, *got, *last;
const char *errstr;
- int errors = 0, i, lineno;
+ int errors = 0, i, lineno, mode;
struct gl_entry entry;
size_t len;
@@ -40,9 +41,9 @@ main(int argc, char **argv)
* Read in test file, which is formatted thusly:
*
* [pattern] <flags>
- * result1
- * result2
- * result3
+ * result1 [mode]
+ * result2 [mode]
+ * result3 [mode]
* ...
*
*/
@@ -76,7 +77,7 @@ main(int argc, char **argv)
if ((cp = strchr(buf, '>')) == NULL)
errx(1, "invalid entry on line %d", lineno);
entry.flags = (int)strtol(buf, &cp, 0);
- if (*cp != '>' || entry.flags < 0 || entry.flags >
0x2000)
+ if (*cp != '>' || entry.flags < 0 || entry.flags >
0x4000)
errx(1, "invalid flags: %s", buf);
entry.nresults = 0;
continue;
@@ -88,6 +89,12 @@ main(int argc, char **argv)
errx(1, "too many results for %s, max %d",
entry.pattern, MAX_RESULTS);
}
+ if ((cp = strchr(buf, ' ')) != NULL) {
+ *cp++ = '\0';
+ mode = strtol(cp, NULL, 8);
+ } else
+ mode = -1;
+ entry.modes[entry.nresults] = mode;
entry.results[entry.nresults++] = strdup(buf);
}
if (entry.pattern[0])
@@ -109,12 +116,23 @@ int test_glob(struct gl_entry *entry)
for (i = 0; i < gl.gl_matchc; i++) {
if (strcmp(gl.gl_pathv[i], entry->results[i]) != 0)
goto mismatch;
+ if ((entry->flags & GLOB_KEEPSTAT) != 0 &&
+ entry->modes[i] != -1 &&
+ entry->modes[i] != gl.gl_statv[i].st_mode)
+ goto badmode;
free(entry->results[i]);
}
return (0);
-mismatch:
- warnx("mismatch for pattern %s, flags 0x%x", entry->pattern,
- entry->flags);
+ badmode:
+ warnx("mismatch mode for pattern %s, flags 0x%x, file \"%s\" "
+ "(found %07o, expected %07o)", entry->pattern, entry->flags,
+ gl.gl_pathv[i], gl.gl_statv[i].st_mode, entry->modes[i]);
+ goto cleanup;
+ mismatch:
+ warnx("mismatch for pattern %s, flags 0x%x "
+ "(found \"%s\", expected \"%s\")", entry->pattern, entry->flags,
+ gl.gl_pathv[i], entry->results[i]);
+ cleanup:
while (i < gl.gl_matchc) {
free(entry->results[i++]);
}
Index: regress/lib/libc/glob/globtest.in
===================================================================
RCS file: /cvs/src/regress/lib/libc/glob/globtest.in,v
retrieving revision 1.2
diff -u -p -r1.2 globtest.in
--- regress/lib/libc/glob/globtest.in 18 Feb 2009 15:24:55 -0000 1.2
+++ regress/lib/libc/glob/globtest.in 23 Sep 2010 00:15:26 -0000
@@ -46,6 +46,54 @@ fake/bin/systrace
fake/bin/tar
fake/bin/test
+[fake/bin/[[:alpha:]]*] <0x4000>
+fake/bin/cat 0100644
+fake/bin/chgrp 0100644
+fake/bin/chio 0100644
+fake/bin/chmod 0100644
+fake/bin/cksum 0100644
+fake/bin/cp 0100644
+fake/bin/cpio 0100644
+fake/bin/csh 0100644
+fake/bin/date 0100644
+fake/bin/dd 0100644
+fake/bin/df 0100644
+fake/bin/domainname 0100644
+fake/bin/echo 0100644
+fake/bin/ed 0100644
+fake/bin/eject 0100644
+fake/bin/expr 0100644
+fake/bin/hostname 0100644
+fake/bin/kill 0100644
+fake/bin/ksh 0100644
+fake/bin/ln 0100644
+fake/bin/ls 0100644
+fake/bin/md5 0100644
+fake/bin/mkdir 0100644
+fake/bin/mt 0100644
+fake/bin/mv 0100644
+fake/bin/pax 0100644
+fake/bin/ps 0100644
+fake/bin/pwd 0100644
+fake/bin/rcp 0100755
+fake/bin/rksh 0100755
+fake/bin/rm 0100755
+fake/bin/rmail 0100755
+fake/bin/rmd160 0100755
+fake/bin/rmdir 0100755
+fake/bin/sh 0100444
+fake/bin/sha1 0100444
+fake/bin/sha256 0100444
+fake/bin/sha384 0100444
+fake/bin/sha512 0100444
+fake/bin/sleep 0100444
+fake/bin/stty 0100444
+fake/bin/sum 0100444
+fake/bin/sync 0100444
+fake/bin/systrace 0100444
+fake/bin/tar 0100711
+fake/bin/test 0100711
+
[fake/bin/rm{,dir,ail}] <0x80>
fake/bin/rm
fake/bin/rmdir
@@ -62,3 +110,7 @@ fake/bin/sha512
[fake/bin/ca[a-z]] <0x0>
fake/bin/cat
+
+[fake/b[a-z]*] <0x4000>
+fake/bin 0040755
+