Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 20 Jul 2016 07:30:44 +0000 (UTC)
From:      "Andrey A. Chernov" <ache@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r303074 - head/lib/libc/gen
Message-ID:  <201607200730.u6K7Ui3s096327@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ache
Date: Wed Jul 20 07:30:44 2016
New Revision: 303074
URL: https://svnweb.freebsd.org/changeset/base/303074

Log:
  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.

Modified:
  head/lib/libc/gen/glob.c

Modified: head/lib/libc/gen/glob.c
==============================================================================
--- head/lib/libc/gen/glob.c	Wed Jul 20 06:29:26 2016	(r303073)
+++ head/lib/libc/gen/glob.c	Wed Jul 20 07:30:44 2016	(r303074)
@@ -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 *
     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, in
 	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, 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)
+			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, in
 			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 *pat
 		/*
 		 * 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 *pat
 		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
 		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
 				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 = 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
 		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
 {
 	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
 	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
 		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
 				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
 		    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
  *	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 *pgl
 	}
 	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 *pgl
 		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_
 {
 	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
 {
 	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;
 	}



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