Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 16 Aug 2016 07:14:10 +0000 (UTC)
From:      "Andrey A. Chernov" <ache@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r304203 - stable/10/lib/libc/gen
Message-ID:  <201608160714.u7G7EAUQ061719@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ache
Date: Tue Aug 16 07:14:09 2016
New Revision: 304203
URL: https://svnweb.freebsd.org/changeset/base/304203

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/10/lib/libc/gen/glob.3
  stable/10/lib/libc/gen/glob.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/lib/libc/gen/glob.3
==============================================================================
--- stable/10/lib/libc/gen/glob.3	Tue Aug 16 06:40:27 2016	(r304202)
+++ stable/10/lib/libc/gen/glob.3	Tue Aug 16 07:14:09 2016	(r304203)
@@ -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/10/lib/libc/gen/glob.c
==============================================================================
--- stable/10/lib/libc/gen/glob.c	Tue Aug 16 06:40:27 2016	(r304202)
+++ stable/10/lib/libc/gen/glob.c	Tue Aug 16 07:14:09 2016	(r304203)
@@ -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);
+
+	*b = EOS;
+	h = NULL;
 
-	if (((char *) patbuf)[0] == EOS) {
+	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,42 +718,44 @@ 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];
 
-	/*
-	 * The readdirfunc declaration can't be prototyped, because it is
-	 * assigned, below, to two functions which are prototyped in glob.h
-	 * and dirent.h as taking pointers to differently typed opaque
-	 * structures.
-	 */
-	struct dirent *(*readdirfunc)();
+	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;
 
-	/* Search directory for matching names. */
+	/* pglob->gl_readdir takes a void *, fix this manually */
 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
-		readdirfunc = pglob->gl_readdir;
+		readdirfunc = (struct dirent *(*)(DIR *))pglob->gl_readdir;
 	else
 		readdirfunc = readdir;
-	while ((dp = (*readdirfunc)(dirp))) {
+
+	errno = 0;
+	/* Search directory for matching names. */
+	while ((dp = (*readdirfunc)(dirp)) != NULL) {
 		char *sc;
 		Char *dc;
 		wchar_t wc;
@@ -691,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);
 }
 
 
@@ -752,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;
@@ -761,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);
 	}
 
@@ -779,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;
@@ -830,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)
@@ -875,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)
@@ -893,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;
@@ -907,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;
@@ -937,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;
@@ -949,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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201608160714.u7G7EAUQ061719>