From owner-svn-src-all@freebsd.org Wed Aug 17 09:34:58 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 29ADCBBD379; Wed, 17 Aug 2016 09:34:58 +0000 (UTC) (envelope-from ache@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id ED1E01F9C; Wed, 17 Aug 2016 09:34:57 +0000 (UTC) (envelope-from ache@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u7H9YvPT057248; Wed, 17 Aug 2016 09:34:57 GMT (envelope-from ache@FreeBSD.org) Received: (from ache@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u7H9YvVl057246; Wed, 17 Aug 2016 09:34:57 GMT (envelope-from ache@FreeBSD.org) Message-Id: <201608170934.u7H9YvVl057246@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ache set sender to ache@FreeBSD.org using -f From: "Andrey A. Chernov" Date: Wed, 17 Aug 2016 09:34:57 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r304284 - stable/11/lib/libc/gen X-SVN-Group: stable-11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 17 Aug 2016 09:34:58 -0000 Author: ache Date: Wed Aug 17 09:34:56 2016 New Revision: 304284 URL: https://svnweb.freebsd.org/changeset/base/304284 Log: MFC r302943,r302944,r303004,r303010,r303011,r303013,r303014,r303074, r303088,r303142,r303208,r303210,r303530,r303536,r303564,r303565, r303706 In short: 1) All situations with glob(3) error return codes are well defined by POSIX, so rewrite old sporadic errors processing to match those definitions. Including subcases: Both C99 and POSIX directly prohibits any standard function to set errno to 0. Breaking this rule in 2001 NetBSD hack was imported which attempts to workaround very limited glob(3) return codes amount. Use POSIX-compatible workaround now with E2BIG which can't comes from other functions used instead of prohibited 0. Process errors happpens in (*readdirfunc)() too, as POSIX requires. Per POSIX GLOB_NOCHECK should return original pattern, unmodified, if no matches found. But our code strips all '\' returning it. Rewrite the code to allow to return original pattern. GLOB_ERR and gl_errfunc are supposed to work only for real directories per POSIX, so don't act on missing or plain files for ENOENT or ENOTDIR (as TODO in the code suggested). Remove the hack in the manpage describing how to skip ENOENT and ENOTDIR in gl_errfunc, it is unneeded now. Per POSIX GLOB_ERR must be considered even if gl_errfunc is not set, old code skips it in that case. 2) For near MAXPATHLEN long pathes old glob(3) code can operate on truncated results, prevent it in several places. 3) Results was not sorted according to collate as POSIX requires. 4) globtilde() forget to convert expanded user home dir from multibyte to wide chars. Moreover, those chars are addded as not protected, so can be treated as special chars. 5) Backward hack for EILSEQ in g_Ctoc() was not implemented, so all pathes with illegal byte sequences are skipped as result, implement it now. 6) GLOB_BRACE was somehow broken. First it repeatedly calls glob0() in globexp1() recursive calls, but glob0() was not supposed to be called repeatedly in the original code. It finalize results by possible adding original pattern for no match case, may return GLOB_NOMATCH error and by sorting all things. Original pattern adding or GLOB_NOMATCH error can happens each time glob0() called repeatedly, and sorting happens for one item only, all things are never sorted. Second, f.e. "a{a" pattern does not match "a{a" file but match "a" file instead. Third, some errors (f.e. for limits or overflow) can be ignored by GLOB_BRACE code because it forces return (0). Add non-finalizing flag to glob0() and make globexp0() wrapper around recursively called globexp1() to finalize things like glob0() does. Reorganize braces code to work correctly. 7) Don't allow MB_CUR_MAX * strlen overallocation hits GLOB_LIMIT_STRING (ARG_MAX) limit, use final string length, not malloced space for it. Modified: stable/11/lib/libc/gen/glob.3 stable/11/lib/libc/gen/glob.c Directory Properties: stable/11/ (props changed) Modified: stable/11/lib/libc/gen/glob.3 ============================================================================== --- stable/11/lib/libc/gen/glob.3 Wed Aug 17 09:24:46 2016 (r304283) +++ stable/11/lib/libc/gen/glob.3 Wed Aug 17 09:34:56 2016 (r304284) @@ -275,24 +275,10 @@ is .Pf non- Dv NULL , .Fn glob calls -.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) . -This may be unintuitive: a pattern like -.Ql */Makefile -will try to -.Xr stat 2 -.Ql foo/Makefile -even if -.Ql foo -is not a directory, resulting in a -call to -.Fa errfunc . -The error routine can suppress this action by testing for -.Er ENOENT -and -.Er ENOTDIR ; +.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) , however, the .Dv GLOB_ERR -flag will still cause an immediate +flag will cause an immediate return when this happens. .Pp If @@ -377,7 +363,7 @@ file .It Dv GLOB_NOSPACE An attempt to allocate memory failed, or if .Fa errno -was 0 +was E2BIG, .Dv GLOB_LIMIT was specified in the flags and .Fa pglob\->gl_matchc Modified: stable/11/lib/libc/gen/glob.c ============================================================================== --- stable/11/lib/libc/gen/glob.c Wed Aug 17 09:24:46 2016 (r304283) +++ stable/11/lib/libc/gen/glob.c Wed Aug 17 09:34:56 2016 (r304284) @@ -71,7 +71,7 @@ __FBSDID("$FreeBSD$"); * 1. Patterns with illegal byte sequences match nothing - even if * GLOB_NOCHECK is specified. * 2. Illegal byte sequences in filenames are handled by treating them as - * single-byte characters with a value of the first byte of the sequence + * single-byte characters with a values of such bytes of the sequence * cast to wchar_t. * 3. State-dependent encodings are not currently supported. */ @@ -113,25 +113,20 @@ struct glob_limit { size_t l_string_cnt; }; -#define DOLLAR '$' -#define DOT '.' -#define EOS '\0' -#define LBRACKET '[' -#define NOT '!' -#define QUESTION '?' -#define QUOTE '\\' -#define RANGE '-' -#define RBRACKET ']' -#define SEP '/' -#define STAR '*' -#define TILDE '~' -#define UNDERSCORE '_' -#define LBRACE '{' -#define RBRACE '}' -#define SLASH '/' -#define COMMA ',' - -#ifndef DEBUG +#define DOT L'.' +#define EOS L'\0' +#define LBRACKET L'[' +#define NOT L'!' +#define QUESTION L'?' +#define QUOTE L'\\' +#define RANGE L'-' +#define RBRACKET L']' +#define SEP L'/' +#define STAR L'*' +#define TILDE L'~' +#define LBRACE L'{' +#define RBRACE L'}' +#define COMMA L',' #define M_QUOTE 0x8000000000ULL #define M_PROTECT 0x4000000000ULL @@ -140,28 +135,19 @@ struct glob_limit { typedef uint_fast64_t Char; -#else - -#define M_QUOTE 0x80 -#define M_PROTECT 0x40 -#define M_MASK 0xff -#define M_CHAR 0x7f - -typedef char Char; - -#endif - - #define CHAR(c) ((Char)((c)&M_CHAR)) #define META(c) ((Char)((c)|M_QUOTE)) -#define M_ALL META('*') -#define M_END META(']') -#define M_NOT META('!') -#define M_ONE META('?') -#define M_RNG META('-') -#define M_SET META('[') +#define UNPROT(c) ((c) & ~M_PROTECT) +#define M_ALL META(L'*') +#define M_END META(L']') +#define M_NOT META(L'!') +#define M_ONE META(L'?') +#define M_RNG META(L'-') +#define M_SET META(L'[') #define ismeta(c) (((c)&M_QUOTE) != 0) - +#ifdef DEBUG +#define isprot(c) (((c)&M_PROTECT) != 0) +#endif static int compare(const void *, const void *); static int g_Ctoc(const Char *, char *, size_t); @@ -172,19 +158,27 @@ static const Char *g_strchr(const Char * static Char *g_strcat(Char *, const Char *); #endif static int g_stat(Char *, struct stat *, glob_t *); -static int glob0(const Char *, glob_t *, struct glob_limit *); +static int glob0(const Char *, glob_t *, struct glob_limit *, + const char *); static int glob1(Char *, glob_t *, struct glob_limit *); 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 *, + const char *); static const Char * globtilde(const Char *, Char *, size_t, glob_t *); +static int globexp0(const Char *, glob_t *, struct glob_limit *, + const char *); static int globexp1(const Char *, glob_t *, struct glob_limit *); -static int globexp2(const Char *, const Char *, glob_t *, int *, +static int globexp2(const Char *, const Char *, glob_t *, struct glob_limit *); +static int globfinal(glob_t *, struct glob_limit *, size_t, + const char *); static int match(Char *, Char *, Char *); +static int err_nomatch(glob_t *, struct glob_limit *, const char *); +static int err_aborted(glob_t *, int, char *); #ifdef DEBUG static void qprintf(const char *, Char *); #endif @@ -199,6 +193,7 @@ glob(const char * __restrict pattern, in mbstate_t mbs; wchar_t wc; size_t clen; + int too_long; patnext = pattern; if (!(flags & GLOB_APPEND)) { @@ -218,24 +213,27 @@ glob(const char * __restrict pattern, in 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) + return (err_nomatch(pglob, &limit, pattern)); + 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) { - if (*patnext == QUOTE) { - if (*++patnext == EOS) { - *bufnext++ = QUOTE | M_PROTECT; + while (bufnext <= bufend) { + if (*patnext == '\\') { + if (*++patnext == '\0') { + *bufnext++ = QUOTE; continue; } prot = M_PROTECT; @@ -243,19 +241,47 @@ glob(const char * __restrict pattern, in prot = 0; clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) - return (GLOB_NOMATCH); - else if (clen == 0) + return (err_nomatch(pglob, &limit, pattern)); + else if (clen == 0) { + too_long = 0; break; + } *bufnext++ = wc | prot; patnext += clen; } } + if (too_long) + return (err_nomatch(pglob, &limit, pattern)); *bufnext = EOS; if (flags & GLOB_BRACE) - return (globexp1(patbuf, pglob, &limit)); + return (globexp0(patbuf, pglob, &limit, pattern)); else - return (glob0(patbuf, pglob, &limit)); + return (glob0(patbuf, pglob, &limit, pattern)); +} + +static int +globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, + const char *origpat) { + int rv; + size_t oldpathc; + + /* Protect a single {}, for find(1), like csh */ + if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) { + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + return (glob0(pattern, pglob, limit, origpat)); + } + + oldpathc = pglob->gl_pathc; + + if ((rv = globexp1(pattern, pglob, limit)) != 0) + return rv; + + return (globfinal(pglob, limit, oldpathc, origpat)); } /* @@ -266,24 +292,18 @@ glob(const char * __restrict pattern, in static int globexp1(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { - const Char* ptr = pattern; - int rv; + const Char* ptr; - if ((pglob->gl_flags & GLOB_LIMIT) && - limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { - errno = 0; - return (GLOB_NOSPACE); + if ((ptr = g_strchr(pattern, LBRACE)) != NULL) { + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + return (globexp2(ptr, pattern, pglob, limit)); } - /* Protect a single {}, for find(1), like csh */ - if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) - return glob0(pattern, pglob, limit); - - while ((ptr = g_strchr(ptr, LBRACE)) != NULL) - if (!globexp2(ptr, pattern, pglob, &rv, limit)) - return rv; - - return glob0(pattern, pglob, limit); + return (glob0(pattern, pglob, limit, NULL)); } @@ -293,10 +313,10 @@ globexp1(const Char *pattern, glob_t *pg * 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, struct glob_limit *limit) { - int i; + int i, rv; Char *lm, *ls; const Char *pe, *pm, *pm1, *pl; Char patbuf[MAXPATHLEN]; @@ -308,7 +328,7 @@ globexp2(const Char *ptr, const Char *pa ls = lm; /* Find the balanced brace */ - for (i = 0, pe = ++ptr; *pe; pe++) + for (i = 0, pe = ++ptr; *pe != EOS; pe++) if (*pe == LBRACKET) { /* Ignore everything between [] */ for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) @@ -330,10 +350,8 @@ globexp2(const Char *ptr, const Char *pa } /* Non matching braces; just glob the pattern */ - if (i != 0 || *pe == EOS) { - *rv = glob0(patbuf, pglob, limit); - return (0); - } + if (i != 0 || *pe == EOS) + return (glob0(pattern, pglob, limit, NULL)); for (i = 0, pl = pm = ptr; pm <= pe; pm++) switch (*pm) { @@ -378,7 +396,9 @@ globexp2(const Char *ptr, const Char *pa #ifdef DEBUG qprintf("globexp2:", patbuf); #endif - *rv = globexp1(patbuf, pglob, limit); + rv = globexp1(patbuf, pglob, limit); + if (rv) + return (rv); /* move after the comma, to the next string */ pl = pm + 1; @@ -388,7 +408,6 @@ globexp2(const Char *ptr, const Char *pa default: break; } - *rv = 0; return (0); } @@ -401,9 +420,15 @@ static const Char * globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) { struct passwd *pwd; - char *h; + char *h, *sc; const Char *p; Char *b, *eb; + wchar_t wc; + wchar_t wbuf[MAXPATHLEN]; + wchar_t *wbufend, *dc; + size_t clen; + mbstate_t mbs; + int too_long; if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) return (pattern); @@ -412,13 +437,17 @@ globtilde(const Char *pattern, Char *pat * Copy up to the end of the string or / */ eb = &patbuf[patbuf_len - 1]; - for (p = pattern + 1, h = (char *) patbuf; - h < (char *)eb && *p && *p != SLASH; *h++ = *p++) + for (p = pattern + 1, b = patbuf; + b < eb && *p != EOS && UNPROT(*p) != SEP; *b++ = *p++) continue; - *h = EOS; + if (*p != EOS && UNPROT(*p) != SEP) + return (NULL); - if (((char *) patbuf)[0] == EOS) { + *b = EOS; + h = NULL; + + if (patbuf[0] == EOS) { /* * handle a plain ~ or ~/ by expanding $HOME first (iff * we're not running setuid or setgid) and then trying @@ -438,20 +467,56 @@ globtilde(const Char *pattern, Char *pat /* * Expand a ~user */ - if ((pwd = getpwnam((char*) patbuf)) == NULL) + if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf))) + return (NULL); + if ((pwd = getpwnam((char *)wbuf)) == NULL) return (pattern); else h = pwd->pw_dir; } /* Copy the home directory */ - for (b = patbuf; b < eb && *h; *b++ = *h++) + dc = wbuf; + sc = h; + wbufend = wbuf + MAXPATHLEN - 1; + too_long = 1; + memset(&mbs, 0, sizeof(mbs)); + while (dc <= wbufend) { + clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); + if (clen == (size_t)-1 || clen == (size_t)-2) { + /* XXX See initial comment #2. */ + wc = (unsigned char)*sc; + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if ((*dc++ = wc) == EOS) { + too_long = 0; + break; + } + sc += clen; + } + if (too_long) + return (NULL); + + dc = wbuf; + for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT) continue; + if (*dc != EOS) + return (NULL); /* Append the rest of the pattern */ - while (b < eb && (*b++ = *p++) != EOS) - continue; - *b = EOS; + if (*p != EOS) { + too_long = 1; + while (b <= eb) { + if ((*b++ = *p++) == EOS) { + too_long = 0; + break; + } + } + if (too_long) + return (NULL); + } else + *b = EOS; return (patbuf); } @@ -464,14 +529,18 @@ globtilde(const Char *pattern, Char *pat * if things went well, nonzero if errors occurred. */ static int -glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit) -{ +glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, + const char *origpat) { const Char *qpatnext; int err; size_t oldpathc; Char *bufnext, c, patbuf[MAXPATHLEN]; qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); + if (qpatnext == NULL) { + errno = E2BIG; + return (GLOB_NOSPACE); + } oldpathc = pglob->gl_pathc; bufnext = patbuf; @@ -530,30 +599,29 @@ glob0(const Char *pattern, glob_t *pglob if ((err = glob1(patbuf, pglob, limit)) != 0) return(err); - /* - * If there was no match we are going to append the pattern - * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified - * and the pattern did not contain any magic characters - * GLOB_NOMAGIC is there just for compatibility with csh. - */ - if (pglob->gl_pathc == oldpathc) { - if (((pglob->gl_flags & GLOB_NOCHECK) || - ((pglob->gl_flags & GLOB_NOMAGIC) && - !(pglob->gl_flags & GLOB_MAGCHAR)))) - return (globextend(pattern, pglob, limit)); - else - return (GLOB_NOMATCH); - } + if (origpat != NULL) + return (globfinal(pglob, limit, oldpathc, origpat)); + + return (0); +} + +static int +globfinal(glob_t *pglob, struct glob_limit *limit, size_t oldpathc, + const char *origpat) { + if (pglob->gl_pathc == oldpathc) + return (err_nomatch(pglob, limit, origpat)); + if (!(pglob->gl_flags & GLOB_NOSORT)) qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, pglob->gl_pathc - oldpathc, sizeof(char *), compare); + return (0); } static int compare(const void *p, const void *q) { - return (strcmp(*(char **)p, *(char **)q)); + return (strcoll(*(char **)p, *(char **)q)); } static int @@ -593,44 +661,47 @@ glob2(Char *pathbuf, Char *pathend, Char if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) { - errno = 0; - if (pathend + 1 > pathend_last) - return (GLOB_ABORTED); - *pathend++ = SEP; - *pathend = EOS; + errno = E2BIG; 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) - return (GLOB_ABORTED); + if (pathend + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } *pathend++ = SEP; *pathend = EOS; } ++pglob->gl_matchc; - return (globextend(pathbuf, pglob, limit)); + return (globextend(pathbuf, pglob, limit, NULL)); } /* 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) - return (GLOB_ABORTED); + if (q + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } *q++ = *p++; } if (!anymeta) { /* No expansion, do next segment. */ pathend = q; pattern = p; - while (*pattern == SEP) { - if (pathend + 1 > pathend_last) - return (GLOB_ABORTED); + while (UNPROT(*pattern) == SEP) { + if (pathend + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } *pathend++ = *pattern++; } } else /* Need expansion, recurse. */ @@ -647,26 +718,31 @@ glob3(Char *pathbuf, Char *pathend, Char { struct dirent *dp; DIR *dirp; - int err; - char buf[MAXPATHLEN]; + int err, too_long, saverrno, saverrno2; + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; struct dirent *(*readdirfunc)(DIR *); - if (pathend > pathend_last) - return (GLOB_ABORTED); + if (pathend > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } *pathend = EOS; - errno = 0; + if (pglob->gl_errfunc != NULL && + g_Ctoc(pathbuf, buf, sizeof(buf))) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + saverrno = errno; + errno = 0; if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { - /* TODO: don't call for ENOENT or ENOTDIR? */ - if (pglob->gl_errfunc) { - if (g_Ctoc(pathbuf, buf, sizeof(buf))) - return (GLOB_ABORTED); - if (pglob->gl_errfunc(buf, errno) || - pglob->gl_flags & GLOB_ERR) - return (GLOB_ABORTED); - } - return (0); + if (errno == ENOENT || errno == ENOTDIR) + return (0); + err = err_aborted(pglob, errno, buf); + if (errno == 0) + errno = saverrno; + return (err); } err = 0; @@ -677,6 +753,7 @@ glob3(Char *pathbuf, Char *pathend, Char else readdirfunc = readdir; + errno = 0; /* Search directory for matching names. */ while ((dp = (*readdirfunc)(dirp)) != NULL) { char *sc; @@ -687,49 +764,70 @@ glob3(Char *pathbuf, Char *pathend, Char if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { - errno = 0; - if (pathend + 1 > pathend_last) - err = GLOB_ABORTED; - else { - *pathend++ = SEP; - *pathend = EOS; - err = GLOB_NOSPACE; - } + errno = E2BIG; + err = GLOB_NOSPACE; break; } /* Initial DOT must be matched literally. */ - if (dp->d_name[0] == DOT && *pattern != DOT) + if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT) { + errno = 0; 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) { - wc = *sc; + /* XXX See initial comment #2. */ + wc = (unsigned char)*sc; 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 && (err = err_aborted(pglob, ENAMETOOLONG, + buf))) { + errno = ENAMETOOLONG; + break; + } + if (too_long || !match(pathend, pattern, restpattern)) { *pathend = EOS; + errno = 0; continue; } + if (errno == 0) + errno = saverrno; err = glob2(pathbuf, --dc, pathend_last, restpattern, pglob, limit); if (err) break; + errno = 0; } + saverrno2 = errno; if (pglob->gl_flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir)(dirp); else closedir(dirp); - return (err); + errno = saverrno2; + + if (err) + return (err); + + if (dp == NULL && errno != 0 && + (err = err_aborted(pglob, errno, buf))) + return (err); + + if (errno == 0) + errno = saverrno; + return (0); } @@ -748,7 +846,8 @@ glob3(Char *pathbuf, Char *pathend, Char * 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, + const char *origpat) { char **pathv; size_t i, newsize, len; @@ -757,7 +856,7 @@ globextend(const Char *path, glob_t *pgl if ((pglob->gl_flags & GLOB_LIMIT) && pglob->gl_matchc > limit->l_path_lim) { - errno = 0; + errno = E2BIG; return (GLOB_NOSPACE); } @@ -775,18 +874,26 @@ globextend(const Char *path, glob_t *pgl } pglob->gl_pathv = pathv; - for (p = path; *p++;) - continue; - len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ - limit->l_string_cnt += len; - if ((pglob->gl_flags & GLOB_LIMIT) && - limit->l_string_cnt >= GLOB_LIMIT_STRING) { - errno = 0; - return (GLOB_NOSPACE); + if (origpat != NULL) + copy = strdup(origpat); + else { + for (p = path; *p++ != EOS;) + continue; + len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ + if ((copy = malloc(len)) != NULL) { + if (g_Ctoc(path, copy, len)) { + free(copy); + errno = E2BIG; + return (GLOB_NOSPACE); + } + } } - if ((copy = malloc(len)) != NULL) { - if (g_Ctoc(path, copy, len)) { + if (copy != NULL) { + limit->l_string_cnt += strlen(copy) + 1; + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_string_cnt >= GLOB_LIMIT_STRING) { free(copy); + errno = E2BIG; return (GLOB_NOSPACE); } pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; @@ -826,15 +933,17 @@ match(Char *name, Char *pat, Char *paten ok = 0; if ((k = *name++) == EOS) return (0); - if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) + if ((negate_range = ((*pat & M_MASK) == M_NOT)) != 0) ++pat; while (((c = *pat++) & M_MASK) != M_END) if ((*pat & M_MASK) == M_RNG) { if (table->__collate_load_error ? - CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1]) : - __wcollate_range_cmp(CHAR(c), CHAR(k)) <= 0 - && __wcollate_range_cmp(CHAR(k), CHAR(pat[1])) <= 0 - ) + CHAR(c) <= CHAR(k) && + CHAR(k) <= CHAR(pat[1]) : + __wcollate_range_cmp(CHAR(c), + CHAR(k)) <= 0 && + __wcollate_range_cmp(CHAR(k), + CHAR(pat[1])) <= 0) ok = 1; pat += 2; } else if (c == k) @@ -871,13 +980,15 @@ globfree(glob_t *pglob) static DIR * g_opendir(Char *str, glob_t *pglob) { - char buf[MAXPATHLEN]; + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; - if (!*str) + if (*str == EOS) strcpy(buf, "."); else { - if (g_Ctoc(str, buf, sizeof(buf))) + if (g_Ctoc(str, buf, sizeof(buf))) { + errno = ENAMETOOLONG; return (NULL); + } } if (pglob->gl_flags & GLOB_ALTDIRFUNC) @@ -889,7 +1000,7 @@ g_opendir(Char *str, glob_t *pglob) static int g_lstat(Char *fn, struct stat *sb, glob_t *pglob) { - char buf[MAXPATHLEN]; + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; @@ -903,7 +1014,7 @@ g_lstat(Char *fn, struct stat *sb, glob_ static int g_stat(Char *fn, struct stat *sb, glob_t *pglob) { - char buf[MAXPATHLEN]; + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; @@ -933,10 +1044,14 @@ g_Ctoc(const Char *str, char *buf, size_ memset(&mbs, 0, sizeof(mbs)); while (len >= MB_CUR_MAX) { - clen = wcrtomb(buf, *str, &mbs); - if (clen == (size_t)-1) - return (1); - if (*str == L'\0') + clen = wcrtomb(buf, CHAR(*str), &mbs); + if (clen == (size_t)-1) { + /* XXX See initial comment #2. */ + *buf = (char)CHAR(*str); + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if (CHAR(*str) == EOS) return (0); str++; buf += clen; @@ -945,21 +1060,46 @@ g_Ctoc(const Char *str, char *buf, size_ return (1); } +static int +err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) { + /* + * If there was no match we are going to append the origpat + * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified + * and the origpat did not contain any magic characters + * GLOB_NOMAGIC is there just for compatibility with csh. + */ + if ((pglob->gl_flags & GLOB_NOCHECK) || + ((pglob->gl_flags & GLOB_NOMAGIC) && + !(pglob->gl_flags & GLOB_MAGCHAR))) + return (globextend(NULL, pglob, limit, origpat)); + return (GLOB_NOMATCH); +} + +static int +err_aborted(glob_t *pglob, int err, char *buf) { + if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || + (pglob->gl_flags & GLOB_ERR)) + return (GLOB_ABORTED); + return (0); +} + #ifdef DEBUG static void qprintf(const char *str, Char *s) { Char *p; - (void)printf("%s:\n", str); - for (p = s; *p; p++) - (void)printf("%c", CHAR(*p)); - (void)printf("\n"); - for (p = s; *p; p++) - (void)printf("%c", *p & M_PROTECT ? '"' : ' '); - (void)printf("\n"); - for (p = s; *p; p++) - (void)printf("%c", ismeta(*p) ? '_' : ' '); - (void)printf("\n"); + (void)printf("%s\n", str); + if (s != NULL) { + for (p = s; *p != EOS; p++) + (void)printf("%c", (char)CHAR(*p)); + (void)printf("\n"); + for (p = s; *p != EOS; p++) + (void)printf("%c", (isprot(*p) ? '\\' : ' ')); + (void)printf("\n"); + for (p = s; *p != EOS; p++) + (void)printf("%c", (ismeta(*p) ? '_' : ' ')); + (void)printf("\n"); + } } #endif