1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

1) Per POSIX (and glibc) GLOB_NOCHECK should return original pattern,

unmodified, if no matches found. But our original code strips all '\'
returning it. Rewrite the code to allow to reconstruct exact the
original pattern with backslashes for this case.

2) Prevent to use truncated pattern if MAXPATHLEN exceeded, return
GLOB_NOMATCH instead.

3) Fix few end loop conditions filling Char arrays with mbrtowc(),
MB_CUR_MAX is unneeded in two places and condition is less by one
in other place.

4) Prevent to use truncated filenames match if MAXPATHLEN exceeded,
skip such directory entries.

5) Don't end *pathend with L'/' in glob3() if limit is reached, this
change will be not visible since error is returned.

6) If error happens in (*readdirfunc)(), do the same GLOB_ABORTED
processing as for g_opendir() as POSIX requires.
This commit is contained in:
Andrey A. Chernov 2016-07-20 07:30:44 +00:00
parent 0ee5c319f2
commit aed721ec51
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=303074

View File

@ -152,6 +152,7 @@ typedef char Char;
#define CHAR(c) ((Char)((c)&M_CHAR))
#define META(c) ((Char)((c)|M_QUOTE))
#define UNPROT(c) ((c) & ~M_PROTECT)
#define M_ALL META(L'*')
#define M_END META(L']')
#define M_NOT META(L'!')
@ -159,10 +160,11 @@ typedef char Char;
#define M_RNG META(L'-')
#define M_SET META(L'[')
#define ismeta(c) (((c)&M_QUOTE) != 0)
#define isprot(c) (((c)&M_PROTECT) != 0)
static int compare(const void *, const void *);
static int g_Ctoc(const Char *, char *, size_t);
static int g_Ctoc(const Char *, char *, size_t, int);
static int g_lstat(Char *, struct stat *, glob_t *);
static DIR *g_opendir(Char *, glob_t *);
static const Char *g_strchr(const Char *, wchar_t);
@ -176,7 +178,7 @@ static int glob2(Char *, Char *, Char *, Char *, glob_t *,
struct glob_limit *);
static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *,
struct glob_limit *);
static int globextend(const Char *, glob_t *, struct glob_limit *);
static int globextend(const Char *, glob_t *, struct glob_limit *, int);
static const Char *
globtilde(const Char *, Char *, size_t, glob_t *);
static int globexp1(const Char *, glob_t *, struct glob_limit *);
@ -197,6 +199,7 @@ glob(const char * __restrict pattern, int flags,
mbstate_t mbs;
wchar_t wc;
size_t clen;
int too_long;
patnext = pattern;
if (!(flags & GLOB_APPEND)) {
@ -216,24 +219,27 @@ glob(const char * __restrict pattern, int flags,
bufnext = patbuf;
bufend = bufnext + MAXPATHLEN - 1;
too_long = 1;
if (flags & GLOB_NOESCAPE) {
memset(&mbs, 0, sizeof(mbs));
while (bufend - bufnext >= MB_CUR_MAX) {
while (bufnext <= bufend) {
clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs);
if (clen == (size_t)-1 || clen == (size_t)-2)
return (GLOB_NOMATCH);
else if (clen == 0)
else if (clen == 0) {
too_long = 0;
break;
}
*bufnext++ = wc;
patnext += clen;
}
} else {
/* Protect the quoted characters. */
memset(&mbs, 0, sizeof(mbs));
while (bufend - bufnext >= MB_CUR_MAX) {
while (bufnext <= bufend) {
if (*patnext == '\\') {
if (*++patnext == '\0') {
*bufnext++ = QUOTE | M_PROTECT;
*bufnext++ = QUOTE;
continue;
}
prot = M_PROTECT;
@ -242,13 +248,16 @@ glob(const char * __restrict pattern, int flags,
clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs);
if (clen == (size_t)-1 || clen == (size_t)-2)
return (GLOB_NOMATCH);
else if (clen == 0)
else if (clen == 0) {
too_long = 0;
break;
*bufnext++ = wc | ((wc != DOT && wc != SEP) ?
prot : 0);
}
*bufnext++ = wc | prot;
patnext += clen;
}
}
if (too_long)
return (GLOB_NOMATCH);
*bufnext = EOS;
if (flags & GLOB_BRACE)
@ -447,7 +456,7 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
/*
* Expand a ~user
*/
if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf)))
if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf), 0))
return (NULL);
if ((pwd = getpwnam((char *)wbuf)) == NULL)
return (pattern);
@ -479,8 +488,8 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
return (NULL);
dc = wbuf;
for (b = patbuf; b < eb && *dc != EOS; b++, dc++)
*b = *dc | ((*dc != DOT && *dc != SEP) ? M_PROTECT : 0);
for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT)
continue;
if (*dc != EOS)
return (NULL);
@ -589,7 +598,7 @@ glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit)
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, 1));
else
return (GLOB_NOMATCH);
}
@ -645,10 +654,11 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
errno = 0;
return (GLOB_NOSPACE);
}
if (((pglob->gl_flags & GLOB_MARK) &&
pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
|| (S_ISLNK(sb.st_mode) &&
(g_stat(pathbuf, &sb, pglob) == 0) &&
if ((pglob->gl_flags & GLOB_MARK) &&
UNPROT(pathend[-1]) != SEP &&
(S_ISDIR(sb.st_mode) ||
(S_ISLNK(sb.st_mode) &&
g_stat(pathbuf, &sb, pglob) == 0 &&
S_ISDIR(sb.st_mode)))) {
if (pathend + 1 > pathend_last) {
errno = 0;
@ -658,13 +668,13 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
*pathend = EOS;
}
++pglob->gl_matchc;
return (globextend(pathbuf, pglob, limit));
return (globextend(pathbuf, pglob, limit, 0));
}
/* Find end of next segment, copy tentatively to pathend. */
q = pathend;
p = pattern;
while (*p != EOS && *p != SEP) {
while (*p != EOS && UNPROT(*p) != SEP) {
if (ismeta(*p))
anymeta = 1;
if (q + 1 > pathend_last) {
@ -677,7 +687,7 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
if (!anymeta) { /* No expansion, do next segment. */
pathend = q;
pattern = p;
while (*pattern == SEP) {
while (UNPROT(*pattern) == SEP) {
if (pathend + 1 > pathend_last) {
errno = 0;
return (GLOB_NOSPACE);
@ -698,28 +708,29 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
{
struct dirent *dp;
DIR *dirp;
int err;
int err, too_long, saverrno;
char buf[MAXPATHLEN + MB_LEN_MAX - 1];
struct dirent *(*readdirfunc)(DIR *);
errno = 0;
if (pathend > pathend_last)
if (pathend > pathend_last) {
errno = 0;
return (GLOB_NOSPACE);
}
*pathend = EOS;
if (pglob->gl_errfunc != NULL &&
g_Ctoc(pathbuf, buf, sizeof(buf), 0)) {
errno = 0;
return (GLOB_NOSPACE);
}
errno = 0;
if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
if (errno == ENOENT || errno == ENOTDIR)
return (0);
if (pglob->gl_errfunc != NULL) {
if (g_Ctoc(pathbuf, buf, sizeof(buf))) {
errno = 0;
return (GLOB_NOSPACE);
}
if (pglob->gl_errfunc(buf, errno))
return (GLOB_ABORTED);
}
if (pglob->gl_flags & GLOB_ERR)
if ((pglob->gl_errfunc != NULL &&
pglob->gl_errfunc(buf, errno)) ||
(pglob->gl_flags & GLOB_ERR))
return (GLOB_ABORTED);
return (0);
}
@ -732,6 +743,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
else
readdirfunc = readdir;
errno = 0;
/* Search directory for matching names. */
while ((dp = (*readdirfunc)(dirp)) != NULL) {
char *sc;
@ -743,23 +755,18 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
if ((pglob->gl_flags & GLOB_LIMIT) &&
limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) {
errno = 0;
if (pathend + 1 > pathend_last)
err = GLOB_NOSPACE;
else {
*pathend++ = SEP;
*pathend = EOS;
err = GLOB_NOSPACE;
}
err = GLOB_NOSPACE;
break;
}
/* Initial DOT must be matched literally. */
if (dp->d_name[0] == '.' && *pattern != DOT)
if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT)
continue;
memset(&mbs, 0, sizeof(mbs));
dc = pathend;
sc = dp->d_name;
while (dc < pathend_last) {
too_long = 1;
while (dc <= pathend_last) {
clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs);
if (clen == (size_t)-1 || clen == (size_t)-2) {
/* XXX See initial comment #2. */
@ -767,11 +774,13 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
clen = 1;
memset(&mbs, 0, sizeof(mbs));
}
if ((*dc++ = wc) == EOS)
if ((*dc++ = wc) == EOS) {
too_long = 0;
break;
}
sc += clen;
}
if (!match(pathend, pattern, restpattern)) {
if (too_long || !match(pathend, pattern, restpattern)) {
*pathend = EOS;
continue;
}
@ -779,13 +788,24 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
pglob, limit);
if (err)
break;
errno = 0;
}
saverrno = errno;
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
(*pglob->gl_closedir)(dirp);
else
closedir(dirp);
return (err);
errno = saverrno;
if (err)
return (err);
if (dp == NULL && errno != 0 && ((pglob->gl_errfunc != NULL &&
pglob->gl_errfunc(buf, errno)) || (pglob->gl_flags & GLOB_ERR)))
return (GLOB_ABORTED);
return (0);
}
@ -804,7 +824,8 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
*/
static int
globextend(const Char *path, glob_t *pglob, struct glob_limit *limit)
globextend(const Char *path, glob_t *pglob, struct glob_limit *limit,
int prot)
{
char **pathv;
size_t i, newsize, len;
@ -831,9 +852,11 @@ globextend(const Char *path, glob_t *pglob, struct glob_limit *limit)
}
pglob->gl_pathv = pathv;
for (p = path; *p++;)
for (p = path; *p++ != EOS;)
continue;
len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */
if (prot)
len += (size_t)(p - path) - 1;
limit->l_string_cnt += len;
if ((pglob->gl_flags & GLOB_LIMIT) &&
limit->l_string_cnt >= GLOB_LIMIT_STRING) {
@ -841,7 +864,7 @@ globextend(const Char *path, glob_t *pglob, struct glob_limit *limit)
return (GLOB_NOSPACE);
}
if ((copy = malloc(len)) != NULL) {
if (g_Ctoc(path, copy, len)) {
if (g_Ctoc(path, copy, len, prot)) {
free(copy);
errno = 0;
return (GLOB_NOSPACE);
@ -935,7 +958,7 @@ g_opendir(Char *str, glob_t *pglob)
if (*str == EOS)
strcpy(buf, ".");
else {
if (g_Ctoc(str, buf, sizeof(buf))) {
if (g_Ctoc(str, buf, sizeof(buf), 0)) {
errno = ENAMETOOLONG;
return (NULL);
}
@ -952,7 +975,7 @@ g_lstat(Char *fn, struct stat *sb, glob_t *pglob)
{
char buf[MAXPATHLEN + MB_LEN_MAX - 1];
if (g_Ctoc(fn, buf, sizeof(buf))) {
if (g_Ctoc(fn, buf, sizeof(buf), 0)) {
errno = ENAMETOOLONG;
return (-1);
}
@ -966,7 +989,7 @@ g_stat(Char *fn, struct stat *sb, glob_t *pglob)
{
char buf[MAXPATHLEN + MB_LEN_MAX - 1];
if (g_Ctoc(fn, buf, sizeof(buf))) {
if (g_Ctoc(fn, buf, sizeof(buf), 0)) {
errno = ENAMETOOLONG;
return (-1);
}
@ -987,23 +1010,31 @@ g_strchr(const Char *str, wchar_t ch)
}
static int
g_Ctoc(const Char *str, char *buf, size_t len)
g_Ctoc(const Char *str, char *buf, size_t len, int prot)
{
mbstate_t mbs;
size_t clen;
Char Ch;
Ch = *str;
memset(&mbs, 0, sizeof(mbs));
while (len >= MB_CUR_MAX) {
clen = wcrtomb(buf, CHAR(*str), &mbs);
if (prot && isprot(Ch)) {
Ch = UNPROT(Ch);
*buf++ = '\\';
len--;
continue;
}
clen = wcrtomb(buf, CHAR(Ch), &mbs);
if (clen == (size_t)-1) {
/* XXX See initial comment #2. */
*buf = (char)CHAR(*str);
*buf = (char)CHAR(Ch);
clen = 1;
memset(&mbs, 0, sizeof(mbs));
}
if (CHAR(*str) == EOS)
if (CHAR(Ch) == EOS)
return (0);
str++;
Ch = *++str;
buf += clen;
len -= clen;
}