public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 00/18] posix: glob fixes and refactor
@ 2017-08-11 14:50 Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 11/18] posix: Remove alloca usage on glob dirname Adhemerval Zanella
                   ` (18 more replies)
  0 siblings, 19 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:50 UTC (permalink / raw)
  To: libc-alpha

Changes from previous version [1]

  - Add fix for BZ#10246.
  - Incorporate review change for the dynamic implementation for
    C strings.
  - Reorganize patch order.

---

The set fixes some long standing glob issues (gnulib sync and dandling
symlinks), adjust its tests for libsupport, and refactor current 
implementation to use a dynarray implementation for C strings.

To correct provide a C string analogous using dynarray I had to access
its private member directly, so I created a specialized implementation
(malloc/char_array-skeleton.c).  It currently works similar to dynarray,
by including its header which defines static functions.  If it is the
case, one could its function as an internal API.  I added some
function only to manipulate mainly for the work to refactor glob.

The idea to refactor is mainly to remove the alloca and internal buffer
handling boilerplate usage.  So it does not trace its possible stack
usage using 'alloca_used' variable, which is somewhat fragile (since
it just track glob usage itself).  I used 256 bytes for default
string size (for initial stack allocation), but we can also tune it
if required.  The GCC -fstack-usage on x86_64 for shared cases
shows:

  glob.c:246:1:next_brace_sub     32      static
  dynarray-skeleton.c:203:1:globnames_array_free  48      static
  glob.c:1176:1:collated_compare  32      static
  glob.c:1196:1:prefix_array      96      static
  dynarray-skeleton.c:275:1:globnames_array_add__ 64      static
  dynarray-skeleton.c:373:1:char_array_resize.part.3      64      static
  char_array-skeleton.c:257:1:char_array_replace_str_pos  64      static
  char_array-skeleton.c:195:1:char_array_crop     48      static
  char_array-skeleton.c:231:1:char_array_append_str       64      static
  char_array-skeleton.c:117:1:char_array_set_str_size     64      static
  glob.c:271:1:get_home_directory 1200    static
  glob.c:1264:1:glob_in_dir       1104    static
  glob.c:352:1:glob       1216    static

I did not and will not take care of the non-unix code on glob.c (it
is probably broken after the glob refactor).  In fact I would like
to cleanup this implementation even further to remove all code not
really used for GLIBC if gnulib developers want to not keep the
implementation at sync.

Adhemerval Zanella (17):
  posix: Sync glob with gnulib [BZ #1062]
  posix: Adjust glob tests to libsupport
  posix: Consolidate glob implementation
  posix: Allow glob to match dangling symlinks [BZ #866]
  posix: Remove glob GET_LOGIN_NAME_MAX usage
  posix: User LOGIN_NAME_MAX for all user names in glob
  malloc: Add specialized dynarray for C strings
  posix: Use char_array for internal glob dirname
  posix: Remove alloca usage for GLOB_BRACE on glob
  posix: Remove alloca usage on glob dirname
  posix: Use dynarray for globname in glob
  posix: Remove all alloca usage in glob
  posix: Use char_array for home_dir in glob
  posix: Add common function to get home directory
  posix: More check for overflow allocation in glob
  posix: Use enum for __glob_pattern_type result
  posix: Fix glob with GLOB_NOCHECK returning modified patterns
    (BZ#10246)

Florian Weimer (1):
  posix: Rewrite to use struct scratch_buffer instead of extend_alloca

 malloc/Makefile                                    |    4 +-
 malloc/Versions                                    |    7 +
 malloc/char_array-impl.c                           |   57 +
 malloc/char_array-skeleton.c                       |  271 +++++
 malloc/char_array.h                                |   53 +
 malloc/dynarray.h                                  |    9 +
 malloc/dynarray_overflow_failure.c                 |   31 +
 malloc/malloc-internal.h                           |   14 +
 malloc/tst-char_array.c                            |  110 ++
 posix/Makefile                                     |   17 +-
 posix/bug-glob1.c                                  |   88 --
 posix/bug-glob3.c                                  |   45 -
 posix/glob.c                                       | 1194 ++++++++------------
 posix/glob_internal.h                              |   65 ++
 posix/glob_pattern_p.c                             |   29 +
 posix/globfree.c                                   |   37 +
 posix/globfree64.c                                 |   27 +
 posix/globtest.c                                   |  166 +--
 posix/globtest.sh                                  |   36 +
 posix/tst-glob_basic.c                             |   43 +
 posix/tst-glob_common.c                            |  103 ++
 posix/{bug-glob2.c => tst-glob_memory.c}           |  100 +-
 posix/tst-glob_symlinks.c                          |  133 +++
 posix/tst-gnuglob.c                                |  109 +-
 sysdeps/gnu/glob64.c                               |   25 -
 sysdeps/unix/sysv/linux/Makefile                   |    2 +-
 sysdeps/unix/sysv/linux/alpha/Makefile             |    4 -
 sysdeps/unix/sysv/linux/alpha/{glob.c => glob64.c} |   11 -
 sysdeps/unix/sysv/linux/alpha/globfree.c           |   36 +
 sysdeps/unix/sysv/linux/arm/glob64.c               |    1 -
 sysdeps/unix/sysv/linux/glob.c                     |   23 +
 sysdeps/unix/sysv/linux/glob64.c                   |   55 +
 sysdeps/unix/sysv/linux/globfree.c                 |   23 +
 sysdeps/unix/sysv/linux/globfree64.c               |    0
 sysdeps/unix/sysv/linux/i386/alphasort64.c         |    2 +-
 sysdeps/unix/sysv/linux/i386/getdents64.c          |    2 +-
 sysdeps/unix/sysv/linux/i386/readdir64.c           |    2 +-
 sysdeps/unix/sysv/linux/i386/readdir64_r.c         |    2 +-
 sysdeps/unix/sysv/linux/i386/versionsort64.c       |    2 +-
 sysdeps/unix/sysv/linux/m68k/glob64.c              |    1 -
 sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c   |    1 -
 sysdeps/unix/sysv/linux/{i386 => }/olddirent.h     |    0
 .../unix/sysv/linux/{i386/glob64.c => oldglob.c}   |   51 +-
 sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c |    1 -
 sysdeps/unix/sysv/linux/s390/s390-32/glob64.c      |   20 +
 sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c     |    2 +
 sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c     |    1 -
 sysdeps/unix/sysv/linux/wordsize-64/glob64.c       |    2 -
 sysdeps/unix/sysv/linux/x86_64/x32/glob.c          |    1 -
 sysdeps/wordsize-64/glob.c                         |    8 -
 sysdeps/wordsize-64/glob64.c                       |    1 -
 51 files changed, 1786 insertions(+), 1241 deletions(-)
 create mode 100644 malloc/char_array-impl.c
 create mode 100644 malloc/char_array-skeleton.c
 create mode 100644 malloc/char_array.h
 create mode 100644 malloc/dynarray_overflow_failure.c
 create mode 100644 malloc/tst-char_array.c
 delete mode 100644 posix/bug-glob1.c
 delete mode 100644 posix/bug-glob3.c
 create mode 100644 posix/glob_internal.h
 create mode 100644 posix/glob_pattern_p.c
 create mode 100644 posix/globfree.c
 create mode 100644 posix/globfree64.c
 create mode 100644 posix/tst-glob_basic.c
 create mode 100644 posix/tst-glob_common.c
 rename posix/{bug-glob2.c => tst-glob_memory.c} (76%)
 create mode 100644 posix/tst-glob_symlinks.c
 delete mode 100644 sysdeps/gnu/glob64.c
 rename sysdeps/unix/sysv/linux/alpha/{glob.c => glob64.c} (78%)
 create mode 100644 sysdeps/unix/sysv/linux/alpha/globfree.c
 delete mode 100644 sysdeps/unix/sysv/linux/arm/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/glob.c
 create mode 100644 sysdeps/unix/sysv/linux/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/globfree.c
 create mode 100644 sysdeps/unix/sysv/linux/globfree64.c
 delete mode 100644 sysdeps/unix/sysv/linux/m68k/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c
 rename sysdeps/unix/sysv/linux/{i386 => }/olddirent.h (100%)
 rename sysdeps/unix/sysv/linux/{i386/glob64.c => oldglob.c} (62%)
 delete mode 100644 sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/s390/s390-32/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c
 delete mode 100644 sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/wordsize-64/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/glob.c
 delete mode 100644 sysdeps/wordsize-64/glob.c
 delete mode 100644 sysdeps/wordsize-64/glob64.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 09/18] posix: Use char_array for internal glob dirname
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (16 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 17/18] posix: Use enum for __glob_pattern_type result Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-17 10:19 ` [PATCH v2 00/18] posix: glob fixes and refactor Florian Weimer
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This is the first patch of the set to remove alloca usage on glob
implementation.  Internal path to search for file might expand to a
non static directory derived from pattern for some difference cases
(GLOB_NOESCAPE, GNU GLOB_TILDE) and to allow a non-static dirname
path glob uses a lot of boilerplate code to manage the buffer (which
is either allocated using alloca or malloc depending both to size
requested and the total alloca_used).

The patch changes to use the char_array struct with the default size
(256 bytes).  It simplifies all the allocation code by using char_array
one and every internal buffer access is done using char_array provided
functions.  No functional changes are expected.

Checked on x86_64-linux-gnu.

	* posix/globc.c (glob): Use char_array for dirname.
---
 posix/glob.c | 269 +++++++++++++++++++++++++----------------------------------
 1 file changed, 112 insertions(+), 157 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 554d558..858b709 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -91,6 +91,7 @@
 
 #include <glob_internal.h>
 #include <scratch_buffer.h>
+#include <malloc/char_array-skeleton.c>
 
 #ifndef LOGIN_NAME_MAX
 # define LOGIN_NAME_MAX 256
@@ -315,16 +316,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       glob_t *pglob)
 {
   const char *filename;
-  char *dirname = NULL;
   size_t dirlen;
   int status;
   size_t oldcount;
   int meta;
-  int dirname_modified;
-  int malloc_dirname = 0;
+  bool dirname_modified;
   glob_t dirs;
   int retval = 0;
   size_t alloca_used = 0;
+  struct char_array dirname;
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
     {
@@ -332,6 +332,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       return -1;
     }
 
+  if (!char_array_init_empty (&dirname))
+    return GLOB_NOSPACE;
+
   /* POSIX requires all slashes to be matched.  This means that with
      a trailing slash we must match only directories.  */
   if (pattern[0] && pattern[strlen (pattern) - 1] == '/')
@@ -352,12 +355,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  size_t i;
 
 	  if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *))
-	    return GLOB_NOSPACE;
+	    goto err_nospace;
 
 	  pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
 					      * sizeof (char *));
 	  if (pglob->gl_pathv == NULL)
-	    return GLOB_NOSPACE;
+	    goto err_nospace;
 
 	  for (i = 0; i <= pglob->gl_offs; ++i)
 	    pglob->gl_pathv[i] = NULL;
@@ -415,7 +418,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		      pglob->gl_pathc = 0;
 		      pglob->gl_pathv = NULL;
 		    }
-		  return GLOB_NOSPACE;
+		  goto err_nospace;
 		}
 	    }
 
@@ -431,6 +434,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    illegal_brace:
 	      if (__glibc_unlikely (!alloca_onealt))
 		free (onealt);
+	      char_array_free (&dirname);
 	      return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
 	    }
 
@@ -476,7 +480,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		      globfree (pglob);
 		      pglob->gl_pathc = 0;
 		    }
-		  return result;
+		  retval = result;
+		  goto out;
 		}
 
 	      if (*next == '}')
@@ -493,9 +498,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 
 	  if (pglob->gl_pathc != firstc)
 	    /* We found some entries.  */
-	    return 0;
+	    retval = 0;
 	  else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
-	    return GLOB_NOMATCH;
+	    retval = GLOB_NOMATCH;
+	  goto out;
 	}
     }
 
@@ -511,14 +517,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
   if (filename == NULL)
     filename = strchr (pattern, ':');
 #endif /* __MSDOS__ || WINDOWS32 */
-  dirname_modified = 0;
+  dirname_modified = false;
   if (filename == NULL)
     {
       /* This can mean two things: a simple name or "~name".  The latter
 	 case is nothing but a notation for a directory.  */
       if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~')
 	{
-	  dirname = (char *) pattern;
+	  if (!char_array_set_str (&dirname, pattern))
+	    goto err_nospace;
 	  dirlen = strlen (pattern);
 
 	  /* Set FILENAME to NULL as a special flag.  This is ugly but
@@ -536,10 +543,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 
 	  filename = pattern;
 #ifdef _AMIGA
-	  dirname = (char *) "";
+# define CURRENT_FILENAME ""
 #else
-	  dirname = (char *) ".";
+# define CURRENT_FILENAME "."
 #endif
+	  if (!char_array_set_str (&dirname, CURRENT_FILENAME))
+	    goto err_nospace;
 	  dirlen = 0;
 	}
     }
@@ -548,13 +557,13 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	       && (flags & GLOB_NOESCAPE) == 0))
     {
       /* "/pattern" or "\\/pattern".  */
-      dirname = (char *) "/";
+      if (!char_array_set_str (&dirname, "/"))
+	goto err_nospace;
       dirlen = 1;
       ++filename;
     }
   else
     {
-      char *newp;
       dirlen = filename - pattern;
 #if defined __MSDOS__ || defined WINDOWS32
       if (*filename == ':'
@@ -568,50 +577,48 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* For now, disallow wildcards in the drive spec, to
 	     prevent infinite recursion in glob.  */
 	  if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
-	    return GLOB_NOMATCH;
+	    {
+	      retval = GLOB_NOMATCH;
+	      goto out;
+	    }
 	  /* If this is "d:pattern", we need to copy ':' to DIRNAME
 	     as well.  If it's "d:/pattern", don't remove the slash
 	     from "d:/", since "d:" and "d:/" are not the same.*/
 	}
 #endif
-      if (glob_use_alloca (alloca_used, dirlen + 1))
-	newp = alloca_account (dirlen + 1, alloca_used);
-      else
-	{
-	  newp = malloc (dirlen + 1);
-	  if (newp == NULL)
-	    return GLOB_NOSPACE;
-	  malloc_dirname = 1;
-	}
-      *((char *) mempcpy (newp, pattern, dirlen)) = '\0';
-      dirname = newp;
+      if (!char_array_set_str_size (&dirname, pattern, dirlen))
+	goto err_nospace;
       ++filename;
 
       if (filename[0] == '\0'
 #if defined __MSDOS__ || defined WINDOWS32
-	  && dirname[dirlen - 1] != ':'
-	  && (dirlen < 3 || dirname[dirlen - 2] != ':'
-	      || dirname[dirlen - 1] != '/')
+	  && char_array_pos (&dirname, dirlen - 1) != ':'
+	  && (dirlen < 3 || char_array_pos (&dirname, dirlen - 2) != ':'
+	      || char_array_pos (&dirname, dirlen - 1) != '/')
 #endif
 	  && dirlen > 1)
 	/* "pattern/".  Expand "pattern", appending slashes.  */
 	{
 	  int orig_flags = flags;
           int val;
-	  if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\')
+	  if (!(flags & GLOB_NOESCAPE)
+	      && char_array_pos (&dirname, dirlen - 1) == '\\')
 	    {
 	      /* "pattern\\/".  Remove the final backslash if it hasn't
 		 been quoted.  */
-	      char *p = (char *) &dirname[dirlen - 1];
-
-	      while (p > dirname && p[-1] == '\\') --p;
-	      if ((&dirname[dirlen] - p) & 1)
+	      size_t p = dirlen - 1;
+	      while (p > 0 && char_array_pos (&dirname, p - 1) == '\\') --p;
+	      if ((dirlen - p) & 1)
 		{
-		  *(char *) &dirname[--dirlen] = '\0';
+		  /* Since we are shrinking the array, there is no need to
+		     check the function return.  */
+		  dirlen -= 1;
+		  char_array_crop (&dirname, dirlen);
 		  flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
 		}
 	    }
-	  val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
+	  val = glob (char_array_str (&dirname), flags | GLOB_MARK, errfunc,
+		      pglob);
 	  if (val == 0)
 	    pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
 			       | (flags & GLOB_MARK));
@@ -628,11 +635,14 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	}
     }
 
-  if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
+  if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK))
+      && char_array_pos (&dirname, 0) == '~')
     {
-      if (dirname[1] == '\0' || dirname[1] == '/'
-	  || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\'
-	      && (dirname[2] == '\0' || dirname[2] == '/')))
+      if (char_array_pos (&dirname, 1) == '\0'
+	  || char_array_pos (&dirname, 1) == '/'
+	  || (!(flags & GLOB_NOESCAPE) && char_array_pos (&dirname, 1) == '\\'
+	      && (char_array_pos (&dirname, 2) == '\0'
+		  || char_array_pos (&dirname, 2) == '/')))
 	{
 	  /* Look up home directory.  */
 	  char *home_dir = getenv ("HOME");
@@ -688,10 +698,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 			  break;
 			}
 		      if (!scratch_buffer_grow (&pwtmpbuf))
-			{
-			  retval = GLOB_NOSPACE;
-			  goto out;
-			}
+			goto err_nospace;
 		      __set_errno (save);
 		    }
 #   else
@@ -704,8 +711,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		      if (home_dir == NULL)
 			{
 			  scratch_buffer_free (&pwtmpbuf);
-			  retval = GLOB_NOSPACE;
-			  goto out;
+			  goto err_nospace;
 			}
 		    }
 		  scratch_buffer_free (&pwtmpbuf);
@@ -729,53 +735,26 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 #  endif /* WINDOWS32 */
 # endif
 	  /* Now construct the full directory.  */
-	  if (dirname[1] == '\0')
+	  if (char_array_pos (&dirname, 1) == '\0')
 	    {
-	      if (__glibc_unlikely (malloc_dirname))
-		free (dirname);
-
-	      dirname = home_dir;
-	      dirlen = strlen (dirname);
-	      malloc_dirname = malloc_home_dir;
+	      if (!char_array_set_str (&dirname, home_dir))
+		goto err_nospace;
+	      dirlen = char_array_size (&dirname) - 1;
 	    }
 	  else
 	    {
-	      char *newp;
-	      size_t home_len = strlen (home_dir);
-	      int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen);
-	      if (use_alloca)
-		newp = alloca_account (home_len + dirlen, alloca_used);
-	      else
-		{
-		  newp = malloc (home_len + dirlen);
-		  if (newp == NULL)
-		    {
-		      if (__glibc_unlikely (malloc_home_dir))
-			free (home_dir);
-		      retval = GLOB_NOSPACE;
-		      goto out;
-		    }
-		}
-
-	      mempcpy (mempcpy (newp, home_dir, home_len),
-		       &dirname[1], dirlen);
-
-	      if (__glibc_unlikely (malloc_dirname))
-		free (dirname);
-
-	      dirname = newp;
-	      dirlen += home_len - 1;
-	      malloc_dirname = !use_alloca;
-
-	      if (__glibc_unlikely (malloc_home_dir))
-		free (home_dir);
+	      /* Replaces '~' by the obtained HOME dir.  */
+	      char_array_erase (&dirname, 0);
+	      if (!char_array_prepend_str (&dirname, home_dir))
+		goto err_nospace;
 	    }
-	  dirname_modified = 1;
+	  dirname_modified = true;
 	}
 # if !defined _AMIGA && !defined WINDOWS32
       else
 	{
-	  char *end_name = strchr (dirname, '/');
+	  char *dirnamestr = char_array_at (&dirname, 0);
+	  char *end_name = strchr (dirnamestr, '/');
 	  char user_name[LOGIN_NAME_MAX];
 	  char *unescape = NULL;
 
@@ -783,22 +762,22 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    {
 	      if (end_name == NULL)
 		{
-		  unescape = strchr (dirname, '\\');
+		  unescape = strchr (dirnamestr, '\\');
 		  if (unescape)
 		    end_name = strchr (unescape, '\0');
 		}
 	      else
-		unescape = memchr (dirname, '\\', end_name - dirname);
+		unescape = memchr (dirnamestr, '\\', end_name - dirnamestr);
 	    }
 	  if (end_name == NULL)
-	    strncpy (user_name, dirname + 1, LOGIN_NAME_MAX - 1);
+	    strncpy (user_name, dirnamestr + 1, LOGIN_NAME_MAX - 1);
 	  else
 	    {
 	      if (unescape != NULL)
 		{
-		  ptrdiff_t name_len = unescape - dirname - 1;
+		  ptrdiff_t name_len = unescape - dirnamestr - 1;
 		  name_len = MIN (name_len, LOGIN_NAME_MAX - 1);
-		  char *p = mempcpy (user_name, dirname + 1, name_len);
+		  char *p = mempcpy (user_name, dirnamestr + 1, name_len);
 		  char *q = unescape;
 		  while (*q != '\0')
 		    {
@@ -821,9 +800,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		}
 	      else
 		{
-		  ptrdiff_t name_len = end_name - dirname;
+		  ptrdiff_t name_len = end_name - dirnamestr;
 		  name_len = MIN (name_len, LOGIN_NAME_MAX - 1);
-		  *((char *) mempcpy (user_name, dirname + 1, name_len))
+		  *((char *) mempcpy (user_name, dirnamestr + 1, name_len))
 		    = '\0';
 		}
 	    }
@@ -860,32 +839,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    /* If we found a home directory use this.  */
 	    if (p != NULL)
 	      {
-		size_t home_len = strlen (p->pw_dir);
-		size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
-
-		if (__glibc_unlikely (malloc_dirname))
-		  free (dirname);
-		malloc_dirname = 0;
-
-		if (glob_use_alloca (alloca_used, home_len + rest_len + 1))
-		  dirname = alloca_account (home_len + rest_len + 1,
-					    alloca_used);
-		else
+		if (!char_array_set_str (&dirname, p->pw_dir))
 		  {
-		    dirname = malloc (home_len + rest_len + 1);
-		    if (dirname == NULL)
-		      {
-			scratch_buffer_free (&pwtmpbuf);
-			retval = GLOB_NOSPACE;
-			goto out;
-		      }
-		    malloc_dirname = 1;
+		    scratch_buffer_free (&pwtmpbuf);
+		    retval = GLOB_NOSPACE;
+		    goto out;
 		  }
-		*((char *) mempcpy (mempcpy (dirname, p->pw_dir, home_len),
-				    end_name, rest_len)) = '\0';
 
-		dirlen = home_len + rest_len;
-		dirname_modified = 1;
+		dirlen = strlen (p->pw_dir);
+		dirname_modified = true;
 	      }
 	    else
 	      {
@@ -913,9 +875,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       /* Return the directory if we don't check for error or if it exists.  */
       if ((flags & GLOB_NOCHECK)
 	  || (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
-	       ? ((*pglob->gl_lstat) (dirname, &st) == 0
+	       ? ((*pglob->gl_lstat) (char_array_str (&dirname), &st) == 0
 		  && S_ISDIR (st.st_mode))
-	       : (__lstat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode)))))
+	       : (__lstat64 (char_array_str (&dirname), &st64) == 0
+		  && S_ISDIR (st64.st_mode)))))
 	{
 	  size_t newcount = pglob->gl_pathc + pglob->gl_offs;
 	  char **new_gl_pathv;
@@ -926,8 +889,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      free (pglob->gl_pathv);
 	      pglob->gl_pathv = NULL;
 	      pglob->gl_pathc = 0;
-	      retval = GLOB_NOSPACE;
-	      goto out;
+	      goto err_nospace;
 	    }
 
 	  new_gl_pathv = realloc (pglob->gl_pathv,
@@ -942,28 +904,23 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      pglob->gl_pathv[newcount] = malloc (dirlen + 2);
 	      if (pglob->gl_pathv[newcount] == NULL)
 		goto nospace;
-	      p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
+	      p = mempcpy (pglob->gl_pathv[newcount],
+			   char_array_str (&dirname), dirlen);
 	      p[0] = '/';
 	      p[1] = '\0';
-	      if (__glibc_unlikely (malloc_dirname))
-		free (dirname);
 	    }
 	  else
 	    {
-	      if (__glibc_unlikely (malloc_dirname))
-		pglob->gl_pathv[newcount] = dirname;
-	      else
-		{
-		  pglob->gl_pathv[newcount] = strdup (dirname);
-		  if (pglob->gl_pathv[newcount] == NULL)
-		    goto nospace;
-		}
+	      pglob->gl_pathv[newcount] = strdup (char_array_str (&dirname));
+	      if (pglob->gl_pathv[newcount] == NULL)
+		goto nospace;
 	    }
 	  pglob->gl_pathv[++newcount] = NULL;
 	  ++pglob->gl_pathc;
 	  pglob->gl_flags = flags;
 
-	  return 0;
+	  retval = 0;
+	  goto out;
 	}
 
       /* Not found.  */
@@ -971,7 +928,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       goto out;
     }
 
-  meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
+  meta = __glob_pattern_type (char_array_str (&dirname),
+			      !(flags & GLOB_NOESCAPE));
   /* meta is 1 if correct glob pattern containing metacharacters.
      If meta has bit (1 << 2) set, it means there was an unterminated
      [ which we handle the same, using fnmatch.  Broken unterminated
@@ -984,15 +942,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	 the pattern in each directory found.  */
       size_t i;
 
-      if (!(flags & GLOB_NOESCAPE) && dirlen > 0 && dirname[dirlen - 1] == '\\')
+      if (!(flags & GLOB_NOESCAPE) && dirlen > 0
+	  && char_array_pos (&dirname, dirlen - 1) == '\\')
 	{
 	  /* "foo\\/bar".  Remove the final backslash from dirname
 	     if it has not been quoted.  */
-	  char *p = (char *) &dirname[dirlen - 1];
-
-	  while (p > dirname && p[-1] == '\\') --p;
-	  if ((&dirname[dirlen] - p) & 1)
-	    *(char *) &dirname[--dirlen] = '\0';
+	  size_t p = dirlen - 1;
+	  while (p > 0 && char_array_pos (&dirname, p - 1) == '\\') --p;
+	  if ((dirlen - p) & 1)
+	    char_array_crop (&dirname, --dirlen);
 	}
 
       if (__glibc_unlikely ((flags & GLOB_ALTDIRFUNC) != 0))
@@ -1006,7 +964,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  dirs.gl_lstat = pglob->gl_lstat;
 	}
 
-      status = glob (dirname,
+      status = glob (char_array_str (&dirname),
 		     ((flags & (GLOB_ERR | GLOB_NOESCAPE
 				| GLOB_ALTDIRFUNC))
 		      | GLOB_NOSORT | GLOB_ONLYDIR),
@@ -1068,8 +1026,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      globfree (&dirs);
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      retval = GLOB_NOSPACE;
-	      goto out;
+	      goto err_nospace;
 	    }
 	}
 
@@ -1091,8 +1048,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		{
 		nospace2:
 		  globfree (&dirs);
-		  retval = GLOB_NOSPACE;
-		  goto out;
+		  goto err_nospace;
 		}
 
 	      new_gl_pathv = realloc (pglob->gl_pathv,
@@ -1107,8 +1063,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  globfree (&dirs);
 		  globfree (pglob);
 		  pglob->gl_pathc = 0;
-		  retval = GLOB_NOSPACE;
-		  goto out;
+		  goto err_nospace;
 		}
 
 	      ++pglob->gl_pathc;
@@ -1134,7 +1089,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 
       if (meta & 2)
 	{
-	  char *p = strchr (dirname, '\\'), *q;
+	  char *p = strchr (char_array_str (&dirname), '\\'), *q;
 	  /* We need to unescape the dirname string.  It is certainly
 	     allocated by alloca, as otherwise filename would be NULL
 	     or dirname wouldn't contain backslashes.  */
@@ -1151,12 +1106,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      ++q;
 	    }
 	  while (*p++ != '\0');
-	  dirname_modified = 1;
+	  dirname_modified = true;
 	}
       if (dirname_modified)
 	flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
-      status = glob_in_dir (filename, dirname, flags, errfunc, pglob,
-			    alloca_used);
+      status = glob_in_dir (filename, char_array_str (&dirname), flags,
+			    errfunc, pglob, alloca_used);
       if (status != 0)
 	{
 	  if (status == GLOB_NOMATCH && flags != orig_flags
@@ -1174,14 +1129,13 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       if (dirlen > 0)
 	{
 	  /* Stick the directory on the front of each name.  */
-	  if (prefix_array (dirname,
+	  if (prefix_array (char_array_str (&dirname),
 			    &pglob->gl_pathv[old_pathc + pglob->gl_offs],
 			    pglob->gl_pathc - old_pathc))
 	    {
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      retval = GLOB_NOSPACE;
-	      goto out;
+	      goto err_nospace;
 	    }
 	}
     }
@@ -1206,8 +1160,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      {
 		globfree (pglob);
 		pglob->gl_pathc = 0;
-		retval = GLOB_NOSPACE;
-		goto out;
+		goto err_nospace;
 	      }
 	    strcpy (&new[len - 2], "/");
 	    pglob->gl_pathv[i] = new;
@@ -1223,10 +1176,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
     }
 
  out:
-  if (__glibc_unlikely (malloc_dirname))
-    free (dirname);
-
+  char_array_free (&dirname);
   return retval;
+
+ err_nospace:
+  char_array_free (&dirname);
+  return GLOB_NOSPACE;
 }
 #if defined _LIBC && !defined glob
 libc_hidden_def (glob)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 16/18] posix: More check for overflow allocation in glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (8 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 03/18] posix: Consolidate glob implementation Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 07/18] posix: User LOGIN_NAME_MAX for all user names " Adhemerval Zanella
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch adds and replace the allocation overflow based using
malloc internal functions check_add_wrapv_size_t and __libc_reallocarray.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob_malloc_incr): New function.
	(glob_malloc_incr2): Likewise.
	(glob_realloc_incr): Likewise.
	(glob): Use glob_{realloc,malloc}_incr{2}.
---
 posix/glob.c | 92 +++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 63 insertions(+), 29 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index c85342a..647334d 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -303,6 +303,39 @@ get_home_directory (const char *user_name, struct char_array *home_dir)
   return retval;
 }
 
+/* Allocate '(size + incr) * typesize' bytes while for overflow on the
+   arithmetic operations.  */
+static void *
+glob_malloc_incr (size_t size, size_t incr, size_t typesize)
+{
+  size_t newsize;
+  if (check_add_overflow_size_t (size, incr, &newsize))
+    return NULL;
+  return __libc_reallocarray (NULL, newsize, typesize);
+}
+
+/* Allocate '(size + incr1 + incr2) * typesize' bytes while for overflow on
+   the arithmetic operations.  */
+static void *
+glob_malloc_incr2 (size_t size, size_t incr1, size_t incr2, size_t typesize)
+{
+  size_t newsize;
+  if (check_add_overflow_size_t (size, incr1, &newsize)
+      || check_add_overflow_size_t (newsize, incr2, &newsize))
+    return NULL;
+  return __libc_reallocarray (NULL, newsize, typesize);
+}
+
+/* Reallocate '(size + incr1) * typesize' bytes while for overflow on the
+   arithmetic operations.  */
+static void *
+glob_realloc_incr (void *old, size_t size, size_t incr, size_t typesize)
+{
+  size_t newsize;
+  if (check_add_overflow_size_t (size, incr, &newsize))
+    return NULL;
+  return __libc_reallocarray (old, newsize, typesize);
+}
 
 /* Do glob searching for PATTERN, placing results in PGLOB.
    The bits defined above may be set in FLAGS.
@@ -357,11 +390,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	{
 	  size_t i;
 
-	  if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *))
-	    goto err_nospace;
-
-	  pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
-					      * sizeof (char *));
+	  pglob->gl_pathv = glob_malloc_incr (pglob->gl_offs, 1,
+					      sizeof (char *));
 	  if (pglob->gl_pathv == NULL)
 	    goto err_nospace;
 
@@ -817,10 +847,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	       : (__lstat64 (char_array_str (&dirname), &st64) == 0
 		  && S_ISDIR (st64.st_mode)))))
 	{
-	  size_t newcount = pglob->gl_pathc + pglob->gl_offs;
 	  char **new_gl_pathv;
+	  size_t newcount;
 
-          if (newcount > SIZE_MAX / sizeof (char *) - 2)
+	  if (check_add_overflow_size_t (pglob->gl_pathc, pglob->gl_offs,
+					 &newcount))
 	    {
 	    nospace:
 	      free (pglob->gl_pathv);
@@ -829,8 +860,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      goto err_nospace;
 	    }
 
-	  new_gl_pathv = realloc (pglob->gl_pathv,
-				  (newcount + 2) * sizeof (char *));
+	  new_gl_pathv = glob_realloc_incr (pglob->gl_pathv, newcount, 2,
+					    sizeof (char *));
 	  if (new_gl_pathv == NULL)
 	    goto nospace;
 	  pglob->gl_pathv = new_gl_pathv;
@@ -838,9 +869,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  if (flags & GLOB_MARK)
 	    {
 	      char *p;
-	      pglob->gl_pathv[newcount] = malloc (dirlen + 2);
+
+	      pglob->gl_pathv[newcount] = glob_malloc_incr (dirlen, 2,
+							    sizeof (char));
 	      if (pglob->gl_pathv[newcount] == NULL)
 		goto nospace;
+
 	      p = mempcpy (pglob->gl_pathv[newcount],
 			   char_array_str (&dirname), dirlen);
 	      p[0] = '/';
@@ -978,18 +1012,19 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* No matches.  */
 	  if (flags & GLOB_NOCHECK)
 	    {
-	      size_t newcount = pglob->gl_pathc + pglob->gl_offs;
+	      size_t newcount;
 	      char **new_gl_pathv;
 
-	      if (newcount > SIZE_MAX / sizeof (char *) - 2)
+	      if (check_add_overflow_size_t (pglob->gl_pathc, pglob->gl_offs,
+					     &newcount))
 		{
 		nospace2:
 		  globfree (&dirs);
 		  goto err_nospace;
 		}
 
-	      new_gl_pathv = realloc (pglob->gl_pathv,
-				      (newcount + 2) * sizeof (char *));
+	      new_gl_pathv = glob_realloc_incr (pglob->gl_pathv, newcount, 2,
+						sizeof (char *));
 	      if (new_gl_pathv == NULL)
 		goto nospace2;
 	      pglob->gl_pathv = new_gl_pathv;
@@ -1091,15 +1126,16 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	     : (__lstat64 (pglob->gl_pathv[i], &st64) == 0
 		&& (S_ISDIR (st64.st_mode) || S_ISLNK (st64.st_mode)))))
 	  {
-	    size_t len = strlen (pglob->gl_pathv[i]) + 2;
-	    char *new = realloc (pglob->gl_pathv[i], len);
+	    size_t len = strlen (pglob->gl_pathv[i]);
+	    char *new = glob_realloc_incr (pglob->gl_pathv[i], len, 2,
+					   sizeof (char));
 	    if (new == NULL)
 	      {
 		globfree (pglob);
 		pglob->gl_pathc = 0;
 		goto err_nospace;
 	      }
-	    strcpy (&new[len - 2], "/");
+	    strcpy (&new[len], "/");
 	    pglob->gl_pathv[i] = new;
 	  }
     }
@@ -1184,7 +1220,7 @@ prefix_array (const char *dirname, char **array, size_t n)
   for (i = 0; i < n; ++i)
     {
       size_t eltlen = strlen (array[i]) + 1;
-      char *new = malloc (dirlen + 1 + eltlen);
+      char *new = glob_malloc_incr2 (dirlen, 1, eltlen, sizeof (char));
       if (new == NULL)
 	{
 	  while (i > 0)
@@ -1192,11 +1228,10 @@ prefix_array (const char *dirname, char **array, size_t n)
 	  return 1;
 	}
 
-      {
-	char *endp = mempcpy (new, dirname, dirlen);
-	*endp++ = DIRSEP_CHAR;
-	mempcpy (endp, array[i], eltlen);
-      }
+      char *endp = mempcpy (new, dirname, dirlen);
+      *endp++ = DIRSEP_CHAR;
+      mempcpy (endp, array[i], eltlen);
+
       free (array[i]);
       array[i] = new;
     }
@@ -1349,16 +1384,15 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
   if (nfound != 0)
     {
       char **new_gl_pathv;
+      size_t newlen;
       result = 0;
 
-      if (SIZE_MAX / sizeof (char *) - pglob->gl_pathc
-          < pglob->gl_offs + nfound + 1)
+      if (check_add_overflow_size_t (pglob->gl_pathc, pglob->gl_offs, &newlen)
+	  || check_add_overflow_size_t (newlen, nfound, &newlen)
+	  || check_add_overflow_size_t (newlen, 1, &newlen))
 	goto memory_error;
 
-      new_gl_pathv
-	= realloc (pglob->gl_pathv,
-		   (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
-		    * sizeof (char *));
+      new_gl_pathv = realloc (pglob->gl_pathv, newlen * sizeof (char *));
 
       if (new_gl_pathv == NULL)
 	{
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866]
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 11/18] posix: Remove alloca usage on glob dirname Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 18/18] posix: Fix glob with GLOB_NOCHECK returning modified patterns (BZ#10246) Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-31 22:11   ` Paul Eggert
  2017-08-11 14:51 ` [PATCH 08/18] malloc: Add specialized dynarray for C strings Adhemerval Zanella
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch makes glob match dangling symlinks.  Compared to other glob
implementation (*BSD, bash, musl, and other shells as well), GLIBC seems
the be the only one that does not match dangling symlinks.  As for
comment #5 in BZ #866, POSIX does not have any strict specification for
dangling symlinks match and it is reasonable that trying to glob everything
in a path should return all types of files (such as for a 'rm *').  Also,
comment #7 shows even more example where GLIBC current behavior is
unexepected.

I avoided adding another GNU specific flag to set this behavior and
instead make it the default.  Although this change the semanthic from
previous implementation, I think adding another compat symbol to be
really unecessary as from aforementioned reasons (current behavior not
defined in any standard, general idea of different implementation is
to list dangling symbols).

Checked on x86_64-linux-gnu.

	* posix/Makefile (tests): Add tst-glob_symlinks and remove tst-glob3.
	* posix/bug-glob1.c: Remove file.
	* posix/glob.c (glob): Match dangling symlinks.
	(link_exists2_p): Remove function.
	(link_exists_p): Likewise.
	* posix/tst-glob_symlinks.c: New file.
	* sysdeps/gnu/glob64.c (__stat): Redefine to __lstat.
	* sysdeps/unix/sysv/linux/i386/glob64.c (__stat): Likewise.
---
 posix/Makefile            |   3 +-
 posix/bug-glob1.c         |  88 ------------------------------
 posix/glob.c              | 124 +++++++++++-------------------------------
 posix/tst-glob_symlinks.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 165 insertions(+), 183 deletions(-)
 delete mode 100644 posix/bug-glob1.c
 create mode 100644 posix/tst-glob_symlinks.c

diff --git a/posix/Makefile b/posix/Makefile
index 8340549..91f78c9 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -69,7 +69,7 @@ tests		:= test-errno tstgetopt testfnm runtests runptests \
 		   tst-mmap tst-mmap-offset tst-getaddrinfo tst-truncate \
 		   tst-truncate64 tst-fork tst-fnmatch tst-regexloc tst-dir \
 		   tst-chmod bug-regex1 bug-regex2 bug-regex3 bug-regex4 \
-		   tst-gnuglob tst-regex bug-regex6 bug-regex7 \
+		   tst-gnuglob tst-glob_symlinks tst-regex bug-regex6 bug-regex7 \
 		   bug-regex8 bug-regex9 bug-regex10 bug-regex11 bug-regex12 \
 		   bug-regex13 bug-regex14 bug-regex15 bug-regex16 \
 		   bug-regex17 bug-regex18 bug-regex19 \
@@ -250,7 +250,6 @@ tst-rxspencer-ARGS = --utf8 rxspencer/tests
 tst-rxspencer-no-utf8-ARGS = rxspencer/tests
 tst-pcre-ARGS = PCRE.tests
 tst-boost-ARGS = BOOST.tests
-bug-glob1-ARGS = "$(objpfx)"
 tst-execvp3-ARGS = --test-dir=$(objpfx)
 
 testcases.h: TESTS TESTS2C.sed
diff --git a/posix/bug-glob1.c b/posix/bug-glob1.c
deleted file mode 100644
index 05c2da7..0000000
--- a/posix/bug-glob1.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Test case for globbing dangling symlink.  By Ulrich Drepper.  */
-#include <errno.h>
-#include <error.h>
-#include <glob.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-
-static void prepare (int argc, char *argv[]);
-#define PREPARE prepare
-static int do_test (void);
-#define TEST_FUNCTION do_test ()
-
-#include "../test-skeleton.c"
-
-
-static char *fname;
-
-static void
-prepare (int argc, char *argv[])
-{
-  if (argc < 2)
-    error (EXIT_FAILURE, 0, "missing argument");
-
-  size_t len = strlen (argv[1]);
-  static const char ext[] = "globXXXXXX";
-  fname = malloc (len + sizeof (ext));
-  if (fname == NULL)
-    error (EXIT_FAILURE, errno, "cannot create temp file");
- again:
-  strcpy (stpcpy (fname, argv[1]), ext);
-  fname = mktemp (fname);
-  if (fname == NULL || *fname == '\0')
-    error (EXIT_FAILURE, errno, "cannot create temp file name");
-  if (symlink ("bug-glob1-does-not-exist", fname) != 0)
-    {
-      if (errno == EEXIST)
-	goto again;
-
-      error (EXIT_FAILURE, errno, "cannot create symlink");
-    }
-  add_temp_file (fname);
-}
-
-
-static int
-do_test (void)
-{
-  glob_t gl;
-  int retval = 0;
-  int e;
-
-  e = glob (fname, 0, NULL, &gl);
-  if (e == 0)
-    {
-      printf ("glob(\"%s\") succeeded\n", fname);
-      retval = 1;
-    }
-  globfree (&gl);
-
-  size_t fnamelen = strlen (fname);
-  char buf[fnamelen + 2];
-
-  strcpy (buf, fname);
-  buf[fnamelen - 1] = '?';
-  e = glob (buf, 0, NULL, &gl);
-  if (e == 0)
-    {
-      printf ("glob(\"%s\") succeeded\n", buf);
-      retval = 1;
-    }
-  globfree (&gl);
-
-  strcpy (buf, fname);
-  buf[fnamelen] = '*';
-  buf[fnamelen + 1] = '\0';
-  e = glob (buf, 0, NULL, &gl);
-  if (e == 0)
-    {
-      printf ("glob(\"%s\") succeeded\n", buf);
-      retval = 1;
-    }
-  globfree (&gl);
-
-  return retval;
-}
diff --git a/posix/glob.c b/posix/glob.c
index dcfbc78..7b6b426 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -69,8 +69,8 @@
 # define readdir(str) __readdir64 (str)
 # define getpwnam_r(name, bufp, buf, len, res) \
    __getpwnam_r (name, bufp, buf, len, res)
-# ifndef __stat64
-#  define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
+# ifndef __lstat64
+#  define __lstat64(fname, buf) __lxstat64 (_STAT_VER, fname, buf)
 # endif
 # define struct_stat64		struct stat64
 #else /* !_LIBC */
@@ -1046,9 +1046,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       /* Return the directory if we don't check for error or if it exists.  */
       if ((flags & GLOB_NOCHECK)
 	  || (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
-	       ? ((*pglob->gl_stat) (dirname, &st) == 0
+	       ? ((*pglob->gl_lstat) (dirname, &st) == 0
 		  && S_ISDIR (st.st_mode))
-	       : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode)))))
+	       : (__lstat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode)))))
 	{
 	  size_t newcount = pglob->gl_pathc + pglob->gl_offs;
 	  char **new_gl_pathv;
@@ -1328,10 +1328,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 
       for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i)
 	if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-	     ? ((*pglob->gl_stat) (pglob->gl_pathv[i], &st) == 0
-		&& S_ISDIR (st.st_mode))
-	     : (__stat64 (pglob->gl_pathv[i], &st64) == 0
-		&& S_ISDIR (st64.st_mode))))
+	     ? ((*pglob->gl_lstat) (pglob->gl_pathv[i], &st) == 0
+		&& (S_ISDIR (st.st_mode) || S_ISLNK (st.st_mode)))
+	     : (__lstat64 (pglob->gl_pathv[i], &st64) == 0
+		&& (S_ISDIR (st64.st_mode) || S_ISLNK (st64.st_mode)))))
 	  {
 	    size_t len = strlen (pglob->gl_pathv[i]) + 2;
 	    char *new = realloc (pglob->gl_pathv[i], len);
@@ -1441,58 +1441,6 @@ prefix_array (const char *dirname, char **array, size_t n)
   return 0;
 }
 
-/* We put this in a separate function mainly to allow the memory
-   allocated with alloca to be recycled.  */
-static int
-__attribute_noinline__
-link_stat (const char *dir, size_t dirlen, const char *fname,
-	       glob_t *pglob
-# ifndef _LIBC
-		, int flags
-# endif
-		)
-{
-  size_t fnamelen = strlen (fname);
-  char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1);
-  struct stat st;
-# ifndef _LIBC
-  struct_stat64 st64;
-# endif
-
-  mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1),
-	   fname, fnamelen + 1);
-
-# if !defined _LIBC && !HAVE_FSTATAT
-  if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) == 0, 1))
-    {
-      struct_stat64 st64;
-      return __stat64 (fullname, &st64);
-    }
-# endif
-  return (*pglob->gl_stat) (fullname, &st);
-}
-
-/* Return true if DIR/FNAME exists.  */
-static int
-link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname,
-               glob_t *pglob, int flags)
-{
-  int status;
-# if defined _LIBC || HAVE_FSTATAT
-  if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
-    status = link_stat (dir, dirlen, fname, pglob);
-  else
-    {
-      /* dfd cannot be -1 here, because dirfd never returns -1 on
-         glibc, or on hosts that have fstatat.  */
-      struct_stat64 st64;
-      status = __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0);
-    }
-# else
-  status = link_stat (dir, dirlen, fname, pglob, flags);
-# endif
-  return status == 0 || errno == EOVERFLOW;
-}
 
 /* Like `glob', but PATTERN is a final pathname component,
    and matches are searched for in DIRECTORY.
@@ -1563,7 +1511,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	       pattern, patlen + 1);
       if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
 	   ? (*pglob->gl_stat) (fullname, &ust.st)
-	   : __stat64 (fullname, &ust.st64))
+	   : __lstat64 (fullname, &ust.st64))
 	  == 0)
 	  || errno == EOVERFLOW)
 	/* We found this file to be existing.  Now tell the rest
@@ -1587,8 +1535,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	}
       else
 	{
-	  int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-		     ? -1 : dirfd ((DIR *) stream));
 	  int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
 			   | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
 #if defined _AMIGA || defined VMS
@@ -1624,38 +1570,30 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 
 	      if (fnmatch (pattern, d.name, fnm_flags) == 0)
 		{
-		  /* If the file we found is a symlink we have to
-		     make sure the target file exists.  */
-		  if (!readdir_result_might_be_symlink (d)
-		      || link_exists_p (dfd, directory, dirlen, d.name,
-					pglob, flags))
+		  if (cur == names->count)
 		    {
-		      if (cur == names->count)
-			{
-			  struct globnames *newnames;
-			  size_t count = names->count * 2;
-			  size_t size = (sizeof (struct globnames)
-					 + ((count - INITIAL_COUNT)
-					    * sizeof (char *)));
-			  if (glob_use_alloca (alloca_used, size))
-			    newnames = names_alloca
-			      = alloca_account (size, alloca_used);
-			  else if ((newnames = malloc (size))
-				   == NULL)
-			    goto memory_error;
-			  newnames->count = count;
-			  newnames->next = names;
-			  names = newnames;
-			  cur = 0;
-			}
-		      names->name[cur] = strdup (d.name);
-		      if (names->name[cur] == NULL)
-			goto memory_error;
-		      ++cur;
-		      ++nfound;
-		      if (SIZE_MAX - pglob->gl_offs <= nfound)
+		      struct globnames *newnames;
+		      size_t count = names->count * 2;
+		      size_t size = (sizeof (struct globnames)
+				     + ((count - INITIAL_COUNT)
+				     * sizeof (char *)));
+		      if (glob_use_alloca (alloca_used, size))
+			newnames = names_alloca
+			  = alloca_account (size, alloca_used);
+		      else if ((newnames = malloc (size)) == NULL)
 			goto memory_error;
-		    }
+		      newnames->count = count;
+		      newnames->next = names;
+		      names = newnames;
+		      cur = 0;
+		   }
+		   names->name[cur] = strdup (d.name);
+		   if (names->name[cur] == NULL)
+		     goto memory_error;
+		   ++cur;
+		   ++nfound;
+		   if (SIZE_MAX - pglob->gl_offs <= nfound)
+		     goto memory_error;
 		}
 	    }
 	}
diff --git a/posix/tst-glob_symlinks.c b/posix/tst-glob_symlinks.c
new file mode 100644
index 0000000..4af8287
--- /dev/null
+++ b/posix/tst-glob_symlinks.c
@@ -0,0 +1,133 @@
+/* Test glob danglin symlink match (BZ #866).
+   for the filesystem access functions.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stddef.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+static void do_prepare (int argc, char *argv[]);
+#define PREPARE do_prepare
+static int do_test (void);
+#include <support/test-driver.c>
+
+static void
+create_link (const char *base, const char *fname, char *linkname,
+	     size_t linknamesize)
+{
+  int ntries = 0;
+  while (1)
+    {
+      snprintf (linkname, linknamesize, "%s/%s%02d", test_dir, base,
+		ntries);
+      if (symlink (fname, linkname) == 0)
+	break;
+      if (errno != EEXIST)
+	FAIL_EXIT1 ("symlink failed: %m");
+      if (ntries++ == 10)
+	FAIL_EXIT1 ("symlink failed with EEXIST too many times");
+    }
+  add_temp_file (linkname);
+}
+
+static char valid_link[PATH_MAX];
+static char dangling_link[PATH_MAX];
+static char dangling_dir[PATH_MAX];
+
+static void
+do_prepare (int argc, char *argv[])
+{
+  char *fname;
+
+  create_temp_file ("tst-glob_symlinks.", &fname);
+
+  /* Create a existing symlink.  */
+  create_link ("valid-symlink-tst-glob_symlinks", fname, valid_link,
+	       sizeof valid_link);
+
+  /* Create a dangling symlink to a file.  */
+  int fd = create_temp_file ("dangling-tst-glob_file", &fname);
+  TEST_VERIFY_EXIT (close (fd) == 0);
+  /* It throws an warning at process end due 'add_temp_file' trying to
+     unlink it again.  */
+  TEST_VERIFY_EXIT (unlink (fname) == 0);
+  create_link ("dangling-symlink-file-tst-glob", fname, dangling_link,
+	       sizeof dangling_link);
+
+  /* Create a dangling symlink to a directory.  */
+  char tmpdir[PATH_MAX];
+  snprintf (tmpdir, sizeof tmpdir, "%s/dangling-tst-glob_folder.XXXXXX",
+	    test_dir);
+  TEST_VERIFY_EXIT (mkdtemp (tmpdir) != NULL);
+  create_link ("dangling-symlink-dir-tst-glob", tmpdir, dangling_dir,
+	       sizeof dangling_dir);
+  TEST_VERIFY_EXIT (rmdir (tmpdir) == 0);
+}
+
+static int
+do_test (void)
+{
+  char buf[PATH_MAX];
+  glob_t gl;
+
+  TEST_VERIFY_EXIT (glob (valid_link, 0, NULL, &gl) == 0);
+  TEST_VERIFY_EXIT (gl.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], valid_link) == 0);
+  globfree (&gl);
+
+  TEST_VERIFY_EXIT (glob (dangling_link, 0, NULL, &gl) == 0);
+  TEST_VERIFY_EXIT (gl.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], dangling_link) == 0);
+  globfree (&gl);
+
+  TEST_VERIFY_EXIT (glob (dangling_dir, 0, NULL, &gl) == 0);
+  TEST_VERIFY_EXIT (gl.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], dangling_dir) == 0);
+  globfree (&gl);
+
+  snprintf (buf, sizeof buf, "%s", dangling_link);
+  buf[strlen(buf) - 1] = '?';
+  TEST_VERIFY_EXIT (glob (buf, 0, NULL, &gl) == 0);
+  TEST_VERIFY_EXIT (gl.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], dangling_link) == 0);
+  globfree (&gl);
+
+  /* glob should handle dangling symbol as normal file, so <file>? should
+     return an empty string.  */
+  snprintf (buf, sizeof buf, "%s?", dangling_link);
+  TEST_VERIFY_EXIT (glob (buf, 0, NULL, &gl) != 0);
+  globfree (&gl);
+
+  snprintf (buf, sizeof buf, "%s*", dangling_link);
+  TEST_VERIFY_EXIT (glob (buf, 0, NULL, &gl) == 0);
+  TEST_VERIFY_EXIT (gl.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], dangling_link) == 0);
+  globfree (&gl);
+
+  return 0;
+}
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 10/18] posix: Remove alloca usage for GLOB_BRACE on glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (12 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 15/18] posix: Add common function to get home directory Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 02/18] posix: Adjust glob tests to libsupport Adhemerval Zanella
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

GNU GLOB_BRACE internal implementation constructs a new expression and
calls glob recursively.  It then requires a possible large temporary
buffer place the new pattern.

This patch removes the alloca/malloc usage and replaces it with
char_array.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob): Remove alloca usage for onealt.
---
 posix/glob.c | 63 ++++++++++++++++++++++++++++++------------------------------
 1 file changed, 32 insertions(+), 31 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 858b709..1892f48 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -398,44 +398,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* Allocate working buffer large enough for our work.  Note that
 	     we have at least an opening and closing brace.  */
 	  size_t firstc;
-	  char *alt_start;
 	  const char *p;
 	  const char *next;
 	  const char *rest;
 	  size_t rest_len;
-	  char *onealt;
-	  size_t pattern_len = strlen (pattern) - 1;
-	  int alloca_onealt = glob_use_alloca (alloca_used, pattern_len);
-	  if (alloca_onealt)
-	    onealt = alloca_account (pattern_len, alloca_used);
-	  else
+	  struct char_array onealt;
+
+	  /* We know the prefix for all sub-patterns.  */
+	  ptrdiff_t onealtlen = begin - pattern;
+	  if (!char_array_init_str_size (&onealt, pattern, onealtlen))
 	    {
-	      onealt = malloc (pattern_len);
-	      if (onealt == NULL)
+	      if (!(flags & GLOB_APPEND))
 		{
-		  if (!(flags & GLOB_APPEND))
-		    {
-		      pglob->gl_pathc = 0;
-		      pglob->gl_pathv = NULL;
-		    }
-		  goto err_nospace;
+		  pglob->gl_pathc = 0;
+		  pglob->gl_pathv = NULL;
 		}
+	      goto err_nospace;
 	    }
 
-	  /* We know the prefix for all sub-patterns.  */
-	  alt_start = mempcpy (onealt, pattern, begin - pattern);
-
 	  /* Find the first sub-pattern and at the same time find the
 	     rest after the closing brace.  */
 	  next = next_brace_sub (begin + 1, flags);
 	  if (next == NULL)
 	    {
 	      /* It is an invalid expression.  */
-	    illegal_brace:
-	      if (__glibc_unlikely (!alloca_onealt))
-		free (onealt);
-	      char_array_free (&dirname);
-	      return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
+	      char_array_free (&onealt);
+	      goto illegal_brace;
 	    }
 
 	  /* Now find the end of the whole brace expression.  */
@@ -444,8 +432,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    {
 	      rest = next_brace_sub (rest + 1, flags);
 	      if (rest == NULL)
-		/* It is an illegal expression.  */
-		goto illegal_brace;
+		{
+		  /* It is an illegal expression.  */
+		  char_array_free (&onealt);
+		  goto illegal_brace;
+		}
 	    }
 	  /* Please note that we now can be sure the brace expression
 	     is well-formed.  */
@@ -464,17 +455,24 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      int result;
 
 	      /* Construct the new glob expression.  */
-	      mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len);
+	      ptrdiff_t nextlen = next - p;
+	      if (!char_array_replace_str_pos (&onealt, onealtlen, p, nextlen)
+		  || !char_array_replace_str_pos (&onealt, onealtlen + nextlen,
+						  rest, rest_len))
+		{
+		  char_array_free (&onealt);
+		  retval = GLOB_NOSPACE;
+		  goto out;
+		}
 
-	      result = glob (onealt,
+	      result = glob (char_array_str (&onealt),
 			     ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
 			      | GLOB_APPEND), errfunc, pglob);
 
 	      /* If we got an error, return it.  */
 	      if (result && result != GLOB_NOMATCH)
 		{
-		  if (__glibc_unlikely (!alloca_onealt))
-		    free (onealt);
+		  char_array_free (&onealt);
 		  if (!(flags & GLOB_APPEND))
 		    {
 		      globfree (pglob);
@@ -493,8 +491,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      assert (next != NULL);
 	    }
 
-	  if (__glibc_unlikely (!alloca_onealt))
-	    free (onealt);
+	  char_array_free (&onealt);
 
 	  if (pglob->gl_pathc != firstc)
 	    /* We found some entries.  */
@@ -1182,6 +1179,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
  err_nospace:
   char_array_free (&dirname);
   return GLOB_NOSPACE;
+
+ illegal_brace:
+  char_array_free (&dirname);
+  return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
 }
 #if defined _LIBC && !defined glob
 libc_hidden_def (glob)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 01/18] posix: Sync glob with gnulib [BZ #1062]
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (6 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 12/18] posix: Use dynarray for globname " Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 03/18] posix: Consolidate glob implementation Adhemerval Zanella
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch syncs posix/glob.c implementation with gnulib version
1dc82a77fa606e18edf.  The main differences to gnulib code:

  1. Commit 44c637c (Properly initialize glob structure with
     GLOB_BRACE|GLOB_DOOFFS) which fixes BZ# 20707.

  2. No inclusion of flexmember.h header and its usage on glob.
     The code is meant to be rewritten and header is unrequired in
     following patches in this set.

  3. An additional define (GLOB_COMPAT_BUILD) to avoid building
     size_and_wrapv and gblo_use_alloca twice on some configurations
     (i368 compat code) due multiple inclusion.

The main changes are:

  - Header organization mostly due gnulib requirements.  It leads
    to some simplification and less conditional includes.

  - Use of glob_use_alloca with wraps up __libc_use_alloca with
    saturated math for the total size calculation.

  - Simplify some size allocation overflow calculation.

  - Some fixed on non supported glibc systems.

  - Some comments adjustments.

The changes does not alter current glob internal semantic.  I also
added a missing globfree on posix/globtest.c (it helps silence
some valgrind or other memory profilers).

	[BZ #1062]
	* posix/glob.c: Sync with gnulib.
	* posix/globtest.c (main): Add final globfree.
	* sysdeps/unix/sysv/linux/i386/glob64.c (GLOB_COMPAT_BUILD):
	Define.
---
 posix/glob.c                          | 526 ++++++++++++++++++++--------------
 posix/globtest.c                      |   3 +
 sysdeps/unix/sysv/linux/i386/glob64.c |   2 +
 3 files changed, 322 insertions(+), 209 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index c653809..5e4b7fc 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -15,7 +15,10 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#ifdef	HAVE_CONFIG_H
+#ifndef _LIBC
+/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
+   optimizes away the pattern == NULL || pglob == NULL tests below.  */
+# define _GL_ARG_NONNULL(params)
 # include <config.h>
 #endif
 
@@ -34,22 +37,19 @@
 
 #include <stdio.h>		/* Needed on stupid SunOS for assert.  */
 
-#if !defined _LIBC || !defined GLOB_ONLY_P
-#if defined HAVE_UNISTD_H || defined _LIBC
-# include <unistd.h>
-# ifndef POSIX
-#  ifdef _POSIX_VERSION
-#   define POSIX
-#  endif
-# endif
+#ifndef GLOB_ONLY_P
+
+#include <unistd.h>
+#if !defined POSIX && defined _POSIX_VERSION
+# define POSIX
 #endif
 
-#include <pwd.h>
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WINDOWS32
+#endif
 
-#if defined HAVE_STDINT_H || defined _LIBC
-# include <stdint.h>
-#elif !defined UINTPTR_MAX
-# define UINTPTR_MAX (~((size_t) 0))
+#ifndef WINDOWS32
+# include <pwd.h>
 #endif
 
 #include <errno.h>
@@ -57,24 +57,7 @@
 # define __set_errno(val) errno = (val)
 #endif
 
-#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__
-# include <dirent.h>
-#else
-# define dirent direct
-# ifdef HAVE_SYS_NDIR_H
-#  include <sys/ndir.h>
-# endif
-# ifdef HAVE_SYS_DIR_H
-#  include <sys/dir.h>
-# endif
-# ifdef HAVE_NDIR_H
-#  include <ndir.h>
-# endif
-# ifdef HAVE_VMSDIR_H
-#  include "vmsdir.h"
-# endif /* HAVE_VMSDIR_H */
-#endif
-
+#include <dirent.h>
 #include <stdlib.h>
 #include <string.h>
 #include <alloca.h>
@@ -93,17 +76,16 @@
 # endif
 # define struct_stat64		struct stat64
 #else /* !_LIBC */
-# include "getlogin_r.h"
-# include "mempcpy.h"
-# include "stat-macros.h"
-# include "strdup.h"
-# define __stat64(fname, buf)	stat (fname, buf)
-# define struct_stat64		struct stat
-# define __stat(fname, buf)	stat (fname, buf)
-# define __alloca		alloca
-# define __readdir		readdir
-# define __readdir64		readdir64
-# define __glob_pattern_p	glob_pattern_p
+# define __getlogin_r(buf, len) getlogin_r (buf, len)
+# define __stat64(fname, buf)   stat (fname, buf)
+# define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag)
+# define struct_stat64          struct stat
+# ifndef __MVS__
+#  define __alloca              alloca
+# endif
+# define __readdir              readdir
+# define __glob_pattern_p       glob_pattern_p
+# define COMPILE_GLOB64
 #endif /* _LIBC */
 
 #include <fnmatch.h>
@@ -186,7 +168,7 @@ readdir_result_might_be_dir (struct readdir_result d)
     D_INO_TO_RESULT (source)		   \
   }
 
-#endif /* !defined _LIBC || !defined GLOB_ONLY_P */
+#endif /* !defined GLOB_ONLY_P */
 
 /* Call gl_readdir on STREAM.  This macro can be overridden to reduce
    type safety if an old interface version needs to be supported.  */
@@ -230,13 +212,74 @@ convert_dirent64 (const struct dirent64 *source)
 # define attribute_hidden
 #endif
 
+#ifndef __attribute_noinline__
+# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 1)
+#  define __attribute_noinline__ /* Ignore */
+#else
+#  define __attribute_noinline__ __attribute__ ((__noinline__))
+# endif
+#endif
+
+#ifndef __glibc_unlikely
+# define __glibc_unlikely(expr) __builtin_expect (expr, 0)
+#endif
+
+#ifndef _LIBC
+/* The results of opendir() in this file are not used with dirfd and fchdir,
+   and we do not leak fds to any single-threaded code that could use stdio,
+   therefore save some unnecessary recursion in fchdir.c and opendir_safer.c.
+   FIXME - if the kernel ever adds support for multi-thread safety for
+   avoiding standard fds, then we should use opendir_safer.  */
+# ifdef GNULIB_defined_opendir
+#  undef opendir
+# endif
+# ifdef GNULIB_defined_closedir
+#  undef closedir
+# endif
+
+/* Just use malloc.  */
+# define __libc_use_alloca(n) false
+# define alloca_account(len, avar) ((void) (len), (void) (avar), (void *) 0)
+# define extend_alloca_account(buf, len, newlen, avar) \
+    ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0)
+#endif
+
+/* Set *R = A + B.  Return true if the answer is mathematically
+   incorrect due to overflow; in this case, *R is the low order
+   bits of the correct answer..  */
+
+static bool size_add_wrapv (size_t a, size_t b, size_t *r);
+static bool glob_use_alloca (size_t alloca_used, size_t len);
+
+/* We must not compile this function twice.  */
+#ifndef GLOB_COMPAT_BUILD
+static bool
+size_add_wrapv (size_t a, size_t b, size_t *r)
+{
+#if 5 <= __GNUC__
+  return __builtin_add_overflow (a, b, r);
+#else
+  *r = a + b;
+  return *r < a;
+#endif
+}
+
+static bool
+glob_use_alloca (size_t alloca_used, size_t len)
+{
+  size_t size;
+  return (!size_add_wrapv (alloca_used, len, &size)
+	  && __libc_use_alloca (size));
+}
+#endif
+
 static int glob_in_dir (const char *pattern, const char *directory,
 			int flags, int (*errfunc) (const char *, int),
 			glob_t *pglob, size_t alloca_used);
 extern int __glob_pattern_type (const char *pattern, int quote)
     attribute_hidden;
 
-#if !defined _LIBC || !defined GLOB_ONLY_P
+#ifndef GLOB_ONLY_P
 static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL;
 static int collated_compare (const void *, const void *) __THROWNL;
 
@@ -265,16 +308,16 @@ next_brace_sub (const char *cp, int flags)
   return *cp != '\0' ? cp : NULL;
 }
 
-#endif /* !defined _LIBC || !defined GLOB_ONLY_P */
+#endif /* !defined GLOB_ONLY_P */
 
 /* Do glob searching for PATTERN, placing results in PGLOB.
    The bits defined above may be set in FLAGS.
    If a directory cannot be opened or read and ERRFUNC is not nil,
    it is called with the pathname that caused the error, and the
-   `errno' value from the failing call; if it returns non-zero
-   `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
+   'errno' value from the failing call; if it returns non-zero
+   'glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
    If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
-   Otherwise, `glob' returns zero.  */
+   Otherwise, 'glob' returns zero.  */
 int
 #ifdef GLOB_ATTRIBUTE
 GLOB_ATTRIBUTE
@@ -292,9 +335,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
   int malloc_dirname = 0;
   glob_t dirs;
   int retval = 0;
-#ifdef _LIBC
   size_t alloca_used = 0;
-#endif
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
     {
@@ -308,7 +349,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
     flags |= GLOB_ONLYDIR;
 
   if (!(flags & GLOB_DOOFFS))
-    /* Have to do this so `globfree' knows where to start freeing.  It
+    /* Have to do this so 'globfree' knows where to start freeing.  It
        also makes all the code that uses gl_offs simpler. */
     pglob->gl_offs = 0;
 
@@ -363,7 +404,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       if (begin != NULL)
 	{
 	  /* Allocate working buffer large enough for our work.  Note that
-	    we have at least an opening and closing brace.  */
+	     we have at least an opening and closing brace.  */
 	  size_t firstc;
 	  char *alt_start;
 	  const char *p;
@@ -372,16 +413,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  size_t rest_len;
 	  char *onealt;
 	  size_t pattern_len = strlen (pattern) - 1;
-#ifdef _LIBC
-	  int alloca_onealt = __libc_use_alloca (alloca_used + pattern_len);
+	  int alloca_onealt = glob_use_alloca (alloca_used, pattern_len);
 	  if (alloca_onealt)
 	    onealt = alloca_account (pattern_len, alloca_used);
 	  else
-#endif
 	    {
-	      onealt = (char *) malloc (pattern_len);
+	      onealt = malloc (pattern_len);
 	      if (onealt == NULL)
-		return GLOB_NOSPACE;
+		{
+		  if (!(flags & GLOB_APPEND))
+		    {
+		      pglob->gl_pathc = 0;
+		      pglob->gl_pathv = NULL;
+		    }
+		  return GLOB_NOSPACE;
+		}
 	    }
 
 	  /* We know the prefix for all sub-patterns.  */
@@ -392,14 +438,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  next = next_brace_sub (begin + 1, flags);
 	  if (next == NULL)
 	    {
-	      /* It is an illegal expression.  */
+	      /* It is an invalid expression.  */
 	    illegal_brace:
-#ifdef _LIBC
 	      if (__glibc_unlikely (!alloca_onealt))
-#endif
 		free (onealt);
-	      flags &= ~GLOB_BRACE;
-	      goto no_brace;
+	      return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
 	    }
 
 	  /* Now find the end of the whole brace expression.  */
@@ -437,9 +480,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      /* If we got an error, return it.  */
 	      if (result && result != GLOB_NOMATCH)
 		{
-#ifdef _LIBC
 		  if (__glibc_unlikely (!alloca_onealt))
-#endif
 		    free (onealt);
 		  if (!(flags & GLOB_APPEND))
 		    {
@@ -458,9 +499,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      assert (next != NULL);
 	    }
 
-#ifdef _LIBC
 	  if (__glibc_unlikely (!alloca_onealt))
-#endif
 	    free (onealt);
 
 	  if (pglob->gl_pathc != firstc)
@@ -471,7 +510,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	}
     }
 
- no_brace:
   oldcount = pglob->gl_pathc + pglob->gl_offs;
 
   /* Find the filename.  */
@@ -536,22 +574,20 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  char *drive_spec;
 
 	  ++dirlen;
-	  drive_spec = (char *) __alloca (dirlen + 1);
+	  drive_spec = __alloca (dirlen + 1);
 	  *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
 	  /* For now, disallow wildcards in the drive spec, to
 	     prevent infinite recursion in glob.  */
 	  if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
 	    return GLOB_NOMATCH;
-	  /* If this is "d:pattern", we need to copy `:' to DIRNAME
+	  /* If this is "d:pattern", we need to copy ':' to DIRNAME
 	     as well.  If it's "d:/pattern", don't remove the slash
 	     from "d:/", since "d:" and "d:/" are not the same.*/
 	}
 #endif
-#ifdef _LIBC
-      if (__libc_use_alloca (alloca_used + dirlen + 1))
+      if (glob_use_alloca (alloca_used, dirlen + 1))
 	newp = alloca_account (dirlen + 1, alloca_used);
       else
-#endif
 	{
 	  newp = malloc (dirlen + 1);
 	  if (newp == NULL)
@@ -572,6 +608,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	/* "pattern/".  Expand "pattern", appending slashes.  */
 	{
 	  int orig_flags = flags;
+          int val;
 	  if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\')
 	    {
 	      /* "pattern\\/".  Remove the final backslash if it hasn't
@@ -585,7 +622,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
 		}
 	    }
-	  int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
+	  val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
 	  if (val == 0)
 	    pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
 			       | (flags & GLOB_MARK));
@@ -602,7 +639,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	}
     }
 
-#ifndef VMS
   if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
     {
       if (dirname[1] == '\0' || dirname[1] == '/'
@@ -617,95 +653,129 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    home_dir = "SYS:";
 # else
 #  ifdef WINDOWS32
+	  /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
+	     to HOME, because the user can change HOME.  */
 	  if (home_dir == NULL || home_dir[0] == '\0')
-	    home_dir = "c:/users/default"; /* poor default */
+	    {
+	      const char *home_drive = getenv ("HOMEDRIVE");
+	      const char *home_path = getenv ("HOMEPATH");
+
+	      if (home_drive != NULL && home_path != NULL)
+		{
+		  size_t home_drive_len = strlen (home_drive);
+		  size_t home_path_len = strlen (home_path);
+		  char *mem = alloca (home_drive_len + home_path_len + 1);
+
+		  memcpy (mem, home_drive, home_drive_len);
+		  memcpy (mem + home_drive_len, home_path, home_path_len + 1);
+		  home_dir = mem;
+		}
+	      else
+		home_dir = "c:/users/default"; /* poor default */
+	    }
 #  else
 	  if (home_dir == NULL || home_dir[0] == '\0')
 	    {
 	      int success;
 	      char *name;
+	      int malloc_name = 0;
 	      size_t buflen = GET_LOGIN_NAME_MAX () + 1;
 
 	      if (buflen == 0)
 		/* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
 		   a moderate value.  */
 		buflen = 20;
-	      name = alloca_account (buflen, alloca_used);
+	      if (glob_use_alloca (alloca_used, buflen))
+		name = alloca_account (buflen, alloca_used);
+	      else
+		{
+		  name = malloc (buflen);
+		  if (name == NULL)
+		    {
+		      retval = GLOB_NOSPACE;
+		      goto out;
+		    }
+		  malloc_name = 1;
+		}
 
 	      success = __getlogin_r (name, buflen) == 0;
 	      if (success)
 		{
 		  struct passwd *p;
-#   if defined HAVE_GETPWNAM_R || defined _LIBC
-		  long int pwbuflen = GETPW_R_SIZE_MAX ();
+		  char *malloc_pwtmpbuf = NULL;
 		  char *pwtmpbuf;
+#   if defined HAVE_GETPWNAM_R || defined _LIBC
+		  long int pwbuflenmax = GETPW_R_SIZE_MAX ();
+		  size_t pwbuflen = pwbuflenmax;
 		  struct passwd pwbuf;
-		  int malloc_pwtmpbuf = 0;
 		  int save = errno;
 
 #    ifndef _LIBC
-		  if (pwbuflen == -1)
+                  if (! (0 < pwbuflenmax && pwbuflenmax <= SIZE_MAX))
 		    /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.
 		       Try a moderate value.  */
 		    pwbuflen = 1024;
 #    endif
-		  if (__libc_use_alloca (alloca_used + pwbuflen))
+		  if (glob_use_alloca (alloca_used, pwbuflen))
 		    pwtmpbuf = alloca_account (pwbuflen, alloca_used);
 		  else
 		    {
 		      pwtmpbuf = malloc (pwbuflen);
 		      if (pwtmpbuf == NULL)
 			{
+			  if (__glibc_unlikely (malloc_name))
+			    free (name);
 			  retval = GLOB_NOSPACE;
 			  goto out;
 			}
-		      malloc_pwtmpbuf = 1;
+		      malloc_pwtmpbuf = pwtmpbuf;
 		    }
 
 		  while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
 			 != 0)
 		    {
+		      size_t newlen;
+		      bool v;
 		      if (errno != ERANGE)
 			{
 			  p = NULL;
 			  break;
 			}
-
-		      if (!malloc_pwtmpbuf
-			  && __libc_use_alloca (alloca_used
-						+ 2 * pwbuflen))
+		      v = size_add_wrapv (pwbuflen, pwbuflen, &newlen);
+		      if (!v && malloc_pwtmpbuf == NULL
+			  && glob_use_alloca (alloca_used, newlen))
 			pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen,
-							  2 * pwbuflen,
-							  alloca_used);
+							  newlen, alloca_used);
 		      else
 			{
-			  char *newp = realloc (malloc_pwtmpbuf
-						? pwtmpbuf : NULL,
-						2 * pwbuflen);
+			  char *newp = (v ? NULL
+					: realloc (malloc_pwtmpbuf, newlen));
 			  if (newp == NULL)
 			    {
-			      if (__glibc_unlikely (malloc_pwtmpbuf))
-				free (pwtmpbuf);
+			      free (malloc_pwtmpbuf);
+			      if (__glibc_unlikely (malloc_name))
+				free (name);
 			      retval = GLOB_NOSPACE;
 			      goto out;
 			    }
-			  pwtmpbuf = newp;
-			  pwbuflen = 2 * pwbuflen;
-			  malloc_pwtmpbuf = 1;
+			  malloc_pwtmpbuf = pwtmpbuf = newp;
 			}
+		      pwbuflen = newlen;
 		      __set_errno (save);
 		    }
 #   else
 		  p = getpwnam (name);
 #   endif
+		  if (__glibc_unlikely (malloc_name))
+		    free (name);
 		  if (p != NULL)
 		    {
-		      if (!malloc_pwtmpbuf)
+		      if (malloc_pwtmpbuf == NULL)
 			home_dir = p->pw_dir;
 		      else
 			{
 			  size_t home_dir_len = strlen (p->pw_dir) + 1;
-			  if (__libc_use_alloca (alloca_used + home_dir_len))
+			  if (glob_use_alloca (alloca_used, home_dir_len))
 			    home_dir = alloca_account (home_dir_len,
 						       alloca_used);
 			  else
@@ -720,23 +790,30 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 			      malloc_home_dir = 1;
 			    }
 			  memcpy (home_dir, p->pw_dir, home_dir_len);
-
-			  free (pwtmpbuf);
 			}
 		    }
+		  free (malloc_pwtmpbuf);
+		}
+	      else
+		{
+		  if (__glibc_unlikely (malloc_name))
+		    free (name);
 		}
 	    }
 	  if (home_dir == NULL || home_dir[0] == '\0')
 	    {
+	      if (__glibc_unlikely (malloc_home_dir))
+		free (home_dir);
 	      if (flags & GLOB_TILDE_CHECK)
 		{
-		  if (__glibc_unlikely (malloc_home_dir))
-		    free (home_dir);
 		  retval = GLOB_NOMATCH;
 		  goto out;
 		}
 	      else
-		home_dir = (char *) "~"; /* No luck.  */
+		{
+		  home_dir = (char *) "~"; /* No luck.  */
+		  malloc_home_dir = 0;
+		}
 	    }
 #  endif /* WINDOWS32 */
 # endif
@@ -754,8 +831,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    {
 	      char *newp;
 	      size_t home_len = strlen (home_dir);
-	      int use_alloca = __libc_use_alloca (alloca_used
-						  + home_len + dirlen);
+	      int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen);
 	      if (use_alloca)
 		newp = alloca_account (home_len + dirlen, alloca_used);
 	      else
@@ -779,6 +855,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      dirname = newp;
 	      dirlen += home_len - 1;
 	      malloc_dirname = !use_alloca;
+
+	      if (__glibc_unlikely (malloc_home_dir))
+		free (home_dir);
 	    }
 	  dirname_modified = 1;
 	}
@@ -806,7 +885,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  else
 	    {
 	      char *newp;
-	      if (__libc_use_alloca (alloca_used + (end_name - dirname)))
+	      if (glob_use_alloca (alloca_used, end_name - dirname))
 		newp = alloca_account (end_name - dirname, alloca_used);
 	      else
 		{
@@ -851,20 +930,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* Look up specific user's home directory.  */
 	  {
 	    struct passwd *p;
+	    char *malloc_pwtmpbuf = NULL;
 #  if defined HAVE_GETPWNAM_R || defined _LIBC
-	    long int buflen = GETPW_R_SIZE_MAX ();
+	    long int buflenmax = GETPW_R_SIZE_MAX ();
+	    size_t buflen = buflenmax;
 	    char *pwtmpbuf;
-	    int malloc_pwtmpbuf = 0;
 	    struct passwd pwbuf;
 	    int save = errno;
 
 #   ifndef _LIBC
-	    if (buflen == -1)
-	      /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
+	    if (! (0 <= buflenmax && buflenmax <= SIZE_MAX))
+	      /* Perhaps 'sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
 		 moderate value.  */
 	      buflen = 1024;
 #   endif
-	    if (__libc_use_alloca (alloca_used + buflen))
+	    if (glob_use_alloca (alloca_used, buflen))
 	      pwtmpbuf = alloca_account (buflen, alloca_used);
 	    else
 	      {
@@ -877,32 +957,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		    retval = GLOB_NOSPACE;
 		    goto out;
 		  }
-		malloc_pwtmpbuf = 1;
+		malloc_pwtmpbuf = pwtmpbuf;
 	      }
 
 	    while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0)
 	      {
+		size_t newlen;
+		bool v;
 		if (errno != ERANGE)
 		  {
 		    p = NULL;
 		    break;
 		  }
-		if (!malloc_pwtmpbuf
-		    && __libc_use_alloca (alloca_used + 2 * buflen))
+		v = size_add_wrapv (buflen, buflen, &newlen);
+		if (!v && malloc_pwtmpbuf == NULL
+		    && glob_use_alloca (alloca_used, newlen))
 		  pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen,
-						    2 * buflen, alloca_used);
+						    newlen, alloca_used);
 		else
 		  {
-		    char *newp = realloc (malloc_pwtmpbuf ? pwtmpbuf : NULL,
-					  2 * buflen);
+		    char *newp = v ? NULL : realloc (malloc_pwtmpbuf, newlen);
 		    if (newp == NULL)
 		      {
-			if (__glibc_unlikely (malloc_pwtmpbuf))
-			  free (pwtmpbuf);
+			free (malloc_pwtmpbuf);
 			goto nomem_getpw;
 		      }
-		    pwtmpbuf = newp;
-		    malloc_pwtmpbuf = 1;
+		    malloc_pwtmpbuf = pwtmpbuf = newp;
 		  }
 		__set_errno (save);
 	      }
@@ -923,7 +1003,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  free (dirname);
 		malloc_dirname = 0;
 
-		if (__libc_use_alloca (alloca_used + home_len + rest_len + 1))
+		if (glob_use_alloca (alloca_used, home_len + rest_len + 1))
 		  dirname = alloca_account (home_len + rest_len + 1,
 					    alloca_used);
 		else
@@ -931,8 +1011,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		    dirname = malloc (home_len + rest_len + 1);
 		    if (dirname == NULL)
 		      {
-			if (__glibc_unlikely (malloc_pwtmpbuf))
-			  free (pwtmpbuf);
+			free (malloc_pwtmpbuf);
 			retval = GLOB_NOSPACE;
 			goto out;
 		      }
@@ -944,24 +1023,24 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		dirlen = home_len + rest_len;
 		dirname_modified = 1;
 
-		if (__glibc_unlikely (malloc_pwtmpbuf))
-		  free (pwtmpbuf);
+		free (malloc_pwtmpbuf);
 	      }
 	    else
 	      {
-		if (__glibc_unlikely (malloc_pwtmpbuf))
-		  free (pwtmpbuf);
+		free (malloc_pwtmpbuf);
 
 		if (flags & GLOB_TILDE_CHECK)
+		  {
 		  /* We have to regard it as an error if we cannot find the
 		     home directory.  */
-		  return GLOB_NOMATCH;
+		    retval = GLOB_NOMATCH;
+		    goto out;
+		  }
 	      }
 	  }
 	}
 # endif	/* Not Amiga && not WINDOWS32.  */
     }
-#endif	/* Not VMS.  */
 
   /* Now test whether we looked for "~" or "~NAME".  In this case we
      can give the answer now.  */
@@ -980,19 +1059,18 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  size_t newcount = pglob->gl_pathc + pglob->gl_offs;
 	  char **new_gl_pathv;
 
-	  if (newcount > UINTPTR_MAX - (1 + 1)
-	      || newcount + 1 + 1 > ~((size_t) 0) / sizeof (char *))
+          if (newcount > SIZE_MAX / sizeof (char *) - 2)
 	    {
 	    nospace:
 	      free (pglob->gl_pathv);
 	      pglob->gl_pathv = NULL;
 	      pglob->gl_pathc = 0;
-	      return GLOB_NOSPACE;
+	      retval = GLOB_NOSPACE;
+	      goto out;
 	    }
 
-	  new_gl_pathv
-	    = (char **) realloc (pglob->gl_pathv,
-				 (newcount + 1 + 1) * sizeof (char *));
+	  new_gl_pathv = realloc (pglob->gl_pathv,
+				  (newcount + 2) * sizeof (char *));
 	  if (new_gl_pathv == NULL)
 	    goto nospace;
 	  pglob->gl_pathv = new_gl_pathv;
@@ -1006,12 +1084,19 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
 	      p[0] = '/';
 	      p[1] = '\0';
+	      if (__glibc_unlikely (malloc_dirname))
+		free (dirname);
 	    }
 	  else
 	    {
-	      pglob->gl_pathv[newcount] = strdup (dirname);
-	      if (pglob->gl_pathv[newcount] == NULL)
-		goto nospace;
+	      if (__glibc_unlikely (malloc_dirname))
+		pglob->gl_pathv[newcount] = dirname;
+	      else
+		{
+		  pglob->gl_pathv[newcount] = strdup (dirname);
+		  if (pglob->gl_pathv[newcount] == NULL)
+		    goto nospace;
+		}
 	    }
 	  pglob->gl_pathv[++newcount] = NULL;
 	  ++pglob->gl_pathc;
@@ -1021,7 +1106,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	}
 
       /* Not found.  */
-      return GLOB_NOMATCH;
+      retval = GLOB_NOMATCH;
+      goto out;
     }
 
   meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
@@ -1067,7 +1153,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       if (status != 0)
 	{
 	  if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH)
-	    return status;
+	    {
+	      retval = status;
+	      goto out;
+	    }
 	  goto no_matches;
 	}
 
@@ -1078,7 +1167,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	{
 	  size_t old_pathc;
 
-#ifdef	SHELL
+#ifdef SHELL
 	  {
 	    /* Make globbing interruptible in the bash shell. */
 	    extern int interrupt_state;
@@ -1086,7 +1175,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    if (interrupt_state)
 	      {
 		globfree (&dirs);
-		return GLOB_ABORTED;
+		retval = GLOB_ABORTED;
+		goto out;
 	      }
 	  }
 #endif /* SHELL.  */
@@ -1105,7 +1195,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      globfree (&dirs);
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      return status;
+	      retval = status;
+	      goto out;
 	    }
 
 	  /* Stick the directory on the front of each name.  */
@@ -1116,7 +1207,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      globfree (&dirs);
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      return GLOB_NOSPACE;
+	      retval = GLOB_NOSPACE;
+	      goto out;
 	    }
 	}
 
@@ -1134,28 +1226,28 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      size_t newcount = pglob->gl_pathc + pglob->gl_offs;
 	      char **new_gl_pathv;
 
-	      if (newcount > UINTPTR_MAX - 2
-		  || newcount + 2 > ~((size_t) 0) / sizeof (char *))
+	      if (newcount > SIZE_MAX / sizeof (char *) - 2)
 		{
 		nospace2:
 		  globfree (&dirs);
-		  return GLOB_NOSPACE;
+		  retval = GLOB_NOSPACE;
+		  goto out;
 		}
 
-	      new_gl_pathv = (char **) realloc (pglob->gl_pathv,
-						(newcount + 2)
-						* sizeof (char *));
+	      new_gl_pathv = realloc (pglob->gl_pathv,
+				      (newcount + 2) * sizeof (char *));
 	      if (new_gl_pathv == NULL)
 		goto nospace2;
 	      pglob->gl_pathv = new_gl_pathv;
 
-	      pglob->gl_pathv[newcount] = __strdup (pattern);
+	      pglob->gl_pathv[newcount] = strdup (pattern);
 	      if (pglob->gl_pathv[newcount] == NULL)
 		{
 		  globfree (&dirs);
 		  globfree (pglob);
 		  pglob->gl_pathc = 0;
-		  return GLOB_NOSPACE;
+		  retval = GLOB_NOSPACE;
+		  goto out;
 		}
 
 	      ++pglob->gl_pathc;
@@ -1167,7 +1259,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  else
 	    {
 	      globfree (&dirs);
-	      return GLOB_NOMATCH;
+	      retval = GLOB_NOMATCH;
+	      goto out;
 	    }
 	}
 
@@ -1213,7 +1306,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      flags = orig_flags;
 	      goto no_matches;
 	    }
-	  return status;
+	  retval = status;
+	  goto out;
 	}
 
       if (dirlen > 0)
@@ -1225,7 +1319,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    {
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      return GLOB_NOSPACE;
+	      retval = GLOB_NOSPACE;
+	      goto out;
 	    }
 	}
     }
@@ -1250,7 +1345,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      {
 		globfree (pglob);
 		pglob->gl_pathc = 0;
-		return GLOB_NOSPACE;
+		retval = GLOB_NOSPACE;
+		goto out;
 	      }
 	    strcpy (&new[len - 2], "/");
 	    pglob->gl_pathv[i] = new;
@@ -1276,7 +1372,7 @@ libc_hidden_def (glob)
 #endif
 
 
-#if !defined _LIBC || !defined GLOB_ONLY_P
+#ifndef GLOB_ONLY_P
 
 /* Free storage allocated in PGLOB by a previous `glob' call.  */
 void
@@ -1300,8 +1396,8 @@ libc_hidden_def (globfree)
 static int
 collated_compare (const void *a, const void *b)
 {
-  const char *const s1 = *(const char *const * const) a;
-  const char *const s2 = *(const char *const * const) b;
+  char *const *ps1 = a; char *s1 = *ps1;
+  char *const *ps2 = b; char *s2 = *ps2;
 
   if (s1 == s2)
     return 0;
@@ -1351,7 +1447,7 @@ prefix_array (const char *dirname, char **array, size_t n)
   for (i = 0; i < n; ++i)
     {
       size_t eltlen = strlen (array[i]) + 1;
-      char *new = (char *) malloc (dirlen + 1 + eltlen);
+      char *new = malloc (dirlen + 1 + eltlen);
       if (new == NULL)
 	{
 	  while (i > 0)
@@ -1373,7 +1469,7 @@ prefix_array (const char *dirname, char **array, size_t n)
 
 
 /* We must not compile this function twice.  */
-#if !defined _LIBC || !defined NO_GLOB_PATTERN_P
+#ifndef NO_GLOB_PATTERN_P
 int
 __glob_pattern_type (const char *pattern, int quote)
 {
@@ -1421,50 +1517,57 @@ weak_alias (__glob_pattern_p, glob_pattern_p)
 # endif
 #endif
 
-#endif /* !GLOB_ONLY_P */
-
 
 /* We put this in a separate function mainly to allow the memory
    allocated with alloca to be recycled.  */
-#if !defined _LIBC || !defined GLOB_ONLY_P
 static int
 __attribute_noinline__
-link_exists2_p (const char *dir, size_t dirlen, const char *fname,
+link_stat (const char *dir, size_t dirlen, const char *fname,
 	       glob_t *pglob
-# ifndef _LIBC
+# if !defined _LIBC && !HAVE_FSTATAT
 		, int flags
 # endif
 		)
 {
   size_t fnamelen = strlen (fname);
-  char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1);
+  char *fullname = __alloca (dirlen + 1 + fnamelen + 1);
   struct stat st;
-# ifndef _LIBC
-  struct_stat64 st64;
-# endif
 
   mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1),
 	   fname, fnamelen + 1);
 
-# ifdef _LIBC
-  return (*pglob->gl_stat) (fullname, &st) == 0;
-# else
-  return ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-	   ? (*pglob->gl_stat) (fullname, &st)
-	   : __stat64 (fullname, &st64)) == 0);
+# if !defined _LIBC && !HAVE_FSTATAT
+  if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) == 0, 1))
+    {
+      struct_stat64 st64;
+      return __stat64 (fullname, &st64);
+    }
 # endif
+  return (*pglob->gl_stat) (fullname, &st);
 }
-# ifdef _LIBC
-#  define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \
-  (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)			      \
-   ? link_exists2_p (dirname, dirnamelen, fname, pglob)			      \
-   : ({ struct stat64 st64;						      \
-       __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; }))
+
+/* Return true if DIR/FNAME exists.  */
+static int
+link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname,
+               glob_t *pglob, int flags)
+{
+  int status;
+# if defined _LIBC || HAVE_FSTATAT
+  if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
+    status = link_stat (dir, dirlen, fname, pglob);
+  else
+    {
+      /* dfd cannot be -1 here, because dirfd never returns -1 on
+         glibc, or on hosts that have fstatat.  */
+      struct_stat64 st64;
+      status = __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0);
+    }
 # else
-#  define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \
-  link_exists2_p (dirname, dirnamelen, fname, pglob, flags)
+  status = link_stat (dir, dirlen, fname, pglob, flags);
 # endif
-#endif
+  return status == 0 || errno == EOVERFLOW;
+}
+#endif /* !defined GLOB_ONLY_P */
 
 
 /* Like `glob', but PATTERN is a final pathname component,
@@ -1492,6 +1595,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
   size_t cur = 0;
   int meta;
   int save;
+  int result;
 
   alloca_used += sizeof (init_names);
 
@@ -1516,14 +1620,16 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	struct_stat64 st64;
       } ust;
       size_t patlen = strlen (pattern);
-      int alloca_fullname = __libc_use_alloca (alloca_used
-					       + dirlen + 1 + patlen + 1);
+      size_t fullsize;
+      bool alloca_fullname
+	= (! size_add_wrapv (dirlen + 1, patlen + 1, &fullsize)
+	   && glob_use_alloca (alloca_used, fullsize));
       char *fullname;
       if (alloca_fullname)
-	fullname = alloca_account (dirlen + 1 + patlen + 1, alloca_used);
+	fullname = alloca_account (fullsize, alloca_used);
       else
 	{
-	  fullname = malloc (dirlen + 1 + patlen + 1);
+	  fullname = malloc (fullsize);
 	  if (fullname == NULL)
 	    return GLOB_NOSPACE;
 	}
@@ -1531,9 +1637,11 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
       mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
 			"/", 1),
 	       pattern, patlen + 1);
-      if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+      if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
 	   ? (*pglob->gl_stat) (fullname, &ust.st)
-	   : __stat64 (fullname, &ust.st64)) == 0)
+	   : __stat64 (fullname, &ust.st64))
+	  == 0)
+	  || errno == EOVERFLOW)
 	/* We found this file to be existing.  Now tell the rest
 	   of the function to copy this name into the result.  */
 	flags |= GLOB_NOCHECK;
@@ -1555,10 +1663,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	}
       else
 	{
-#ifdef _LIBC
 	  int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
 		     ? -1 : dirfd ((DIR *) stream));
-#endif
 	  int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
 			   | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
 #if defined _AMIGA || defined VMS
@@ -1607,7 +1713,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 			  size_t size = (sizeof (struct globnames)
 					 + ((count - INITIAL_COUNT)
 					    * sizeof (char *)));
-			  if (__libc_use_alloca (alloca_used + size))
+			  if (glob_use_alloca (alloca_used, size))
 			    newnames = names_alloca
 			      = alloca_account (size, alloca_used);
 			  else if ((newnames = malloc (size))
@@ -1623,6 +1729,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 			goto memory_error;
 		      ++cur;
 		      ++nfound;
+		      if (SIZE_MAX - pglob->gl_offs <= nfound)
+			goto memory_error;
 		    }
 		}
 	    }
@@ -1633,36 +1741,35 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
     {
       size_t len = strlen (pattern);
       nfound = 1;
-      names->name[cur] = (char *) malloc (len + 1);
+      names->name[cur] = malloc (len + 1);
       if (names->name[cur] == NULL)
 	goto memory_error;
       *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0';
     }
 
-  int result = GLOB_NOMATCH;
+  result = GLOB_NOMATCH;
   if (nfound != 0)
     {
+      char **new_gl_pathv;
       result = 0;
 
-      if (pglob->gl_pathc > UINTPTR_MAX - pglob->gl_offs
-	  || pglob->gl_pathc + pglob->gl_offs > UINTPTR_MAX - nfound
-	  || pglob->gl_pathc + pglob->gl_offs + nfound > UINTPTR_MAX - 1
-	  || (pglob->gl_pathc + pglob->gl_offs + nfound + 1
-	      > UINTPTR_MAX / sizeof (char *)))
+      if (SIZE_MAX / sizeof (char *) - pglob->gl_pathc
+          < pglob->gl_offs + nfound + 1)
 	goto memory_error;
 
-      char **new_gl_pathv;
       new_gl_pathv
-	= (char **) realloc (pglob->gl_pathv,
-			     (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
-			     * sizeof (char *));
+	= realloc (pglob->gl_pathv,
+		   (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
+		    * sizeof (char *));
+
       if (new_gl_pathv == NULL)
 	{
 	memory_error:
 	  while (1)
 	    {
 	      struct globnames *old = names;
-	      for (size_t i = 0; i < cur; ++i)
+	      size_t i;
+	      for (i = 0; i < cur; ++i)
 		free (names->name[i]);
 	      names = names->next;
 	      /* NB: we will not leak memory here if we exit without
@@ -1687,7 +1794,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	  while (1)
 	    {
 	      struct globnames *old = names;
-	      for (size_t i = 0; i < cur; ++i)
+	      size_t i;
+	      for (i = 0; i < cur; ++i)
 		new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++]
 		  = names->name[i];
 	      names = names->next;
diff --git a/posix/globtest.c b/posix/globtest.c
index 878ae33..7ffcb91 100644
--- a/posix/globtest.c
+++ b/posix/globtest.c
@@ -114,5 +114,8 @@ main (int argc, char *argv[])
 		g.gl_pathv[i] ? g.gl_pathv[i] : "(null)",
 		quotes ? "'" : "");
     }
+
+  globfree (&g);
+
   return 0;
 }
diff --git a/sysdeps/unix/sysv/linux/i386/glob64.c b/sysdeps/unix/sysv/linux/i386/glob64.c
index f681951..9c7abd8 100644
--- a/sysdeps/unix/sysv/linux/i386/glob64.c
+++ b/sysdeps/unix/sysv/linux/i386/glob64.c
@@ -70,6 +70,8 @@ int __old_glob64 (const char *__pattern, int __flags,
 
 #define GLOB_ONLY_P 1
 
+#define GLOB_COMPAT_BUILD 1
+
 #include <posix/glob.c>
 
 compat_symbol (libc, __old_glob64, glob64, GLIBC_2_1);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 17/18] posix: Use enum for __glob_pattern_type result
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (15 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 09/18] posix: Use char_array for internal glob dirname Adhemerval Zanella
  2017-08-17 10:19 ` [PATCH v2 00/18] posix: glob fixes and refactor Florian Weimer
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch replaces the internal integer constant from
__glob_pattern_type return with a proper enum.

Checked on x86_64-linux-gnu.

	* posix/glob_internal.h (__glob_pat_types): New enumeration.
	(__glob_pattern_type): Use __glob_pat_types.
	* posix/glob_pattern_p.c (__glob_pattern_p): Likewise.
	* posix/glob.c (glob): Likewise.
	(glob_in_dir): Likewise.
---
 posix/glob.c           |  8 ++++----
 posix/glob_internal.h  | 18 +++++++++++++-----
 posix/glob_pattern_p.c |  2 +-
 3 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 647334d..37e8702 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -906,7 +906,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
      [ which we handle the same, using fnmatch.  Broken unterminated
      pattern bracket expressions ought to be rare enough that it is
      not worth special casing them, fnmatch will do the right thing.  */
-  if (meta & 5)
+  if (meta & (__glob_special | __glob_bracket))
     {
       /* The directory name contains metacharacters, so we
 	 have to glob for the directory, and then glob for
@@ -1059,7 +1059,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       size_t old_pathc = pglob->gl_pathc;
       int orig_flags = flags;
 
-      if (meta & 2)
+      if (meta & __glob_backslash)
 	{
 	  char *p = strchr (char_array_str (&dirname), '\\'), *q;
 	  /* We need to unescape the dirname string.  It is certainly
@@ -1272,14 +1272,14 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
   globnames_array_init (&globnames);
 
   meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE));
-  if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+  if (meta == __glob_none && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
     {
       /* We need not do any tests.  The PATTERN contains no meta
 	 characters and we must not return an error therefore the
 	 result will always contain exactly one name.  */
       flags |= GLOB_NOCHECK;
     }
-  else if (meta == 0)
+  else if (meta == __glob_none)
     {
       /* Since we use the normal file functions we can also use stat()
 	 to verify the file is there.  */
diff --git a/posix/glob_internal.h b/posix/glob_internal.h
index d989a98..54143c2 100644
--- a/posix/glob_internal.h
+++ b/posix/glob_internal.h
@@ -19,35 +19,43 @@
 #ifndef GLOB_INTERNAL_H
 # define GLOB_INTERNAL_H
 
+enum __glob_pat_types
+{
+  __glob_none      = 0x0,
+  __glob_special   = 0x1,
+  __glob_backslash = 0x2,
+  __glob_bracket   = 0x4 
+};
+
 static inline int
 __glob_pattern_type (const char *pattern, int quote)
 {
   const char *p;
-  int ret = 0;
+  int ret = __glob_none;
 
   for (p = pattern; *p != '\0'; ++p)
     switch (*p)
       {
       case '?':
       case '*':
-	return 1;
+	return __glob_special;
 
       case '\\':
 	if (quote)
 	  {
 	    if (p[1] != '\0')
 	      ++p;
-	    ret |= 2;
+	    ret |= __glob_backslash;
 	  }
 	break;
 
       case '[':
-	ret |= 4;
+	ret |= __glob_bracket;
 	break;
 
       case ']':
 	if (ret & 4)
-	  return 1;
+	  return __glob_special;
 	break;
       }
 
diff --git a/posix/glob_pattern_p.c b/posix/glob_pattern_p.c
index 6e451f2..61caf37 100644
--- a/posix/glob_pattern_p.c
+++ b/posix/glob_pattern_p.c
@@ -24,6 +24,6 @@
 int
 __glob_pattern_p (const char *pattern, int quote)
 {
-  return __glob_pattern_type (pattern, quote) == 1;
+  return __glob_pattern_type (pattern, quote) == __glob_special;
 }
 weak_alias (__glob_pattern_p, glob_pattern_p)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 02/18] posix: Adjust glob tests to libsupport
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (13 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 10/18] posix: Remove alloca usage for GLOB_BRACE on glob Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage Adhemerval Zanella
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch adjust glob tests to use libsupport.  It also refactor some
tests to move to a more meaningful file name and to gather similar tests
in a common file:

  * move bug-glob3.c tests to tst-glob_basic.c.
  * move bug-glob2.c tests to tst-glob_memory.c
  * move common definitions to tst-glob_common.c.

Checked on x86_64-linux-gnu.

	* posix/Makefile (tests): Remove bug-glob2 and bug-glob3.  Add
	tst-glob_basic and tst-glob_memory.
	* posix/bug-glob3.c: Move to ...
	* posix/tst-glob_basic.c: ... here.
	* posix/bug-glob2.c: Move to ...
	* posix/tst-glob_memory.c: ... here.
	* posix/globtest.c: Use libsupport.
	* posix/tst-gnuglob.c: Likewise.
	* posix/tst-glob_common.c: New file.
---
 posix/Makefile                           |  12 +--
 posix/bug-glob3.c                        |  45 ---------
 posix/globtest.c                         | 163 ++++++++++++++++++-------------
 posix/tst-glob_basic.c                   |  43 ++++++++
 posix/tst-glob_common.c                  | 103 +++++++++++++++++++
 posix/{bug-glob2.c => tst-glob_memory.c} | 100 +++----------------
 posix/tst-gnuglob.c                      | 109 +++------------------
 7 files changed, 272 insertions(+), 303 deletions(-)
 delete mode 100644 posix/bug-glob3.c
 create mode 100644 posix/tst-glob_basic.c
 create mode 100644 posix/tst-glob_common.c
 rename posix/{bug-glob2.c => tst-glob_memory.c} (76%)

diff --git a/posix/Makefile b/posix/Makefile
index 9b534f0..5ebbc4e 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -79,7 +79,7 @@ tests		:= test-errno tstgetopt testfnm runtests runptests \
 		   tst-nice tst-nanosleep tst-regex2 \
 		   transbug tst-rxspencer tst-pcre tst-boost \
 		   bug-ga1 tst-vfork1 tst-vfork2 tst-vfork3 tst-waitid \
-		   tst-getaddrinfo2 bug-glob1 bug-glob2 bug-glob3 tst-sysconf \
+		   tst-getaddrinfo2 tst-glob_memory tst-glob_basic tst-sysconf \
 		   tst-execvp1 tst-execvp2 tst-execlp1 tst-execlp2 \
 		   tst-execv1 tst-execv2 tst-execl1 tst-execl2 \
 		   tst-execve1 tst-execve2 tst-execle1 tst-execle2 \
@@ -122,7 +122,7 @@ generated += $(addprefix wordexp-test-result, 1 2 3 4 5 6 7 8 9 10) \
 	     tst-getconf.out \
 	     tst-pcre-mem.out tst-pcre.mtrace tst-boost-mem.out \
 	     tst-boost.mtrace bug-ga2.mtrace bug-ga2-mem.out \
-	     bug-glob2.mtrace bug-glob2-mem.out tst-vfork3-mem.out \
+	     tst-glob_memory.mtrace tst-glob_memory-mem.out tst-vfork3-mem.out \
 	     tst-vfork3.mtrace getconf.speclist tst-fnmatch-mem.out \
 	     tst-fnmatch.mtrace bug-regex36.mtrace
 
@@ -140,7 +140,7 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \
 		 $(objpfx)bug-regex21-mem.out $(objpfx)bug-regex31-mem.out \
 		 $(objpfx)tst-rxspencer-no-utf8-mem.out $(objpfx)tst-pcre-mem.out \
 		 $(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \
-		 $(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \
+		 $(objpfx)tst-glob_memory-mem.out $(objpfx)tst-vfork3-mem.out \
 		 $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out
 xtests-special += $(objpfx)bug-ga2-mem.out
 endif
@@ -344,10 +344,10 @@ $(objpfx)bug-ga2-mem.out: $(objpfx)bug-ga2.out
 
 bug-ga2-ENV = MALLOC_TRACE=$(objpfx)bug-ga2.mtrace
 
-bug-glob2-ENV = MALLOC_TRACE=$(objpfx)bug-glob2.mtrace
+tst-glob_memory-ENV = MALLOC_TRACE=$(objpfx)tst-glob_memory.mtrace
 
-$(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out
-	$(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \
+$(objpfx)tst-glob_memory-mem.out: $(objpfx)tst-glob_memory.out
+	$(common-objpfx)malloc/mtrace $(objpfx)tst-glob_memory.mtrace > $@; \
 	$(evaluate-test)
 
 $(inst_libexecdir)/getconf: $(inst_bindir)/getconf \
diff --git a/posix/bug-glob3.c b/posix/bug-glob3.c
deleted file mode 100644
index f2fbd70..0000000
--- a/posix/bug-glob3.c
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <glob.h>
-#include <stdio.h>
-#include <string.h>
-
-static int
-do_test (void)
-{
-  int result = 0;
-  glob_t g;
-  g.gl_pathc = 0;
-
-  int r = glob ("", 0, NULL, &g);
-  if (r != GLOB_NOMATCH)
-    {
-      puts ("glob (\"\", 0, NULL, &g) did not fail");
-      result = 1;
-    }
-  else if (g.gl_pathc != 0)
-    {
-      puts ("gl_pathc after glob (\"\", 0, NULL, &g) not zero");
-      result = 1;
-    }
-
-  r = glob ("", GLOB_NOCHECK, NULL, &g);
-  if (r != 0)
-    {
-      puts ("glob (\"\", GLOB_NOCHECK, NULL, &g) did fail");
-      result = 1;
-    }
-  else if (g.gl_pathc != 1)
-    {
-      puts ("gl_pathc after glob (\"\", GLOB_NOCHECK, NULL, &g) not 1");
-      result = 1;
-    }
-  else if (strcmp (g.gl_pathv[0], "") != 0)
-    {
-      puts ("gl_pathv[0] after glob (\"\", GLOB_NOCHECK, NULL, &g) not \"\"");
-      result = 1;
-    }
-
-  return result;
-}
-
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
diff --git a/posix/globtest.c b/posix/globtest.c
index 7ffcb91..e5b5891 100644
--- a/posix/globtest.c
+++ b/posix/globtest.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1997-2017 Free Software Foundation, Inc.
+/* Basic glob tests.  It uses an extenal driver script (tst-glob.sh).
+   Copyright (C) 1997-2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -21,77 +22,97 @@
 #include <unistd.h>
 #include <glob.h>
 
-int
-main (int argc, char *argv[])
+#include <support/check.h>
+
+#define OPT_BRACE		'b'
+#define OPT_NOCHECK		'c'
+#define OPT_ONLYDIR		'd'
+#define OPT_NOESCAPE		'e'
+#define OPT_ERR			'E'
+#define OPT_NOMAGIC		'g'
+#define OPT_MARK		'm'
+#define OPT_DOOFFS		'o'
+#define OPT_PERIOD		'p'
+#define OPT_QUOTES		'q'
+#define OPT_NOSORT		's'
+#define OPT_TILDE		't'
+#define OPT_TILDE_CHECK		'T'
+
+#define CMDLINE_OPTSTRING "bcdeEgmopqstT"
+
+static int glob_flags = 0;
+static int quotes = 1;
+
+static void
+cmdline_process_function (int c)
 {
-  int i, j;
-  int glob_flags = 0;
-  glob_t g;
-  int quotes = 1;
+  switch (c)
+    {
+    case OPT_BRACE:
+      glob_flags |= GLOB_BRACE;
+      break;
+    case OPT_NOCHECK:
+      glob_flags |= GLOB_NOCHECK;
+      break;
+    case OPT_ONLYDIR:
+      glob_flags |= GLOB_ONLYDIR;
+      break;
+    case OPT_NOESCAPE:
+      glob_flags |= GLOB_NOESCAPE;
+      break;
+    case OPT_ERR:
+      glob_flags |= GLOB_ERR;
+      break;
+    case OPT_NOMAGIC:
+      glob_flags |= GLOB_NOMAGIC;
+      break;
+    case OPT_MARK:
+      glob_flags |= GLOB_MARK;
+      break;
+    case OPT_DOOFFS:
+      glob_flags |= GLOB_DOOFFS;
+      break;
+    case OPT_PERIOD:
+      glob_flags |= GLOB_PERIOD;
+      break;
+    case OPT_QUOTES:
+      quotes = 0;
+      break;
+    case OPT_NOSORT:
+      glob_flags |= GLOB_NOSORT;
+      break;
+    case OPT_TILDE:
+      glob_flags |= GLOB_TILDE;
+      break;
+    case OPT_TILDE_CHECK:
+      glob_flags |= GLOB_TILDE_CHECK;
+      break;
+    }
+}
 
-  g.gl_offs = 0;
+#define CMDLINE_PROCESS	cmdline_process_function
 
-  while ((i = getopt (argc, argv, "bcdeEgmopqstT")) != -1)
-    switch(i)
-      {
-      case 'b':
-	glob_flags |= GLOB_BRACE;
-	break;
-      case 'c':
-	glob_flags |= GLOB_NOCHECK;
-	break;
-      case 'd':
-	glob_flags |= GLOB_ONLYDIR;
-	break;
-      case 'e':
-	glob_flags |= GLOB_NOESCAPE;
-	break;
-      case 'E':
-	glob_flags |= GLOB_ERR;
-	break;
-      case 'g':
-	glob_flags |= GLOB_NOMAGIC;
-	break;
-      case 'm':
-	glob_flags |= GLOB_MARK;
-	break;
-      case 'o':
-	glob_flags |= GLOB_DOOFFS;
-	g.gl_offs = 1;
-	break;
-      case 'p':
-	glob_flags |= GLOB_PERIOD;
-	break;
-      case 'q':
-	quotes = 0;
-	break;
-      case 's':
-	glob_flags |= GLOB_NOSORT;
-	break;
-      case 't':
-	glob_flags |= GLOB_TILDE;
-	break;
-      case 'T':
-	glob_flags |= GLOB_TILDE_CHECK;
-	break;
-      default:
-	exit (-1);
-      }
+static int
+do_test_argv (int argc, char *argv[])
+{
+  int i, j;
+  glob_t g;
 
-  if (optind >= argc || chdir (argv[optind]))
-    exit(1);
+  g.gl_offs = glob_flags & GLOB_DOOFFS ? 1 : 0;
 
-  j = optind + 1;
-  if (optind + 1 >= argc)
-    exit (1);
+  if (argc < 2)
+    FAIL_EXIT1 ("invalid arguments (expecting path for glob)");
+  if (chdir (argv[1]) != 0)
+    FAIL_EXIT1 ("chmod (%s): %m", argv[1]);
 
-  /* Do a glob on each remaining argument.  */
-  for (j = optind + 1; j < argc; j++) {
-    i = glob (argv[j], glob_flags, NULL, &g);
-    if (i != 0)
-      break;
-    glob_flags |= GLOB_APPEND;
-  }
+  /* Do a glob on each argument.  */
+  for (j = 2; j < argc; j++)
+    {
+      i = glob (argv[j], glob_flags, NULL, &g);
+      if (i != 0)
+	break;
+      glob_flags |= GLOB_APPEND;
+    }
 
   /* Was there an error? */
   if (i == GLOB_NOSPACE)
@@ -106,16 +127,18 @@ main (int argc, char *argv[])
   if ((glob_flags & GLOB_DOOFFS) && g.gl_pathv[0] == NULL)
     g.gl_pathv[0] = (char *) "abc";
 
-  /* Print out the names.  Unless otherwise specified, qoute them.  */
+  /* Print out the names.  Unless otherwise specified, quote them.  */
   if (g.gl_pathv)
     {
       for (i = 0; i < g.gl_offs + g.gl_pathc; ++i)
-        printf ("%s%s%s\n", quotes ? "`" : "",
-		g.gl_pathv[i] ? g.gl_pathv[i] : "(null)",
-		quotes ? "'" : "");
+	printf ("%s%s%s\n", quotes ? "`" : "",
+		g.gl_pathv[i] ? g.gl_pathv[i] : "(null)", quotes ? "'" : "");
     }
 
   globfree (&g);
 
   return 0;
 }
+
+#define TEST_FUNCTION_ARGV do_test_argv
+#include <support/test-driver.c>
diff --git a/posix/tst-glob_basic.c b/posix/tst-glob_basic.c
new file mode 100644
index 0000000..db30c7a
--- /dev/null
+++ b/posix/tst-glob_basic.c
@@ -0,0 +1,43 @@
+/* Basic glob tests.
+   Copyright (C) 2001-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  glob_t g;
+  g.gl_pathc = 0;
+
+  TEST_VERIFY_EXIT (glob ("", 0, NULL, &g) == GLOB_NOMATCH);
+  TEST_VERIFY_EXIT (g.gl_pathc == 0);
+
+  TEST_VERIFY_EXIT (glob ("", GLOB_NOCHECK, NULL, &g) == 0);
+  TEST_VERIFY_EXIT (g.gl_pathc == 1);
+  TEST_VERIFY_EXIT (strcmp (g.gl_pathv[0], "") == 0);
+
+  globfree (&g);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/posix/tst-glob_common.c b/posix/tst-glob_common.c
new file mode 100644
index 0000000..b7a66c7
--- /dev/null
+++ b/posix/tst-glob_common.c
@@ -0,0 +1,103 @@
+/* Common glob test definition.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+// #define DEBUG
+#ifdef DEBUG
+# define PRINTF(fmt, args...) \
+  do					\
+    {					\
+      int save_errno = errno;		\
+      printf (fmt, ##args);		\
+      errno = save_errno;		\
+    } while (0)
+#else
+# define PRINTF(fmt, args...)
+#endif
+
+struct filesystem_t
+{
+  const char *name;
+  int level;
+  int type;
+  mode_t mode;
+};
+
+static long int
+find_file (const char *s, const struct filesystem_t *filesystem,
+	   size_t nfiles)
+{
+  int level = 1;
+  long int idx = 0;
+
+  while (s[0] == '/')
+    {
+      if (s[1] == '\0')
+	{
+	  s = ".";
+	  break;
+	}
+      ++s;
+    }
+
+  if (strcmp (s, ".") == 0)
+    return 0;
+
+  if (s[0] == '.' && s[1] == '/')
+    s += 2;
+
+  while (*s != '\0')
+    {
+      char *endp = strchrnul (s, '/');
+
+      PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
+
+      while (idx < nfiles && filesystem[idx].level >= level)
+	{
+	  if (filesystem[idx].level == level
+	      && memcmp (s, filesystem[idx].name, endp - s) == 0
+	      && filesystem[idx].name[endp - s] == '\0')
+	    break;
+	  ++idx;
+	}
+
+      if (idx == nfiles || filesystem[idx].level < level)
+	{
+	  errno = ENOENT;
+	  return -1;
+	}
+
+      if (*endp == '\0')
+	return idx + 1;
+
+      if (filesystem[idx].type != DT_DIR
+	  && (idx + 1 >= nfiles
+	      || filesystem[idx].level >= filesystem[idx + 1].level))
+	{
+	  errno = ENOTDIR;
+	  return -1;
+	}
+
+      ++idx;
+
+      s = endp + 1;
+      ++level;
+    }
+
+  errno = ENOENT;
+  return -1;
+}
diff --git a/posix/bug-glob2.c b/posix/tst-glob_memory.c
similarity index 76%
rename from posix/bug-glob2.c
rename to posix/tst-glob_memory.c
index 592d957..33961e8 100644
--- a/posix/bug-glob2.c
+++ b/posix/tst-glob_memory.c
@@ -27,18 +27,10 @@
 #include <string.h>
 #include <sys/stat.h>
 
-// #define DEBUG
-#ifdef DEBUG
-# define PRINTF(fmt, args...) \
-  do					\
-    {					\
-      int save_errno = errno;		\
-      printf (fmt, ##args);		\
-      errno = save_errno;		\
-    } while (0)
-#else
-# define PRINTF(fmt, args...)
-#endif
+#include <support/check.h>
+
+#include "tst-glob_common.c"
+
 
 #define LONG_NAME \
   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
@@ -52,13 +44,7 @@
   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 
-static struct
-{
-  const char *name;
-  int level;
-  int type;
-  mode_t mode;
-} filesystem[] =
+struct filesystem_t filesystem[] =
 {
   { ".", 1, DT_DIR, 0755 },
   { "..", 1, DT_DIR, 0755 },
@@ -79,7 +65,7 @@ static struct
       { "..", 3, DT_DIR, 0755 },
       { "a", 3, DT_REG, 0644 }
 };
-#define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
+static const size_t nfiles = sizeof (filesystem) / sizeof (filesystem[0]);
 
 
 typedef struct
@@ -91,65 +77,10 @@ typedef struct
 } my_DIR;
 
 
-static long int
-find_file (const char *s)
-{
-  int level = 1;
-  long int idx = 0;
-
-  if (strcmp (s, ".") == 0)
-    return 0;
-
-  if (s[0] == '.' && s[1] == '/')
-    s += 2;
-
-  while (*s != '\0')
-    {
-      char *endp = strchrnul (s, '/');
-
-      PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
-
-      while (idx < nfiles && filesystem[idx].level >= level)
-	{
-	  if (filesystem[idx].level == level
-	      && memcmp (s, filesystem[idx].name, endp - s) == 0
-	      && filesystem[idx].name[endp - s] == '\0')
-	    break;
-	  ++idx;
-	}
-
-      if (idx == nfiles || filesystem[idx].level < level)
-	{
-	  errno = ENOENT;
-	  return -1;
-	}
-
-      if (*endp == '\0')
-	return idx + 1;
-
-      if (filesystem[idx].type != DT_DIR
-	  && (idx + 1 >= nfiles
-	      || filesystem[idx].level >= filesystem[idx + 1].level))
-	{
-	  errno = ENOTDIR;
-	  return -1;
-	}
-
-      ++idx;
-
-      s = endp + 1;
-      ++level;
-    }
-
-  errno = ENOENT;
-  return -1;
-}
-
-
 static void *
 my_opendir (const char *s)
 {
-  long int idx = find_file (s);
+  long int idx = find_file (s, filesystem, nfiles);
   my_DIR *dir;
 
   if (idx == -1)
@@ -241,7 +172,7 @@ my_closedir (void *dir)
 static int
 my_stat (const char *name, struct stat *st)
 {
-  long int idx = find_file (name);
+  long int idx = find_file (name, filesystem, nfiles);
 
   if (idx == -1)
     {
@@ -275,7 +206,7 @@ init_glob_altdirfuncs (glob_t *pglob)
 }
 
 
-int
+static int
 do_test (void)
 {
   mtrace ();
@@ -286,10 +217,7 @@ do_test (void)
 
   if (glob ("dir/*able/*", GLOB_ERR | GLOB_ALTDIRFUNC, NULL, &gl)
       != GLOB_ABORTED)
-    {
-      puts ("glob did not fail with GLOB_ABORTED");
-      exit (EXIT_FAILURE);
-    }
+    FAIL_EXIT1 ("glob did not fail with GLOB_ABORTED");
 
   globfree (&gl);
 
@@ -298,10 +226,7 @@ do_test (void)
 
   gl.gl_offs = 3;
   if (glob ("dir2/*", GLOB_DOOFFS, NULL, &gl) != GLOB_NOMATCH)
-    {
-      puts ("glob did not fail with GLOB_NOMATCH");
-      exit (EXIT_FAILURE);
-    }
+    FAIL_EXIT1 ("glot dit not fail with GLOB_NOMATCH");
 
   globfree (&gl);
 
@@ -310,5 +235,4 @@ do_test (void)
   return 0;
 }
 
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/posix/tst-gnuglob.c b/posix/tst-gnuglob.c
index d753674..f86eef7 100644
--- a/posix/tst-gnuglob.c
+++ b/posix/tst-gnuglob.c
@@ -20,7 +20,6 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <error.h>
 #include <glob.h>
 #include <mcheck.h>
 #include <stdio.h>
@@ -28,21 +27,11 @@
 #include <string.h>
 #include <sys/stat.h>
 
+#include <support/check.h>
 
-// #define DEBUG
-#ifdef DEBUG
-# define PRINTF(fmt, args...) printf (fmt, ##args)
-#else
-# define PRINTF(fmt, args...)
-#endif
-
+#include "tst-glob_common.c"
 
-static struct
-{
-  const char *name;
-  int level;
-  int type;
-} filesystem[] =
+struct filesystem_t filesystem[] =
 {
   { ".", 1, DT_DIR },
   { "..", 1, DT_DIR },
@@ -84,7 +73,7 @@ static struct
 	{ "..", 4, DT_DIR },
 	{ "hidden", 4, DT_REG }
 };
-#define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))
+static const size_t nfiles = sizeof (filesystem) / sizeof (filesystem[0]);
 
 
 typedef struct
@@ -96,75 +85,10 @@ typedef struct
 } my_DIR;
 
 
-static long int
-find_file (const char *s)
-{
-  int level = 1;
-  long int idx = 0;
-
-  while (s[0] == '/')
-    {
-      if (s[1] == '\0')
-	{
-	  s = ".";
-	  break;
-	}
-      ++s;
-    }
-
-  if (strcmp (s, ".") == 0)
-    return 0;
-
-  if (s[0] == '.' && s[1] == '/')
-    s += 2;
-
-  while (*s != '\0')
-    {
-      char *endp = strchrnul (s, '/');
-
-      PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);
-
-      while (idx < nfiles && filesystem[idx].level >= level)
-	{
-	  if (filesystem[idx].level == level
-	      && memcmp (s, filesystem[idx].name, endp - s) == 0
-	      && filesystem[idx].name[endp - s] == '\0')
-	    break;
-	  ++idx;
-	}
-
-      if (idx == nfiles || filesystem[idx].level < level)
-	{
-	  errno = ENOENT;
-	  return -1;
-	}
-
-      if (*endp == '\0')
-	return idx + 1;
-
-      if (filesystem[idx].type != DT_DIR
-	  && (idx + 1 >= nfiles
-	      || filesystem[idx].level >= filesystem[idx + 1].level))
-	{
-	  errno = ENOTDIR;
-	  return -1;
-	}
-
-      ++idx;
-
-      s = endp + 1;
-      ++level;
-    }
-
-  errno = ENOENT;
-  return -1;
-}
-
-
 static void *
 my_opendir (const char *s)
 {
-  long int idx = find_file (s);
+  long int idx = find_file (s, filesystem, nfiles);
   my_DIR *dir;
 
 
@@ -176,7 +100,7 @@ my_opendir (const char *s)
 
   dir = (my_DIR *) malloc (sizeof (my_DIR));
   if (dir == NULL)
-    error (EXIT_FAILURE, errno, "cannot allocate directory handle");
+    FAIL_EXIT1 ("cannot allocate directory handle");
 
   dir->level = filesystem[idx].level;
   dir->idx = idx;
@@ -247,13 +171,11 @@ my_closedir (void *dir)
 static int
 my_stat (const char *name, struct stat *st)
 {
-  long int idx = find_file (name);
+  long int idx = find_file (name, filesystem, nfiles);
 
   if (idx == -1)
-    {
-      PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));
-      return -1;
-    }
+    FAIL_EXIT1 ("%s (\"%s\", ...) == -1 (%s)", __func__, name,
+		strerror (errno));
 
   memset (st, '\0', sizeof (*st));
 
@@ -276,7 +198,8 @@ static const char *glob_errstring[] =
   [GLOB_ABORTED] = "read error",
   [GLOB_NOMATCH] = "no matches found"
 };
-#define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))
+static const size_t nglob_errstring = (sizeof (glob_errstring)
+				       / sizeof (glob_errstring[0]));
 
 
 static const char *
@@ -289,7 +212,7 @@ flagstr (int flags)
     "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",
     "GLOB_ONLYDIR", "GLOB_TILDECHECK"
   };
-#define nstrs (sizeof (strs) / sizeof (strs[0]))
+  static const size_t nstrs = (sizeof (strs) / sizeof (strs[0]));
   static char buf[100];
   char *cp = buf;
   int cnt;
@@ -311,7 +234,6 @@ flagstr (int flags)
     }
 
   return buf;
-#undef nstrs
 }
 
 
@@ -378,7 +300,6 @@ test_result (const char *fmt, int flags, glob_t *gl, const char *str[])
   return result;
 }
 
-
 static int
 do_test (void)
 {
@@ -416,7 +337,8 @@ do_test (void)
     result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL });    \
   else									      \
     printf ("result for glob (\"%s\", %s) = %s\n\n", fmt, flagstr (flags),    \
-	    errstr (errval))
+	    errstr (errval));						      \
+  globfree (&gl)
 
   test ("*/*/*", 0, 0,
 	"dir1lev1/dir2lev2/dir1lev3",
@@ -498,5 +420,4 @@ do_test (void)
   return result;
 }
 
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 18/18] posix: Fix glob with GLOB_NOCHECK returning modified patterns (BZ#10246)
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 11/18] posix: Remove alloca usage on glob dirname Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866] Adhemerval Zanella
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

Acconding to POSIX glob with GLOB_NOCHECK should return a list consisting
of only of the input pattern in case of no match.  However GLIBC does not
honor in case of '//<something'.  This is due internally this is handled
and special case and prefix_array (responsable to prepend the directory
name) does not know if the input already contains a slash or not since
either '/<something>' or '//<something>' will be handle in same way.

This patch fix it by using a empty directory name for the latter (since
prefix_array already adds a slash as default for each entry).

Checked on x86_64-linux-gnu.

	[BZ #10246]
	* posix/glob.c (glob): Handle pattern that do not match and
	start with '/' correctly.
	* posix/globtest.sh: New tests for NOCHECK.
---
 posix/glob.c      | 14 ++++++++------
 posix/globtest.sh | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 37e8702..3f85586 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -358,6 +358,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
   size_t oldcount;
   int meta;
   bool dirname_modified;
+  /* Indicate if the directory should be prepended on return values.  */
+  bool dirname_prefix = true;
   glob_t dirs;
   int retval = 0;
   struct char_array dirname;
@@ -591,6 +593,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	goto err_nospace;
       dirlen = 1;
       ++filename;
+      /* prefix_array adds a separator for each result and DIRNAME is
+	 already '/'.  So we indicate later that we should not prepend
+	 anything for this specific case.  */
+      dirname_prefix = false;
     }
   else
     {
@@ -1101,7 +1107,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       if (dirlen > 0)
 	{
 	  /* Stick the directory on the front of each name.  */
-	  if (prefix_array (char_array_str (&dirname),
+	  if (prefix_array (dirname_prefix ? char_array_str (&dirname) : "",
 			    &pglob->gl_pathv[old_pathc + pglob->gl_offs],
 			    pglob->gl_pathc - old_pathc))
 	    {
@@ -1198,12 +1204,8 @@ prefix_array (const char *dirname, char **array, size_t n)
 # define DIRSEP_CHAR '/'
 #endif
 
-  if (dirlen == 1 && dirname[0] == '/')
-    /* DIRNAME is just "/", so normal prepending would get us "//foo".
-       We want "/foo" instead, so don't prepend any chars from DIRNAME.  */
-    dirlen = 0;
 #if defined __MSDOS__ || defined WINDOWS32
-  else if (dirlen > 1)
+  if (dirlen > 1)
     {
       if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':')
 	/* DIRNAME is "d:/".  Don't prepend the slash from DIRNAME.  */
diff --git a/posix/globtest.sh b/posix/globtest.sh
index 73f7ae3..938bc47 100755
--- a/posix/globtest.sh
+++ b/posix/globtest.sh
@@ -242,6 +242,42 @@ if test $failed -ne 0; then
   result=1
 fi
 
+# Test NOCHECK for specific cases where the pattern used starts
+# with '/' (BZ#10246).
+failed=0
+${test_program_prefix} \
+${common_objpfx}posix/globtest -c "$testdir" "/%" |
+sort > $testout
+cat <<"EOF" | $CMP - $testout >> $logfile || failed=1
+`/%'
+EOF
+if test $failed -ne 0; then
+  echo "No check test failed" >> $logfile
+  result=1
+fi
+
+${test_program_prefix} \
+${common_objpfx}posix/globtest -c "$testdir" "//%" |
+sort > $testout
+cat <<"EOF" | $CMP - $testout >> $logfile || failed=1
+`//%'
+EOF
+if test $failed -ne 0; then
+  echo "No check test failed" >> $logfile
+  result=1
+fi
+
+${test_program_prefix} \
+${common_objpfx}posix/globtest -c "$testdir" "///%" |
+sort > $testout
+cat <<"EOF" | $CMP - $testout >> $logfile || failed=1
+`///%'
+EOF
+if test $failed -ne 0; then
+  echo "No check test failed" >> $logfile
+  result=1
+fi
+
 # Test NOMAGIC without magic characters
 failed=0
 ${test_program_prefix} \
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 11/18] posix: Remove alloca usage on glob dirname
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 18/18] posix: Fix glob with GLOB_NOCHECK returning modified patterns (BZ#10246) Adhemerval Zanella
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch replaces the alloca/malloc usage for dirname creation
by the char_array struct.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob_in_dir): Remove alloca usage for fullname.
	* malloc/char_array-skeleton.c (char_array_init_str): Remove unused
	attribute.
	(char_array_append_str): Likewise.
---
 posix/glob.c | 32 +++++++++++---------------------
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 1892f48..c09b347 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -1274,7 +1274,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	     int (*errfunc) (const char *, int),
 	     glob_t *pglob, size_t alloca_used)
 {
-  size_t dirlen = strlen (directory);
   void *stream = NULL;
   struct globnames
     {
@@ -1314,35 +1313,26 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	struct stat st;
 	struct_stat64 st64;
       } ust;
-      size_t patlen = strlen (pattern);
-      size_t fullsize;
-      bool alloca_fullname
-	= (! size_add_wrapv (dirlen + 1, patlen + 1, &fullsize)
-	   && glob_use_alloca (alloca_used, fullsize));
-      char *fullname;
-      if (alloca_fullname)
-	fullname = alloca_account (fullsize, alloca_used);
-      else
+      struct char_array fullname;
+
+      if (!char_array_init_str (&fullname, directory)
+	  || !char_array_append_str (&fullname, "/")
+	  || !char_array_append_str (&fullname, pattern))
 	{
-	  fullname = malloc (fullsize);
-	  if (fullname == NULL)
-	    return GLOB_NOSPACE;
+	  char_array_free (&fullname);
+	  return GLOB_NOSPACE;
 	}
 
-      mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
-			"/", 1),
-	       pattern, patlen + 1);
-      if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-	   ? (*pglob->gl_stat) (fullname, &ust.st)
-	   : __lstat64 (fullname, &ust.st64))
+      if (((__glibc_likely (flags & GLOB_ALTDIRFUNC)
+	   ? (*pglob->gl_lstat) (char_array_str (&fullname), &ust.st)
+	   : __lstat64 (char_array_str (&fullname), &ust.st64))
 	  == 0)
 	  || errno == EOVERFLOW)
 	/* We found this file to be existing.  Now tell the rest
 	   of the function to copy this name into the result.  */
 	flags |= GLOB_NOCHECK;
 
-      if (__glibc_unlikely (!alloca_fullname))
-	free (fullname);
+      char_array_free (&fullname);
     }
   else
     {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 07/18] posix: User LOGIN_NAME_MAX for all user names in glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (9 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 16/18] posix: More check for overflow allocation in glob Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 14/18] posix: Use char_array for home_dir " Adhemerval Zanella
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch limits all user name obtained for GLOB_TILDE to max of
LOGIN_NAME_MAX (256 on glibc) and remove all stack/malloc buffer
handling boilerplate.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob): Remove alloca usage on user_name for
	GLOB_TILDE.
---
 posix/glob.c | 36 ++++++++++++------------------------
 1 file changed, 12 insertions(+), 24 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 3a74758..554d558 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/param.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -775,8 +776,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       else
 	{
 	  char *end_name = strchr (dirname, '/');
-	  char *user_name;
-	  int malloc_user_name = 0;
+	  char user_name[LOGIN_NAME_MAX];
 	  char *unescape = NULL;
 
 	  if (!(flags & GLOB_NOESCAPE))
@@ -791,26 +791,14 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		unescape = memchr (dirname, '\\', end_name - dirname);
 	    }
 	  if (end_name == NULL)
-	    user_name = dirname + 1;
+	    strncpy (user_name, dirname + 1, LOGIN_NAME_MAX - 1);
 	  else
 	    {
-	      char *newp;
-	      if (glob_use_alloca (alloca_used, end_name - dirname))
-		newp = alloca_account (end_name - dirname, alloca_used);
-	      else
-		{
-		  newp = malloc (end_name - dirname);
-		  if (newp == NULL)
-		    {
-		      retval = GLOB_NOSPACE;
-		      goto out;
-		    }
-		  malloc_user_name = 1;
-		}
 	      if (unescape != NULL)
 		{
-		  char *p = mempcpy (newp, dirname + 1,
-				     unescape - dirname - 1);
+		  ptrdiff_t name_len = unescape - dirname - 1;
+		  name_len = MIN (name_len, LOGIN_NAME_MAX - 1);
+		  char *p = mempcpy (user_name, dirname + 1, name_len);
 		  char *q = unescape;
 		  while (*q != '\0')
 		    {
@@ -832,9 +820,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  *p = '\0';
 		}
 	      else
-		*((char *) mempcpy (newp, dirname + 1, end_name - dirname))
-		  = '\0';
-	      user_name = newp;
+		{
+		  ptrdiff_t name_len = end_name - dirname;
+		  name_len = MIN (name_len, LOGIN_NAME_MAX - 1);
+		  *((char *) mempcpy (user_name, dirname + 1, name_len))
+		    = '\0';
+		}
 	    }
 
 	  /* Look up specific user's home directory.  */
@@ -866,9 +857,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    p = getpwnam (user_name);
 #  endif
 
-	    if (__glibc_unlikely (malloc_user_name))
-	      free (user_name);
-
 	    /* If we found a home directory use this.  */
 	    if (p != NULL)
 	      {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (14 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 02/18] posix: Adjust glob tests to libsupport Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-09-02 22:50   ` Paul Eggert
  2017-08-11 14:51 ` [PATCH 17/18] posix: Use enum for __glob_pattern_type result Adhemerval Zanella
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

Current glob implementation allows non limited user name for home
directory construction on GLOB_TILDE case.  To accomplish it glob
either construct a name on stack if size are small enough (based
on current alloca_used) value in heap otherwise.

There is no actual login to resize the buffer in case of the resizing
the buffer in case of ERANGE, so a static buffer using glibc default
LOGIN_NAME_MAX is suffice.

Checked on x86_64-linux-gnu.

	* posix/glob.c (LOGIN_NAME_MAX): Define if not defined.
	(glob): Use static buffer for user_name on getlogin_r.
---
 posix/glob.c | 36 +++++-------------------------------
 1 file changed, 5 insertions(+), 31 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 99e9c54..3a74758 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -91,10 +91,8 @@
 #include <glob_internal.h>
 #include <scratch_buffer.h>
 
-#ifdef _SC_LOGIN_NAME_MAX
-# define GET_LOGIN_NAME_MAX()	sysconf (_SC_LOGIN_NAME_MAX)
-#else
-# define GET_LOGIN_NAME_MAX()	(-1)
+#ifndef LOGIN_NAME_MAX
+# define LOGIN_NAME_MAX 256
 #endif
 \f
 static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
@@ -667,28 +665,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  if (home_dir == NULL || home_dir[0] == '\0')
 	    {
 	      int success;
-	      char *name;
-	      int malloc_name = 0;
-	      size_t buflen = GET_LOGIN_NAME_MAX () + 1;
-
-	      if (buflen == 0)
-		/* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
-		   a moderate value.  */
-		buflen = 20;
-	      if (glob_use_alloca (alloca_used, buflen))
-		name = alloca_account (buflen, alloca_used);
-	      else
-		{
-		  name = malloc (buflen);
-		  if (name == NULL)
-		    {
-		      retval = GLOB_NOSPACE;
-		      goto out;
-		    }
-		  malloc_name = 1;
-		}
+	      char user_name[LOGIN_NAME_MAX];
 
-	      success = __getlogin_r (name, buflen) == 0;
+	      success = __getlogin_r (user_name, sizeof (user_name)) == 0;
 	      if (success)
 		{
 		  struct passwd *p;
@@ -698,7 +677,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  struct scratch_buffer pwtmpbuf;
 		  scratch_buffer_init (&pwtmpbuf);
 
-		  while (getpwnam_r (name, &pwbuf,
+		  while (getpwnam_r (user_name, &pwbuf,
 				     pwtmpbuf.data, pwtmpbuf.length, &p)
 			 != 0)
 		    {
@@ -730,11 +709,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		    }
 		  scratch_buffer_free (&pwtmpbuf);
 		}
-	      else
-		{
-		  if (__glibc_unlikely (malloc_name))
-		    free (name);
-		}
 	    }
 	  if (home_dir == NULL || home_dir[0] == '\0')
 	    {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 15/18] posix: Add common function to get home directory
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (11 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 14/18] posix: Use char_array for home_dir " Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 10/18] posix: Remove alloca usage for GLOB_BRACE on glob Adhemerval Zanella
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch adds a common function to get the full home directory
from a user.  No functional changes expected.

Checked on x86_64-linux-gnu.

	* posix/glob.c (get_home_directory): New function.
	(glob): Use get_home_directory.
---
 posix/glob.c | 145 +++++++++++++++++++++++------------------------------------
 1 file changed, 56 insertions(+), 89 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index d2cb871..c85342a 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -265,6 +265,44 @@ next_brace_sub (const char *cp, int flags)
   return *cp != '\0' ? cp : NULL;
 }
 
+/* Obtain the full home directory path from user 'user_name' and writes it
+   on char_array 'home_dir'.  */
+static bool
+get_home_directory (const char *user_name, struct char_array *home_dir)
+{
+  struct passwd *p;
+#if defined HAVE_GETPWNAM_R || defined _LIBC
+  struct passwd pwbuf;
+  int save = errno;
+  struct scratch_buffer pwtmpbuf;
+  scratch_buffer_init (&pwtmpbuf);
+
+  while (getpwnam_r (user_name, &pwbuf, pwtmpbuf.data, pwtmpbuf.length, &p)
+	 != 0)
+    {
+      if (errno != ERANGE)
+	{
+	  p = NULL;
+	  break;
+	}
+      if (!scratch_buffer_grow (&pwtmpbuf))
+	return false;
+      __set_errno (save);
+    }
+#else
+  p = getpwnam (pwtmpbuf.data);
+#endif
+
+  bool retval = false;
+  if (p != NULL)
+    {
+      if (char_array_set_str (home_dir, p->pw_dir))
+	retval = true;
+    }
+  scratch_buffer_free (&pwtmpbuf);
+  return retval;
+}
+
 
 /* Do glob searching for PATTERN, placing results in PGLOB.
    The bits defined above may be set in FLAGS.
@@ -650,38 +688,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      success = __getlogin_r (user_name, sizeof (user_name)) == 0;
 	      if (success)
 		{
-		  struct passwd *p;
-#   if defined HAVE_GETPWNAM_R || defined _LIBC
-		  struct passwd pwbuf;
-		  int save = errno;
-		  struct scratch_buffer pwtmpbuf;
-		  scratch_buffer_init (&pwtmpbuf);
-
-		  while (getpwnam_r (user_name, &pwbuf,
-				     pwtmpbuf.data, pwtmpbuf.length, &p)
-			 != 0)
-		    {
-		      if (errno != ERANGE)
-			{
-			  p = NULL;
-			  break;
-			}
-		      if (!scratch_buffer_grow (&pwtmpbuf))
-			goto err_nospace;
-		      __set_errno (save);
-		    }
-#   else
-		  p = getpwnam (pwtmpbuf.data);
-#   endif
-		  if (p != NULL)
-		    {
-		      if (!char_array_set_str (&home_dir, p->pw_dir))
-			{
-			  scratch_buffer_free (&pwtmpbuf);
-			  goto err_nospace;
-			}
-		    }
-		  scratch_buffer_free (&pwtmpbuf);
+		  if (!get_home_directory (user_name, &home_dir))
+		    goto err_nospace;
 		}
 	    }
 	  if (char_array_is_empty (&home_dir))
@@ -694,10 +702,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      else
 		{
 		  if (!char_array_set_str (&home_dir, "~"))
-		    {
-		      retval = GLOB_NOSPACE;
-		      goto out;
-		    }
+		    goto err_nospace;
 		}
 	    }
 #  endif /* WINDOWS32 */
@@ -778,59 +783,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    }
 
 	  /* Look up specific user's home directory.  */
-	  {
-	    struct passwd *p;
-	    struct scratch_buffer pwtmpbuf;
-	    scratch_buffer_init (&pwtmpbuf);
-
-#  if defined HAVE_GETPWNAM_R || defined _LIBC
-	    struct passwd pwbuf;
-	    int save = errno;
-
-	    while (getpwnam_r (user_name, &pwbuf,
-			       pwtmpbuf.data, pwtmpbuf.length, &p) != 0)
-	      {
-		if (errno != ERANGE)
-		  {
-		    p = NULL;
-		    break;
-		  }
-		if (!scratch_buffer_grow (&pwtmpbuf))
-		  {
-		    retval = GLOB_NOSPACE;
-		    goto out;
-		  }
-		__set_errno (save);
-	      }
-#  else
-	    p = getpwnam (user_name);
-#  endif
-
-	    /* If we found a home directory use this.  */
-	    if (p != NULL)
-	      {
-		if (!char_array_set_str (&dirname, p->pw_dir))
-		  {
-		    scratch_buffer_free (&pwtmpbuf);
-		    retval = GLOB_NOSPACE;
-		    goto out;
-		  }
-
-		dirlen = strlen (p->pw_dir);
-		dirname_modified = true;
-	      }
-	    else
-	      {
-		if (flags & GLOB_TILDE_CHECK)
-		  {
-		  /* We have to regard it as an error if we cannot find the
-		     home directory.  */
-		    retval = GLOB_NOMATCH;
-		    goto out;
-		  }
-	      }
-	    scratch_buffer_free (&pwtmpbuf);
-	  }
+	  if (get_home_directory (user_name, &dirname))
+	    {
+	      dirlen = char_array_size (&dirname) - 1;
+	      dirname_modified = true;
+	    }
+	  else
+	    {
+	      if (flags & GLOB_TILDE_CHECK)
+	       /* We have to regard it as an error if we cannot find the
+		  home directory.  */
+	        {
+		  retval = GLOB_NOMATCH;
+		  goto out;
+		}
+	    }
 	}
 # endif	/* Not Amiga && not WINDOWS32.  */
     }
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (3 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 08/18] malloc: Add specialized dynarray for C strings Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-09-01 23:50   ` Paul Eggert
  2017-09-02 10:40   ` Paul Eggert
  2017-08-11 14:51 ` [PATCH 13/18] posix: Remove all alloca usage in glob Adhemerval Zanella
                   ` (13 subsequent siblings)
  18 siblings, 2 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

From: Florian Weimer <fweimer@redhat.com>

This patch removes a lot of boilerplate code to manager buffers for
getpwnam_r.

Checked on x86_64-linux-gnu.

	[BZ #18023]
	* posix/glob.c (glob): Use struct scratch_buffer instead of
	extend_alloca.
---
 posix/glob.c | 147 +++++++++++------------------------------------------------
 1 file changed, 26 insertions(+), 121 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 7b6b426..99e9c54 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -89,12 +89,8 @@
 #include <fnmatch.h>
 
 #include <glob_internal.h>
+#include <scratch_buffer.h>
 
-#ifdef _SC_GETPW_R_SIZE_MAX
-# define GETPW_R_SIZE_MAX()	sysconf (_SC_GETPW_R_SIZE_MAX)
-#else
-# define GETPW_R_SIZE_MAX()	(-1)
-#endif
 #ifdef _SC_LOGIN_NAME_MAX
 # define GET_LOGIN_NAME_MAX()	sysconf (_SC_LOGIN_NAME_MAX)
 #else
@@ -696,97 +692,43 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	      if (success)
 		{
 		  struct passwd *p;
-		  char *malloc_pwtmpbuf = NULL;
-		  char *pwtmpbuf;
 #   if defined HAVE_GETPWNAM_R || defined _LIBC
-		  long int pwbuflenmax = GETPW_R_SIZE_MAX ();
-		  size_t pwbuflen = pwbuflenmax;
 		  struct passwd pwbuf;
 		  int save = errno;
+		  struct scratch_buffer pwtmpbuf;
+		  scratch_buffer_init (&pwtmpbuf);
 
-#    ifndef _LIBC
-                  if (! (0 < pwbuflenmax && pwbuflenmax <= SIZE_MAX))
-		    /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.
-		       Try a moderate value.  */
-		    pwbuflen = 1024;
-#    endif
-		  if (glob_use_alloca (alloca_used, pwbuflen))
-		    pwtmpbuf = alloca_account (pwbuflen, alloca_used);
-		  else
-		    {
-		      pwtmpbuf = malloc (pwbuflen);
-		      if (pwtmpbuf == NULL)
-			{
-			  if (__glibc_unlikely (malloc_name))
-			    free (name);
-			  retval = GLOB_NOSPACE;
-			  goto out;
-			}
-		      malloc_pwtmpbuf = pwtmpbuf;
-		    }
-
-		  while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
+		  while (getpwnam_r (name, &pwbuf,
+				     pwtmpbuf.data, pwtmpbuf.length, &p)
 			 != 0)
 		    {
-		      size_t newlen;
-		      bool v;
 		      if (errno != ERANGE)
 			{
 			  p = NULL;
 			  break;
 			}
-		      v = size_add_wrapv (pwbuflen, pwbuflen, &newlen);
-		      if (!v && malloc_pwtmpbuf == NULL
-			  && glob_use_alloca (alloca_used, newlen))
-			pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen,
-							  newlen, alloca_used);
-		      else
+		      if (!scratch_buffer_grow (&pwtmpbuf))
 			{
-			  char *newp = (v ? NULL
-					: realloc (malloc_pwtmpbuf, newlen));
-			  if (newp == NULL)
-			    {
-			      free (malloc_pwtmpbuf);
-			      if (__glibc_unlikely (malloc_name))
-				free (name);
-			      retval = GLOB_NOSPACE;
-			      goto out;
-			    }
-			  malloc_pwtmpbuf = pwtmpbuf = newp;
+			  retval = GLOB_NOSPACE;
+			  goto out;
 			}
-		      pwbuflen = newlen;
 		      __set_errno (save);
 		    }
 #   else
-		  p = getpwnam (name);
+		  p = getpwnam (pwtmpbuf.data);
 #   endif
-		  if (__glibc_unlikely (malloc_name))
-		    free (name);
 		  if (p != NULL)
 		    {
-		      if (malloc_pwtmpbuf == NULL)
-			home_dir = p->pw_dir;
-		      else
+		      home_dir = strdup (p->pw_dir);
+		      malloc_home_dir = 1;
+		      if (home_dir == NULL)
 			{
-			  size_t home_dir_len = strlen (p->pw_dir) + 1;
-			  if (glob_use_alloca (alloca_used, home_dir_len))
-			    home_dir = alloca_account (home_dir_len,
-						       alloca_used);
-			  else
-			    {
-			      home_dir = malloc (home_dir_len);
-			      if (home_dir == NULL)
-				{
-				  free (pwtmpbuf);
-				  retval = GLOB_NOSPACE;
-				  goto out;
-				}
-			      malloc_home_dir = 1;
-			    }
-			  memcpy (home_dir, p->pw_dir, home_dir_len);
+			  scratch_buffer_free (&pwtmpbuf);
+			  retval = GLOB_NOSPACE;
+			  goto out;
 			}
 		    }
-		  free (malloc_pwtmpbuf);
+		  scratch_buffer_free (&pwtmpbuf);
 		}
 	      else
 		{
@@ -924,59 +866,25 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* Look up specific user's home directory.  */
 	  {
 	    struct passwd *p;
-	    char *malloc_pwtmpbuf = NULL;
+	    struct scratch_buffer pwtmpbuf;
+	    scratch_buffer_init (&pwtmpbuf);
+
 #  if defined HAVE_GETPWNAM_R || defined _LIBC
-	    long int buflenmax = GETPW_R_SIZE_MAX ();
-	    size_t buflen = buflenmax;
-	    char *pwtmpbuf;
 	    struct passwd pwbuf;
 	    int save = errno;
 
-#   ifndef _LIBC
-	    if (! (0 <= buflenmax && buflenmax <= SIZE_MAX))
-	      /* Perhaps 'sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
-		 moderate value.  */
-	      buflen = 1024;
-#   endif
-	    if (glob_use_alloca (alloca_used, buflen))
-	      pwtmpbuf = alloca_account (buflen, alloca_used);
-	    else
-	      {
-		pwtmpbuf = malloc (buflen);
-		if (pwtmpbuf == NULL)
-		  {
-		  nomem_getpw:
-		    if (__glibc_unlikely (malloc_user_name))
-		      free (user_name);
-		    retval = GLOB_NOSPACE;
-		    goto out;
-		  }
-		malloc_pwtmpbuf = pwtmpbuf;
-	      }
-
-	    while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0)
+	    while (getpwnam_r (user_name, &pwbuf,
+			       pwtmpbuf.data, pwtmpbuf.length, &p) != 0)
 	      {
-		size_t newlen;
-		bool v;
 		if (errno != ERANGE)
 		  {
 		    p = NULL;
 		    break;
 		  }
-		v = size_add_wrapv (buflen, buflen, &newlen);
-		if (!v && malloc_pwtmpbuf == NULL
-		    && glob_use_alloca (alloca_used, newlen))
-		  pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen,
-						    newlen, alloca_used);
-		else
+		if (!scratch_buffer_grow (&pwtmpbuf))
 		  {
-		    char *newp = v ? NULL : realloc (malloc_pwtmpbuf, newlen);
-		    if (newp == NULL)
-		      {
-			free (malloc_pwtmpbuf);
-			goto nomem_getpw;
-		      }
-		    malloc_pwtmpbuf = pwtmpbuf = newp;
+		    retval = GLOB_NOSPACE;
+		    goto out;
 		  }
 		__set_errno (save);
 	      }
@@ -1005,7 +913,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		    dirname = malloc (home_len + rest_len + 1);
 		    if (dirname == NULL)
 		      {
-			free (malloc_pwtmpbuf);
+			scratch_buffer_free (&pwtmpbuf);
 			retval = GLOB_NOSPACE;
 			goto out;
 		      }
@@ -1016,13 +924,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 
 		dirlen = home_len + rest_len;
 		dirname_modified = 1;
-
-		free (malloc_pwtmpbuf);
 	      }
 	    else
 	      {
-		free (malloc_pwtmpbuf);
-
 		if (flags & GLOB_TILDE_CHECK)
 		  {
 		  /* We have to regard it as an error if we cannot find the
@@ -1031,6 +935,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		    goto out;
 		  }
 	      }
+	    scratch_buffer_free (&pwtmpbuf);
 	  }
 	}
 # endif	/* Not Amiga && not WINDOWS32.  */
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 12/18] posix: Use dynarray for globname in glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (5 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 13/18] posix: Remove all alloca usage in glob Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 01/18] posix: Sync glob with gnulib [BZ #1062] Adhemerval Zanella
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch uses dynarray at glob internal glob_in_dir function to manage
the various matched patterns.  It simplify and removes all the boilerplate
buffer managements required.  It also removes the glob_use_alloca, since
it is not used anymore.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob_use_alloca): Remove.
	(glob_in_dir): Use dynarray for globnames.
---
 posix/glob.c | 155 +++++++++++++++--------------------------------------------
 1 file changed, 38 insertions(+), 117 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index c09b347..f40ee6d 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -238,33 +238,6 @@ convert_dirent64 (const struct dirent64 *source)
     ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0)
 #endif
 
-/* Set *R = A + B.  Return true if the answer is mathematically
-   incorrect due to overflow; in this case, *R is the low order
-   bits of the correct answer..  */
-
-static bool size_add_wrapv (size_t a, size_t b, size_t *r);
-static bool glob_use_alloca (size_t alloca_used, size_t len);
-
-/* We must not compile this function twice.  */
-static bool
-size_add_wrapv (size_t a, size_t b, size_t *r)
-{
-#if 5 <= __GNUC__
-  return __builtin_add_overflow (a, b, r);
-#else
-  *r = a + b;
-  return *r < a;
-#endif
-}
-
-static bool
-glob_use_alloca (size_t alloca_used, size_t len)
-{
-  size_t size;
-  return (!size_add_wrapv (alloca_used, len, &size)
-	  && __libc_use_alloca (size));
-}
-
 static int glob_in_dir (const char *pattern, const char *directory,
 			int flags, int (*errfunc) (const char *, int),
 			glob_t *pglob, size_t alloca_used);
@@ -1264,6 +1237,20 @@ prefix_array (const char *dirname, char **array, size_t n)
   return 0;
 }
 
+struct globnames_result
+{
+  char **names;
+  size_t length;
+};
+
+/* Create a dynamic array for C string representing the glob name found.  */
+#define DYNARRAY_STRUCT            globnames_array
+#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
+#define DYNARRAY_ELEMENT           char *
+#define DYNARRAY_PREFIX            globnames_array_
+#define DYNARRAY_FINAL_TYPE        struct globnames_result
+#define DYNARRAY_INITIAL_SIZE      64
+#include <malloc/dynarray-skeleton.c>
 
 /* Like `glob', but PATTERN is a final pathname component,
    and matches are searched for in DIRECTORY.
@@ -1275,26 +1262,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 	     glob_t *pglob, size_t alloca_used)
 {
   void *stream = NULL;
-  struct globnames
-    {
-      struct globnames *next;
-      size_t count;
-      char *name[64];
-    };
-#define INITIAL_COUNT sizeof (init_names.name) / sizeof (init_names.name[0])
-  struct globnames init_names;
-  struct globnames *names = &init_names;
-  struct globnames *names_alloca = &init_names;
+  struct globnames_array globnames;
   size_t nfound = 0;
-  size_t cur = 0;
   int meta;
   int save;
   int result;
 
-  alloca_used += sizeof (init_names);
-
-  init_names.next = NULL;
-  init_names.count = INITIAL_COUNT;
+  globnames_array_init (&globnames);
 
   meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE));
   if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
@@ -1383,30 +1357,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 
 	      if (fnmatch (pattern, d.name, fnm_flags) == 0)
 		{
-		  if (cur == names->count)
-		    {
-		      struct globnames *newnames;
-		      size_t count = names->count * 2;
-		      size_t size = (sizeof (struct globnames)
-				     + ((count - INITIAL_COUNT)
-				     * sizeof (char *)));
-		      if (glob_use_alloca (alloca_used, size))
-			newnames = names_alloca
-			  = alloca_account (size, alloca_used);
-		      else if ((newnames = malloc (size)) == NULL)
-			goto memory_error;
-		      newnames->count = count;
-		      newnames->next = names;
-		      names = newnames;
-		      cur = 0;
-		   }
-		   names->name[cur] = strdup (d.name);
-		   if (names->name[cur] == NULL)
-		     goto memory_error;
-		   ++cur;
-		   ++nfound;
-		   if (SIZE_MAX - pglob->gl_offs <= nfound)
-		     goto memory_error;
+		  globnames_array_add (&globnames, strdup (d.name));
+		  if (globnames_array_has_failed (&globnames))
+		    goto memory_error;
+		  nfound++;
 		}
 	    }
 	}
@@ -1416,10 +1370,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
     {
       size_t len = strlen (pattern);
       nfound = 1;
-      names->name[cur] = malloc (len + 1);
-      if (names->name[cur] == NULL)
+      char *newp = malloc (len + 1);
+      if (newp == NULL)
+	goto memory_error;
+      *((char *) mempcpy (newp, pattern, len)) = '\0';
+      globnames_array_add (&globnames, newp);
+      if (globnames_array_has_failed (&globnames))
 	goto memory_error;
-      *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0';
     }
 
   result = GLOB_NOMATCH;
@@ -1440,61 +1397,25 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
       if (new_gl_pathv == NULL)
 	{
 	memory_error:
-	  while (1)
-	    {
-	      struct globnames *old = names;
-	      size_t i;
-	      for (i = 0; i < cur; ++i)
-		free (names->name[i]);
-	      names = names->next;
-	      /* NB: we will not leak memory here if we exit without
-		 freeing the current block assigned to OLD.  At least
-		 the very first block is always allocated on the stack
-		 and this is the block assigned to OLD here.  */
-	      if (names == NULL)
-		{
-		  assert (old == &init_names);
-		  break;
-		}
-	      cur = names->count;
-	      if (old == names_alloca)
-		names_alloca = names;
-	      else
-		free (old);
-	    }
+	  globnames_array_free (&globnames);
 	  result = GLOB_NOSPACE;
 	}
       else
 	{
-	  while (1)
+	  struct globnames_result ret = { .names = 0, .length = -1 };
+	  if (!globnames_array_finalize (&globnames, &ret))
+	    result = GLOB_NOSPACE;
+	  else
 	    {
-	      struct globnames *old = names;
-	      size_t i;
-	      for (i = 0; i < cur; ++i)
+	      for (size_t i = 0; i < ret.length; ++i)
 		new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++]
-		  = names->name[i];
-	      names = names->next;
-	      /* NB: we will not leak memory here if we exit without
-		 freeing the current block assigned to OLD.  At least
-		 the very first block is always allocated on the stack
-		 and this is the block assigned to OLD here.  */
-	      if (names == NULL)
-		{
-		  assert (old == &init_names);
-		  break;
-		}
-	      cur = names->count;
-	      if (old == names_alloca)
-		names_alloca = names;
-	      else
-		free (old);
-	    }
-
-	  pglob->gl_pathv = new_gl_pathv;
+		  = ret.names[i];
 
-	  pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
-
-	  pglob->gl_flags = flags;
+	      pglob->gl_pathv = new_gl_pathv;
+	      pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+	      pglob->gl_flags = flags;
+	    }
+	  free (ret.names);
 	}
     }
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 03/18] posix: Consolidate glob implementation
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (7 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 01/18] posix: Sync glob with gnulib [BZ #1062] Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 16/18] posix: More check for overflow allocation in glob Adhemerval Zanella
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch consolidates the glob implementation.  The main changes are:

  * Remove specific defines required for multiple compilation in same
    unit (GLOB_ONLY_P, NO_GLOB_PATTERN_P and GLOB_COMPAT_BUILD).  To allow
    using the same code to build compat version on Linux, extra units are
    used instead (oldglob.c).

  * Both globfree and GNU extension glob_pattern_p are now on their own
    files.  This simplifies the creation of compat symbol when required.

  * Also similar to glob/glob64, a new globfree64 is file is added with an
    empty implementation as default.

  * On Linux all implementation now uses a default one with the exception
    of alpha (which requires specific versioning) and s390-32 (which
    different than other 32 bits with support for v2.1 symbol does not
    add a compat one).

  * Move i386 olddirent.h header to Linux default directory, since it is
    the only header with this name and it is shared among different
    architectures (and used on compat glob symbol as well).

Checked on x86_64-linux-gnu and i686-linux-gnu.

	* posix/Makefile (routines): Add globfree, globfree64, and
	glob_pattern_p.
	* posix/glob.c: Remove GLOB_ONLY_P, GLOB_COMPAT_BUILD, and
	NO_GLOB_PATTERN_P define usage.
	(globfree): Move to its own file.
	(__glob_pattern_type): Likewise.
	(__glob_pattern_p): Likewise.
	* posix/glob_internal.h: New file.
	* posix/glob_pattern_p.c: Likewise.
	* posix/globfree.c: Likewise.
	* posix/globfree64.c: Likewise.
	* sysdeps/gnu/glob64.c: Remove file.
	* sysdeps/unix/sysv/linux/Makefile (sysdep_routines): Add oldglob.
	* sysdeps/unix/sysv/linux/alpha/Makefile [$(subdir) = posix]
	(sysdep_routines): Remove rule.
	* sysdeps/unix/sysv/linux/alpha/glob.c: Remove file.
	* sysdeps/unix/sysv/linux/arm/glob64.c: Likewise.
	* sysdeps/wordsize-64/glob.c: Likewise.
	* sysdeps/unix/sysv/linux/m68k/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/wordsize-64/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/glob.c: Likewise.
	* sysdeps/wordsize-64/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/alpha/glob64.c: New file.
	* sysdeps/unix/sysv/linux/alpha/globfree.c: Likewise.
	* sysdeps/unix/sysv/linux/glob.c: Likewise.
	* sysdeps/unix/sysv/linux/glob64.c: Likewise.
	* sysdeps/unix/sysv/linux/globfree.c: Likewise.
	* sysdeps/unix/sysv/linux/globfree64.c: Likewise.
	* sysdeps/unix/sysv/linux/i386/alphasort64.c: include olddirent.h
	using relative path instead of absolute one.
	* sysdeps/unix/sysv/linux/i386/getdents64.c: Likewise.
	* sysdeps/unix/sysv/linux/i386/readdir64.c: Likewise.
	* sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise.
	* sysdeps/unix/sysv/linux/i386/versionsort64.c: Likewise.
	* sysdeps/unix/sysv/linux/i386/olddirent.h: Move to ...
	* sysdeps/unix/sysv/linux/olddirent.h: ... here.
	* sysdeps/unix/sysv/linux/i386/glob64.c: Move to ...
	* sysdeps/unix/sysv/linux/oldglob.c: ... here.
	* sysdeps/unix/sysv/linux/i386/glob64.c: Remove file.
	* sysdeps/unix/sysv/linux/oldglob.c: New file.
	* sysdeps/unix/sysv/linux/s390/s390-32/glob64.c: New file.
	* sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c: Likewise.
---
 posix/Makefile                                     |  2 +-
 posix/glob.c                                       | 90 ++--------------------
 posix/glob_internal.h                              | 57 ++++++++++++++
 posix/glob_pattern_p.c                             | 29 +++++++
 posix/globfree.c                                   | 37 +++++++++
 posix/globfree64.c                                 | 27 +++++++
 sysdeps/gnu/glob64.c                               | 25 ------
 sysdeps/unix/sysv/linux/Makefile                   |  2 +-
 sysdeps/unix/sysv/linux/alpha/Makefile             |  4 -
 sysdeps/unix/sysv/linux/alpha/{glob.c => glob64.c} | 11 ---
 sysdeps/unix/sysv/linux/alpha/globfree.c           | 36 +++++++++
 sysdeps/unix/sysv/linux/arm/glob64.c               |  1 -
 sysdeps/unix/sysv/linux/glob.c                     | 23 ++++++
 sysdeps/unix/sysv/linux/glob64.c                   | 55 +++++++++++++
 sysdeps/unix/sysv/linux/globfree.c                 | 23 ++++++
 sysdeps/unix/sysv/linux/globfree64.c               |  0
 sysdeps/unix/sysv/linux/i386/alphasort64.c         |  2 +-
 sysdeps/unix/sysv/linux/i386/getdents64.c          |  2 +-
 sysdeps/unix/sysv/linux/i386/readdir64.c           |  2 +-
 sysdeps/unix/sysv/linux/i386/readdir64_r.c         |  2 +-
 sysdeps/unix/sysv/linux/i386/versionsort64.c       |  2 +-
 sysdeps/unix/sysv/linux/m68k/glob64.c              |  1 -
 sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c   |  1 -
 sysdeps/unix/sysv/linux/{i386 => }/olddirent.h     |  0
 .../unix/sysv/linux/{i386/glob64.c => oldglob.c}   | 53 +++----------
 sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c |  1 -
 sysdeps/unix/sysv/linux/s390/s390-32/glob64.c      | 20 +++++
 sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c     |  2 +
 sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c     |  1 -
 sysdeps/unix/sysv/linux/wordsize-64/glob64.c       |  2 -
 sysdeps/unix/sysv/linux/x86_64/x32/glob.c          |  1 -
 sysdeps/wordsize-64/glob.c                         |  8 --
 sysdeps/wordsize-64/glob64.c                       |  1 -
 33 files changed, 335 insertions(+), 188 deletions(-)
 create mode 100644 posix/glob_internal.h
 create mode 100644 posix/glob_pattern_p.c
 create mode 100644 posix/globfree.c
 create mode 100644 posix/globfree64.c
 delete mode 100644 sysdeps/gnu/glob64.c
 rename sysdeps/unix/sysv/linux/alpha/{glob.c => glob64.c} (78%)
 create mode 100644 sysdeps/unix/sysv/linux/alpha/globfree.c
 delete mode 100644 sysdeps/unix/sysv/linux/arm/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/glob.c
 create mode 100644 sysdeps/unix/sysv/linux/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/globfree.c
 create mode 100644 sysdeps/unix/sysv/linux/globfree64.c
 delete mode 100644 sysdeps/unix/sysv/linux/m68k/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c
 rename sysdeps/unix/sysv/linux/{i386 => }/olddirent.h (100%)
 rename sysdeps/unix/sysv/linux/{i386/glob64.c => oldglob.c} (61%)
 delete mode 100644 sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/s390/s390-32/glob64.c
 create mode 100644 sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c
 delete mode 100644 sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/wordsize-64/glob64.c
 delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/glob.c
 delete mode 100644 sysdeps/wordsize-64/glob.c
 delete mode 100644 sysdeps/wordsize-64/glob64.c

diff --git a/posix/Makefile b/posix/Makefile
index 5ebbc4e..8340549 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -45,7 +45,7 @@ routines :=								      \
 	getpgid setpgid getpgrp bsd-getpgrp setpgrp getsid setsid	      \
 	getresuid getresgid setresuid setresgid				      \
 	pathconf sysconf fpathconf					      \
-	glob glob64 fnmatch regex					      \
+	glob glob64 globfree globfree64 glob_pattern_p fnmatch regex	      \
 	confstr								      \
 	getopt getopt1 							      \
 	sched_setp sched_getp sched_sets sched_gets sched_yield sched_primax  \
diff --git a/posix/glob.c b/posix/glob.c
index 5e4b7fc..dcfbc78 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -37,8 +37,6 @@
 
 #include <stdio.h>		/* Needed on stupid SunOS for assert.  */
 
-#ifndef GLOB_ONLY_P
-
 #include <unistd.h>
 #if !defined POSIX && defined _POSIX_VERSION
 # define POSIX
@@ -90,6 +88,8 @@
 
 #include <fnmatch.h>
 
+#include <glob_internal.h>
+
 #ifdef _SC_GETPW_R_SIZE_MAX
 # define GETPW_R_SIZE_MAX()	sysconf (_SC_GETPW_R_SIZE_MAX)
 #else
@@ -168,8 +168,6 @@ readdir_result_might_be_dir (struct readdir_result d)
     D_INO_TO_RESULT (source)		   \
   }
 
-#endif /* !defined GLOB_ONLY_P */
-
 /* Call gl_readdir on STREAM.  This macro can be overridden to reduce
    type safety if an old interface version needs to be supported.  */
 #ifndef GL_READDIR
@@ -252,7 +250,6 @@ static bool size_add_wrapv (size_t a, size_t b, size_t *r);
 static bool glob_use_alloca (size_t alloca_used, size_t len);
 
 /* We must not compile this function twice.  */
-#ifndef GLOB_COMPAT_BUILD
 static bool
 size_add_wrapv (size_t a, size_t b, size_t *r)
 {
@@ -271,7 +268,6 @@ glob_use_alloca (size_t alloca_used, size_t len)
   return (!size_add_wrapv (alloca_used, len, &size)
 	  && __libc_use_alloca (size));
 }
-#endif
 
 static int glob_in_dir (const char *pattern, const char *directory,
 			int flags, int (*errfunc) (const char *, int),
@@ -279,7 +275,6 @@ static int glob_in_dir (const char *pattern, const char *directory,
 extern int __glob_pattern_type (const char *pattern, int quote)
     attribute_hidden;
 
-#ifndef GLOB_ONLY_P
 static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL;
 static int collated_compare (const void *, const void *) __THROWNL;
 
@@ -308,7 +303,6 @@ next_brace_sub (const char *cp, int flags)
   return *cp != '\0' ? cp : NULL;
 }
 
-#endif /* !defined GLOB_ONLY_P */
 
 /* Do glob searching for PATTERN, placing results in PGLOB.
    The bits defined above may be set in FLAGS.
@@ -1372,26 +1366,6 @@ libc_hidden_def (glob)
 #endif
 
 
-#ifndef GLOB_ONLY_P
-
-/* Free storage allocated in PGLOB by a previous `glob' call.  */
-void
-globfree (glob_t *pglob)
-{
-  if (pglob->gl_pathv != NULL)
-    {
-      size_t i;
-      for (i = 0; i < pglob->gl_pathc; ++i)
-	free (pglob->gl_pathv[pglob->gl_offs + i]);
-      free (pglob->gl_pathv);
-      pglob->gl_pathv = NULL;
-    }
-}
-#if defined _LIBC && !defined globfree
-libc_hidden_def (globfree)
-#endif
-
-
 /* Do a collated comparison of A and B.  */
 static int
 collated_compare (const void *a, const void *b)
@@ -1467,71 +1441,23 @@ prefix_array (const char *dirname, char **array, size_t n)
   return 0;
 }
 
-
-/* We must not compile this function twice.  */
-#ifndef NO_GLOB_PATTERN_P
-int
-__glob_pattern_type (const char *pattern, int quote)
-{
-  const char *p;
-  int ret = 0;
-
-  for (p = pattern; *p != '\0'; ++p)
-    switch (*p)
-      {
-      case '?':
-      case '*':
-	return 1;
-
-      case '\\':
-	if (quote)
-	  {
-	    if (p[1] != '\0')
-	      ++p;
-	    ret |= 2;
-	  }
-	break;
-
-      case '[':
-	ret |= 4;
-	break;
-
-      case ']':
-	if (ret & 4)
-	  return 1;
-	break;
-      }
-
-  return ret;
-}
-
-/* Return nonzero if PATTERN contains any metacharacters.
-   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
-int
-__glob_pattern_p (const char *pattern, int quote)
-{
-  return __glob_pattern_type (pattern, quote) == 1;
-}
-# ifdef _LIBC
-weak_alias (__glob_pattern_p, glob_pattern_p)
-# endif
-#endif
-
-
 /* We put this in a separate function mainly to allow the memory
    allocated with alloca to be recycled.  */
 static int
 __attribute_noinline__
 link_stat (const char *dir, size_t dirlen, const char *fname,
 	       glob_t *pglob
-# if !defined _LIBC && !HAVE_FSTATAT
+# ifndef _LIBC
 		, int flags
 # endif
 		)
 {
   size_t fnamelen = strlen (fname);
-  char *fullname = __alloca (dirlen + 1 + fnamelen + 1);
+  char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1);
   struct stat st;
+# ifndef _LIBC
+  struct_stat64 st64;
+# endif
 
   mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1),
 	   fname, fnamelen + 1);
@@ -1567,8 +1493,6 @@ link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname,
 # endif
   return status == 0 || errno == EOVERFLOW;
 }
-#endif /* !defined GLOB_ONLY_P */
-
 
 /* Like `glob', but PATTERN is a final pathname component,
    and matches are searched for in DIRECTORY.
diff --git a/posix/glob_internal.h b/posix/glob_internal.h
new file mode 100644
index 0000000..d989a98
--- /dev/null
+++ b/posix/glob_internal.h
@@ -0,0 +1,57 @@
+/* Shared definition for glob and glob_pattern_p.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GLOB_INTERNAL_H
+# define GLOB_INTERNAL_H
+
+static inline int
+__glob_pattern_type (const char *pattern, int quote)
+{
+  const char *p;
+  int ret = 0;
+
+  for (p = pattern; *p != '\0'; ++p)
+    switch (*p)
+      {
+      case '?':
+      case '*':
+	return 1;
+
+      case '\\':
+	if (quote)
+	  {
+	    if (p[1] != '\0')
+	      ++p;
+	    ret |= 2;
+	  }
+	break;
+
+      case '[':
+	ret |= 4;
+	break;
+
+      case ']':
+	if (ret & 4)
+	  return 1;
+	break;
+      }
+
+  return ret;
+}
+
+#endif /* GLOB_INTERNAL_H  */
diff --git a/posix/glob_pattern_p.c b/posix/glob_pattern_p.c
new file mode 100644
index 0000000..6e451f2
--- /dev/null
+++ b/posix/glob_pattern_p.c
@@ -0,0 +1,29 @@
+/* Return nonzero if PATTERN contains any metacharacters.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <glob.h>
+#include "glob_internal.h"
+
+/* Return nonzero if PATTERN contains any metacharacters.
+   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
+int
+__glob_pattern_p (const char *pattern, int quote)
+{
+  return __glob_pattern_type (pattern, quote) == 1;
+}
+weak_alias (__glob_pattern_p, glob_pattern_p)
diff --git a/posix/globfree.c b/posix/globfree.c
new file mode 100644
index 0000000..4ebd142
--- /dev/null
+++ b/posix/globfree.c
@@ -0,0 +1,37 @@
+/* Frees the dynamically allocated storage from an earlier call to glob.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <glob.h>
+#include <stdlib.h>
+
+/* Free storage allocated in PGLOB by a previous `glob' call.  */
+void
+globfree (glob_t *pglob)
+{
+  if (pglob->gl_pathv != NULL)
+    {
+      size_t i;
+      for (i = 0; i < pglob->gl_pathc; ++i)
+	free (pglob->gl_pathv[pglob->gl_offs + i]);
+      free (pglob->gl_pathv);
+      pglob->gl_pathv = NULL;
+    }
+}
+#ifndef globfree
+libc_hidden_def (globfree)
+#endif
diff --git a/posix/globfree64.c b/posix/globfree64.c
new file mode 100644
index 0000000..cce288e
--- /dev/null
+++ b/posix/globfree64.c
@@ -0,0 +1,27 @@
+/* Frees the dynamically allocated storage from an earlier call to glob.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <glob.h>
+#include <stdlib.h>
+
+/* Free storage allocated in PGLOB by a previous `glob' call.  */
+void
+globfree64 (glob64_t *pglob)
+{
+}
+libc_hidden_def (globfree64)
diff --git a/sysdeps/gnu/glob64.c b/sysdeps/gnu/glob64.c
deleted file mode 100644
index d1e4e6f..0000000
--- a/sysdeps/gnu/glob64.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <dirent.h>
-#include <glob.h>
-#include <sys/stat.h>
-
-#define dirent dirent64
-#define __readdir(dirp) __readdir64 (dirp)
-
-#define glob_t glob64_t
-#define glob(pattern, flags, errfunc, pglob) \
-  glob64 (pattern, flags, errfunc, pglob)
-#define globfree(pglob) globfree64 (pglob)
-
-#undef stat
-#define stat stat64
-#undef __stat
-#define __stat(file, buf) __xstat64 (_STAT_VER, file, buf)
-
-#define NO_GLOB_PATTERN_P 1
-
-#define COMPILE_GLOB64	1
-
-#include <posix/glob.c>
-
-libc_hidden_def (glob64)
-libc_hidden_def (globfree64)
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 9d6a2de..8847751 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -162,7 +162,7 @@ endif
 ifeq ($(subdir),posix)
 sysdep_headers += bits/initspin.h
 
-sysdep_routines += sched_getcpu
+sysdep_routines += sched_getcpu oldglob
 
 tests += tst-affinity tst-affinity-pid
 
diff --git a/sysdeps/unix/sysv/linux/alpha/Makefile b/sysdeps/unix/sysv/linux/alpha/Makefile
index 47bd189..50f4fb1 100644
--- a/sysdeps/unix/sysv/linux/alpha/Makefile
+++ b/sysdeps/unix/sysv/linux/alpha/Makefile
@@ -1,7 +1,3 @@
-ifeq ($(subdir),posix)
-sysdep_routines += oldglob
-endif
-
 ifeq ($(subdir),stdlib)
 gen-as-const-headers += ucontext-offsets.sym
 endif
diff --git a/sysdeps/unix/sysv/linux/alpha/glob.c b/sysdeps/unix/sysv/linux/alpha/glob64.c
similarity index 78%
rename from sysdeps/unix/sysv/linux/alpha/glob.c
rename to sysdeps/unix/sysv/linux/alpha/glob64.c
index 2d7d287..dd86d15 100644
--- a/sysdeps/unix/sysv/linux/alpha/glob.c
+++ b/sysdeps/unix/sysv/linux/alpha/glob64.c
@@ -16,36 +16,25 @@
    <http://www.gnu.org/licenses/>.  */
 
 #define glob64 __no_glob64_decl
-#define globfree64 __no_globfree64_decl
 
-#include <sys/types.h>
 #include <glob.h>
 #include <shlib-compat.h>
 
 /* For Linux/Alpha we have to make the glob symbols versioned.  */
 #define glob(pattern, flags, errfunc, pglob) \
   __new_glob (pattern, flags, errfunc, pglob)
-#define globfree(pglob) \
-  __new_globfree (pglob)
 
 /* We need prototypes for these new names.  */
 extern int __new_glob (const char *__pattern, int __flags,
 		       int (*__errfunc) (const char *, int),
 		       glob_t *__pglob);
-extern void __new_globfree (glob_t *__pglob);
 
 #include <posix/glob.c>
 
 #undef glob
-#undef globfree
 #undef glob64
-#undef globfree64
 
 versioned_symbol (libc, __new_glob, glob, GLIBC_2_1);
-versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1);
 libc_hidden_ver (__new_glob, glob)
-libc_hidden_ver (__new_globfree, globfree)
 
 weak_alias (__new_glob, glob64)
-weak_alias (__new_globfree, globfree64)
-libc_hidden_ver (__new_globfree, globfree64)
diff --git a/sysdeps/unix/sysv/linux/alpha/globfree.c b/sysdeps/unix/sysv/linux/alpha/globfree.c
new file mode 100644
index 0000000..9d159f1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/alpha/globfree.c
@@ -0,0 +1,36 @@
+/* Frees the dynamically allocated storage from an earlier call to glob.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define globfree64 __no_globfree64_decl
+
+#include <glob.h>
+#include <shlib-compat.h>
+
+#define globfree(pglob) __new_globfree (pglob)
+extern void __new_globfree (glob_t *__pglob);
+
+#include <posix/globfree.c>
+
+#undef globfree
+#undef globfree64
+
+versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1);
+libc_hidden_ver (__new_globfree, globfree)
+
+weak_alias (__new_globfree, globfree64)
+libc_hidden_ver (__new_globfree, globfree64)
diff --git a/sysdeps/unix/sysv/linux/arm/glob64.c b/sysdeps/unix/sysv/linux/arm/glob64.c
deleted file mode 100644
index 82a9a29..0000000
--- a/sysdeps/unix/sysv/linux/arm/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-#include <sysdeps/unix/sysv/linux/i386/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/glob.c b/sysdeps/unix/sysv/linux/glob.c
new file mode 100644
index 0000000..153ba8c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/glob.c
@@ -0,0 +1,23 @@
+/* Find pathnames matching a pattern.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+
+#ifndef __OFF_T_MATCHES_OFF64_T
+# include <posix/glob.c>
+#endif
diff --git a/sysdeps/unix/sysv/linux/glob64.c b/sysdeps/unix/sysv/linux/glob64.c
new file mode 100644
index 0000000..6ff6611
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/glob64.c
@@ -0,0 +1,55 @@
+/* Find pathnames matching a pattern.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+
+#ifdef __OFF_T_MATCHES_OFF64_T
+# define glob64 __no_glob64_decl
+# include <posix/glob.c>
+# undef glob64
+weak_alias (glob, glob64)
+#else
+# include <glob.h>
+# include <dirent.h>
+# include <sys/stat.h>
+
+# define dirent dirent64
+# define __readdir(dirp) __readdir64 (dirp)
+
+# define glob_t glob64_t
+# define glob(pattern, flags, errfunc, pglob) \
+  __glob64 (pattern, flags, errfunc, pglob)
+# define globfree(pglob) globfree64 (pglob)
+
+# undef stat
+# define stat stat64
+
+# define COMPILE_GLOB64	1
+
+# include <posix/glob.c>
+
+# include "shlib-compat.h"
+
+# ifdef GLOB_NO_OLD_VERSION
+strong_alias (__glob64, glob64)
+libc_hidden_def (glob64)
+# else
+versioned_symbol (libc, __glob64, glob64, GLIBC_2_2);
+libc_hidden_ver (__glob64, glob64)
+# endif
+#endif
diff --git a/sysdeps/unix/sysv/linux/globfree.c b/sysdeps/unix/sysv/linux/globfree.c
new file mode 100644
index 0000000..d615510
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/globfree.c
@@ -0,0 +1,23 @@
+/* Frees the dynamically allocated storage from an earlier call to glob.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define globfree64 __no_globfree64_decl
+#include <posix/globfree.c>
+#undef globfree64
+weak_alias (globfree, globfree64)
+libc_hidden_ver (globfree, globfree64)
diff --git a/sysdeps/unix/sysv/linux/globfree64.c b/sysdeps/unix/sysv/linux/globfree64.c
new file mode 100644
index 0000000..e69de29
diff --git a/sysdeps/unix/sysv/linux/i386/alphasort64.c b/sysdeps/unix/sysv/linux/i386/alphasort64.c
index d5fd47a..04b29b6 100644
--- a/sysdeps/unix/sysv/linux/i386/alphasort64.c
+++ b/sysdeps/unix/sysv/linux/i386/alphasort64.c
@@ -30,7 +30,7 @@ versioned_symbol (libc, __alphasort64, alphasort64, GLIBC_2_2);
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 int
 __old_alphasort64 (const struct __old_dirent64 **a,
diff --git a/sysdeps/unix/sysv/linux/i386/getdents64.c b/sysdeps/unix/sysv/linux/i386/getdents64.c
index e8b257f..2010bbf 100644
--- a/sysdeps/unix/sysv/linux/i386/getdents64.c
+++ b/sysdeps/unix/sysv/linux/i386/getdents64.c
@@ -28,7 +28,7 @@
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 #define __GETDENTS __old_getdents64
 #define DIRENT_TYPE struct __old_dirent64
diff --git a/sysdeps/unix/sysv/linux/i386/readdir64.c b/sysdeps/unix/sysv/linux/i386/readdir64.c
index f80b6a7..bd2375f 100644
--- a/sysdeps/unix/sysv/linux/i386/readdir64.c
+++ b/sysdeps/unix/sysv/linux/i386/readdir64.c
@@ -31,7 +31,7 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 #define __READDIR attribute_compat_text_section __old_readdir64
 #define __GETDENTS __old_getdents64
diff --git a/sysdeps/unix/sysv/linux/i386/readdir64_r.c b/sysdeps/unix/sysv/linux/i386/readdir64_r.c
index 344fd53..8c0262d 100644
--- a/sysdeps/unix/sysv/linux/i386/readdir64_r.c
+++ b/sysdeps/unix/sysv/linux/i386/readdir64_r.c
@@ -31,7 +31,7 @@ versioned_symbol (libc, __readdir64_r, readdir64_r, GLIBC_2_2);
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 #define __READDIR_R attribute_compat_text_section __old_readdir64_r
 #define __GETDENTS __old_getdents64
diff --git a/sysdeps/unix/sysv/linux/i386/versionsort64.c b/sysdeps/unix/sysv/linux/i386/versionsort64.c
index 3e1c6ea..87f2f95 100644
--- a/sysdeps/unix/sysv/linux/i386/versionsort64.c
+++ b/sysdeps/unix/sysv/linux/i386/versionsort64.c
@@ -30,7 +30,7 @@ versioned_symbol (libc, __versionsort64, versionsort64, GLIBC_2_2);
 
 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 int
 __old_versionsort64 (const struct __old_dirent64 **a,
diff --git a/sysdeps/unix/sysv/linux/m68k/glob64.c b/sysdeps/unix/sysv/linux/m68k/glob64.c
deleted file mode 100644
index 82a9a29..0000000
--- a/sysdeps/unix/sysv/linux/m68k/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-#include <sysdeps/unix/sysv/linux/i386/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c
deleted file mode 100644
index 33918ea..0000000
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-/* glob64 is in glob.c */
diff --git a/sysdeps/unix/sysv/linux/i386/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
similarity index 100%
rename from sysdeps/unix/sysv/linux/i386/olddirent.h
rename to sysdeps/unix/sysv/linux/olddirent.h
diff --git a/sysdeps/unix/sysv/linux/i386/glob64.c b/sysdeps/unix/sysv/linux/oldglob.c
similarity index 61%
rename from sysdeps/unix/sysv/linux/i386/glob64.c
rename to sysdeps/unix/sysv/linux/oldglob.c
index 9c7abd8..af934f9 100644
--- a/sysdeps/unix/sysv/linux/i386/glob64.c
+++ b/sysdeps/unix/sysv/linux/oldglob.c
@@ -1,5 +1,5 @@
-/* Two glob variants with 64-bit support, for dirent64 and __olddirent64.
-   Copyright (C) 1998-2017 Free Software Foundation, Inc.
+/* Find pathnames matching a pattern.  Compatibility version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,61 +16,32 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#include <dirent.h>
 #include <glob.h>
-#include <sys/stat.h>
-
-#define dirent dirent64
-#define __readdir(dirp) __readdir64 (dirp)
-
-#define glob_t glob64_t
-#define glob(pattern, flags, errfunc, pglob) \
-  __glob64 (pattern, flags, errfunc, pglob)
-#define globfree(pglob) globfree64 (pglob)
-
-#undef stat
-#define stat stat64
-#undef __stat
-#define __stat(file, buf) __xstat64 (_STAT_VER, file, buf)
-
-#define NO_GLOB_PATTERN_P 1
-
-#define COMPILE_GLOB64	1
-
-#include <posix/glob.c>
-
 #include "shlib-compat.h"
 
-libc_hidden_def (globfree64)
-
-versioned_symbol (libc, __glob64, glob64, GLIBC_2_2);
-libc_hidden_ver (__glob64, glob64)
-
-#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) \
+    && !defined(GLOB_NO_OLD_VERSION)
 
-#include <sysdeps/unix/sysv/linux/i386/olddirent.h>
+#include <olddirent.h>
 
 int __old_glob64 (const char *__pattern, int __flags,
 		  int (*__errfunc) (const char *, int),
 		  glob64_t *__pglob);
 
-#undef dirent
+#define glob_t glob64_t
+#define globfree(pglob) globfree64 (pglob)
+#undef stat
+#define stat stat64
+
 #define dirent __old_dirent64
-#undef GL_READDIR
-# define GL_READDIR(pglob, stream) \
+#define GL_READDIR(pglob, stream) \
   ((struct __old_dirent64 *) (pglob)->gl_readdir (stream))
-#undef __readdir
 #define __readdir(dirp) __old_readdir64 (dirp)
-#undef glob
 #define glob(pattern, flags, errfunc, pglob) \
   __old_glob64 (pattern, flags, errfunc, pglob)
 #define convert_dirent __old_convert_dirent
-#define glob_in_dir __old_glob_in_dir
-#define GLOB_ATTRIBUTE attribute_compat_text_section
 
-#define GLOB_ONLY_P 1
-
-#define GLOB_COMPAT_BUILD 1
+#define GLOB_ATTRIBUTE attribute_compat_text_section
 
 #include <posix/glob.c>
 
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c b/sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c
deleted file mode 100644
index 82a9a29..0000000
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-#include <sysdeps/unix/sysv/linux/i386/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/glob64.c b/sysdeps/unix/sysv/linux/s390/s390-32/glob64.c
new file mode 100644
index 0000000..b00dc54
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/glob64.c
@@ -0,0 +1,20 @@
+/* Find pathnames matching a pattern.  S390-32 Linux version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define GLOB_NO_OLD_VERSION
+#include <sysdeps/unix/sysv/linux/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c b/sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c
new file mode 100644
index 0000000..56d7d12
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/oldglob.c
@@ -0,0 +1,2 @@
+#define GLOB_NO_OLD_VERSION
+#include <sysdeps/unix/sysv/linux/oldglob.c>
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c b/sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c
deleted file mode 100644
index 82a9a29..0000000
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-#include <sysdeps/unix/sysv/linux/i386/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/wordsize-64/glob64.c b/sysdeps/unix/sysv/linux/wordsize-64/glob64.c
deleted file mode 100644
index eab7703..0000000
--- a/sysdeps/unix/sysv/linux/wordsize-64/glob64.c
+++ /dev/null
@@ -1,2 +0,0 @@
-/* This file is here so sysdeps/gnu/glob64.c doesn't take precedence.  */
-#include <sysdeps/wordsize-64/glob64.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/glob.c b/sysdeps/unix/sysv/linux/x86_64/x32/glob.c
deleted file mode 100644
index e542747..0000000
--- a/sysdeps/unix/sysv/linux/x86_64/x32/glob.c
+++ /dev/null
@@ -1 +0,0 @@
-#include <sysdeps/wordsize-64/glob.c>
diff --git a/sysdeps/wordsize-64/glob.c b/sysdeps/wordsize-64/glob.c
deleted file mode 100644
index 082faf1..0000000
--- a/sysdeps/wordsize-64/glob.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#define glob64 __no_glob64_decl
-#define globfree64 __no_globfree64_decl
-#include <posix/glob.c>
-#undef glob64
-#undef globfree64
-weak_alias (glob, glob64)
-weak_alias (globfree, globfree64)
-libc_hidden_ver (globfree, globfree64)
diff --git a/sysdeps/wordsize-64/glob64.c b/sysdeps/wordsize-64/glob64.c
deleted file mode 100644
index 33918ea..0000000
--- a/sysdeps/wordsize-64/glob64.c
+++ /dev/null
@@ -1 +0,0 @@
-/* glob64 is in glob.c */
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 13/18] posix: Remove all alloca usage in glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (4 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 12/18] posix: Use dynarray for globname " Adhemerval Zanella
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

With alloca usage removal from glob this patch wraps it up by removing
all the alloca defines and macros usage.

Checked on x86_64-linux-gnu.

	posix/glob.c (glob_in_dir): Remove alloca_used argument.
	(glob): Remove alloca_used.
---
 posix/glob.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index f40ee6d..67196fd 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -59,7 +59,6 @@
 #include <dirent.h>
 #include <stdlib.h>
 #include <string.h>
-#include <alloca.h>
 
 #ifdef _LIBC
 # undef strdup
@@ -230,17 +229,11 @@ convert_dirent64 (const struct dirent64 *source)
 # ifdef GNULIB_defined_closedir
 #  undef closedir
 # endif
-
-/* Just use malloc.  */
-# define __libc_use_alloca(n) false
-# define alloca_account(len, avar) ((void) (len), (void) (avar), (void *) 0)
-# define extend_alloca_account(buf, len, newlen, avar) \
-    ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0)
 #endif
 
 static int glob_in_dir (const char *pattern, const char *directory,
 			int flags, int (*errfunc) (const char *, int),
-			glob_t *pglob, size_t alloca_used);
+			glob_t *pglob);
 extern int __glob_pattern_type (const char *pattern, int quote)
     attribute_hidden;
 
@@ -296,7 +289,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
   bool dirname_modified;
   glob_t dirs;
   int retval = 0;
-  size_t alloca_used = 0;
   struct char_array dirname;
 
   if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
@@ -974,7 +966,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  status = glob_in_dir (filename, dirs.gl_pathv[i],
 				((flags | GLOB_APPEND)
 				 & ~(GLOB_NOCHECK | GLOB_NOMAGIC)),
-				errfunc, pglob, alloca_used);
+				errfunc, pglob);
 	  if (status == GLOB_NOMATCH)
 	    /* No matches in this directory.  Try the next.  */
 	    continue;
@@ -1081,7 +1073,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
       if (dirname_modified)
 	flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
       status = glob_in_dir (filename, char_array_str (&dirname), flags,
-			    errfunc, pglob, alloca_used);
+			    errfunc, pglob);
       if (status != 0)
 	{
 	  if (status == GLOB_NOMATCH && flags != orig_flags
@@ -1258,8 +1250,7 @@ struct globnames_result
    The GLOB_APPEND flag is assumed to be set (always appends).  */
 static int
 glob_in_dir (const char *pattern, const char *directory, int flags,
-	     int (*errfunc) (const char *, int),
-	     glob_t *pglob, size_t alloca_used)
+	     int (*errfunc) (const char *, int), glob_t *pglob)
 {
   void *stream = NULL;
   struct globnames_array globnames;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 08/18] malloc: Add specialized dynarray for C strings
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (2 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866] Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-17 10:12   ` Florian Weimer
  2017-08-11 14:51 ` [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca Adhemerval Zanella
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch adds an specialized dynarray to manage C strings using the
dynarray internal implementation.  It uses some private fields from
dynarray and thus it provided specific files to access and manage
the internal string buffer.

   For instance:

   struct char_array str;

   // str == "testing"
   char_array_init_str (&str, "testing");

   // c == 's'
   char c = char_array_pos (&str, 2);

   // str = "testing2"
   char_array_set_str (&str, "testing2");

   // str = "testig2"
   char_array_erase (&str, 5);

   // str = "123testig2";
   char_array_prepend_str (&str, "123");

   // len = 10;
   size_t len = char_array_length (&str);

   // str = "123testig2456";
   char_array_append_str (&str, "456");

   // str = "123test789";
   char_array_replace_str_pos (&str, 7, "789", 3);

The provided function are not extensive and meant mainly to be use in
subsequent glob implementation cleanup.  For internal object consistency
only the function provided by char_array.c should be used, including
internal object manipulation.

To check for possible overflows in internal size manipulation a new
function, check_add_wrapv_size_t, is added on malloc-internal.  It basically
return whether the addition of two size_t overflows.

Checked on x86_64-linux-gnu.

	* malloc/Makefile (test-internal): Add tst-char_array.
	(routines): Add dynarray_overflow_failure and char_array-impl.
	* malloc/Versions [GLIBC_PRIVATE] (libc): Add
	__libc_dynarray_overflow_failure, __char_array_set_str_size,
	__char_array_erase, __char_array_prepend_str_size, and
	__char_array_replace_str_pos.
	* malloc/char_array-impl.c: New file.
	* malloc/char_array-skeleton.c: Likewise.
	* malloc/char_array.h: Likewise.
	* malloc/tst-char-array.c: Likewise.
	* malloc/dynarray_overflow_failure.c: Likewise.
	* malloc/malloc-internal.h (check_add_overflow_size_t): New function.
---
 malloc/Makefile                    |   4 +-
 malloc/Versions                    |   7 +
 malloc/char_array-impl.c           |  57 ++++++++
 malloc/char_array-skeleton.c       | 271 +++++++++++++++++++++++++++++++++++++
 malloc/char_array.h                |  53 ++++++++
 malloc/dynarray.h                  |   9 ++
 malloc/dynarray_overflow_failure.c |  31 +++++
 malloc/malloc-internal.h           |  14 ++
 malloc/tst-char_array.c            | 110 +++++++++++++++
 9 files changed, 555 insertions(+), 1 deletion(-)
 create mode 100644 malloc/char_array-impl.c
 create mode 100644 malloc/char_array-skeleton.c
 create mode 100644 malloc/char_array.h
 create mode 100644 malloc/dynarray_overflow_failure.c
 create mode 100644 malloc/tst-char_array.c

diff --git a/malloc/Makefile b/malloc/Makefile
index 50b487e..a6e6ba9 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -47,6 +47,7 @@ tests-internal += \
 	 tst-dynarray \
 	 tst-dynarray-fail \
 	 tst-dynarray-at-fail \
+	 tst-char_array
 
 ifneq (no,$(have-tunables))
 tests += tst-malloc-usable-tunables
@@ -59,7 +60,7 @@ test-srcs = tst-mtrace
 routines = malloc morecore mcheck mtrace obstack reallocarray \
   scratch_buffer_grow scratch_buffer_grow_preserve \
   scratch_buffer_set_array_size \
-  dynarray_at_failure \
+  dynarray_at_failure dynarray_overflow_failure \
   dynarray_emplace_enlarge \
   dynarray_finalize \
   dynarray_resize \
@@ -69,6 +70,7 @@ routines = malloc morecore mcheck mtrace obstack reallocarray \
   alloc_buffer_copy_bytes  \
   alloc_buffer_copy_string \
   alloc_buffer_create_failure \
+  char_array-impl
 
 install-lib := libmcheck.a
 non-lib.a := libmcheck.a
diff --git a/malloc/Versions b/malloc/Versions
index 2357cff..b21fe59 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -81,6 +81,7 @@ libc {
 
     # dynarray support
     __libc_dynarray_at_failure;
+    __libc_dynarray_overflow_failure;
     __libc_dynarray_emplace_enlarge;
     __libc_dynarray_finalize;
     __libc_dynarray_resize;
@@ -92,5 +93,11 @@ libc {
     __libc_alloc_buffer_copy_bytes;
     __libc_alloc_buffer_copy_string;
     __libc_alloc_buffer_create_failure;
+
+    # char_array support
+    __char_array_set_str_size;
+    __char_array_erase;
+    __char_array_prepend_str_size;
+    __char_array_replace_str_pos;
   }
 }
diff --git a/malloc/char_array-impl.c b/malloc/char_array-impl.c
new file mode 100644
index 0000000..5a56bbc
--- /dev/null
+++ b/malloc/char_array-impl.c
@@ -0,0 +1,57 @@
+/* Specialized dynarray for C strings.  Implementation file.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <malloc/char_array.h>
+
+void
+__char_array_set_str_size (struct dynarray_header *header, const char *str,
+			   size_t size)
+{
+  *((char *) mempcpy (header->array, str, size)) = '\0';
+  header->used = size + 1;
+}
+libc_hidden_def (__char_array_set_str_size)
+
+void
+__char_array_erase (struct dynarray_header *header, size_t pos)
+{
+  char *ppos = header->array + pos;
+  char *lpos = header->array + header->used;
+  ptrdiff_t size = lpos - ppos;
+  memmove (ppos, ppos + 1, size);
+  header->used--;
+}
+libc_hidden_def (__char_array_erase)
+
+void
+__char_array_prepend_str_size (struct dynarray_header *header,
+			       const char *str, size_t size, size_t used)
+{
+  memmove (header->array + size, header->array, used);
+  memcpy (header->array, str, size);
+}
+libc_hidden_def (__char_array_prepend_str_size)
+
+void
+__char_array_replace_str_pos (struct dynarray_header *header, size_t pos,
+			      const char *str, size_t len)
+{
+  char *start = header->array + pos;
+  *(char *) mempcpy (start, str, len) = '\0';
+}
+libc_hidden_def (__char_array_replace_str_pos)
diff --git a/malloc/char_array-skeleton.c b/malloc/char_array-skeleton.c
new file mode 100644
index 0000000..ab59e5e
--- /dev/null
+++ b/malloc/char_array-skeleton.c
@@ -0,0 +1,271 @@
+/* Specialized dynarray for C strings.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This file provides a dynamic C string with an initial stack allocated
+   buffer.  Since it is based on dynarray, it provides dynamic size
+   expansion and heap usage for large strings.
+
+   The following parameters are optional:
+
+   CHAR_ARRAY_INITIAL_SIZE
+      The size of the statically allocated array (default is 256).  It will
+      be used to define DYNARRAY_INITIAL_SIZE.
+
+   The following functions are provided:
+
+   bool char_array_init_empty (struct char_array *);
+   bool char_array_init_str (struct char_array *, const char *);
+   bool char_array_init_str_size (struct char_array *, const char *, size_t);
+   bool char_array_is_empty (struct char_array *);
+   const char *char_array_str (struct char_array *);
+   char char_array_pos (struct char_array *, size_t);
+   size_t char_array_length (struct char_array *);
+   bool char_array_set_str (struct char_array *, const char *);
+   bool char_array_set_str_size (struct char_array *, const char *, size_t);
+   void char_array_erase (struct char_array *, size_t);
+   bool char_array_crop (struct char_array *, size_t);
+   bool char_array_prepend_str (struct char_array *, const char *);
+   bool char_array_append_str (struct char_array *, const char *);
+   bool char_array_replace_str_pos (struct char_array *, size_t, const char *,
+				    size_t);
+
+   For instance:
+
+   struct char_array str;
+
+   // str == "testing"
+   char_array_init_str (&str, "testing");
+
+   // c == 's'
+   char c = char_array_pos (&str, 2);
+
+   // str = "testing2"
+   char_array_set_str (&str, "testing2");
+
+   // str = "testig2"
+   char_array_erase (&str, 5);
+
+   // str = "123testig2";
+   char_array_prepend_str (&str, "123");
+
+   // len = 10;
+   size_t len = char_array_length (&str);
+
+   // str = "123testig2456";
+   char_array_append_str (&str, "456");
+
+   // str = "123test789";
+   char_array_replace_str_pos (&str, 7, "789", 3);
+ */
+
+#define DYNARRAY_STRUCT            char_array
+#define DYNARRAY_ELEMENT           char
+#define DYNARRAY_PREFIX            char_array_
+#ifndef CHAR_ARRAY_INITIAL_SIZE
+# define CHAR_ARRAY_INITIAL_SIZE 256
+#endif
+#define DYNARRAY_INITIAL_SIZE  CHAR_ARRAY_INITIAL_SIZE
+#include <malloc/dynarray-skeleton.c>
+
+#include <malloc/malloc-internal.h>
+#include <malloc/char_array.h>
+
+/* Return a const char for the internal C string handled by 'array'.  */
+__attribute__ ((unused, nonnull (1)))
+static const char *
+char_array_str (struct char_array *array)
+{
+  return char_array_begin (array);
+}
+
+/* Return the character at position 'pos' from the char_array 'array'.  */
+__attribute__ ((unused, nonnull (1)))
+static char
+char_array_pos (struct char_array *array, size_t pos)
+{
+  return *char_array_at (array, pos);
+}
+
+/* Calculate the length of the string, excluding the terminating null.  */
+__attribute__ ((unused, nonnull (1)))
+static size_t
+char_array_length (struct char_array *array)
+{
+  /* Exclude the final '\0'.  */
+  return array->dynarray_header.used - 1;
+}
+
+/* Copy up 'size' bytes from string 'str' to char_array 'array'.  A final
+   '\0' is appended in the char_array.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_set_str_size (struct char_array *array, const char *str,
+			 size_t size)
+{
+  size_t newsize;
+  if (check_add_overflow_size_t (size, 1, &newsize))
+    __libc_dynarray_overflow_failure (size, 1);
+
+  if (!char_array_resize (array, newsize))
+    return false;
+
+  __char_array_set_str_size (&array->dynarray_abstract, str, size);
+  return true;
+}
+
+/* Copy the contents of string 'str' to char_array 'array', including the
+   final '\0'.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_set_str (struct char_array *array, const char *str)
+{
+  return char_array_set_str_size (array, str, strlen (str));
+}
+
+/* Initialize the char_array 'array' and sets it to an empty string ("").  */
+__attribute__ ((unused, nonnull (1)))
+static bool
+char_array_init_empty (struct char_array *array)
+{
+  char_array_init (array);
+  return char_array_set_str (array, "");
+}
+
+/* Initialize the char_array 'array' and copy the content of string 'str'.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_init_str (struct char_array *array, const char *str)
+{
+  char_array_init (array);
+  return char_array_set_str (array, str);
+}
+
+/* Initialize the char_array 'array' and copy the content of string 'str'
+   up to 'size' characteres.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_init_str_size (struct char_array *array, const char *str,
+			  size_t size)
+{
+  char_array_init (array);
+  return char_array_set_str_size (array, str, size);
+}
+
+/* Return if the char_array contain any characteres.  */
+__attribute__ ((unused, nonnull (1)))
+static bool
+char_array_is_empty (struct char_array *array)
+{
+  return *char_array_begin (array) == '\0';
+}
+
+/* Remove the byte at position 'pos' from char_array 'array'.  The contents
+   are moved internally if the position is not at the end of the internal
+   buffer.  */
+__attribute__ ((unused, nonnull (1)))
+static bool
+char_array_erase (struct char_array *array, size_t pos)
+{
+  if (pos >= array->dynarray_header.used - 1)
+    return false;
+
+  __char_array_erase (&array->dynarray_abstract, pos);
+  return true;
+}
+
+/* Resize the char_array 'array' to size 'count' maintaining the ending
+   '\0' byte.  */
+__attribute__ ((unused, nonnull (1)))
+static bool
+char_array_crop (struct char_array *array, size_t size)
+{
+  if (size >= (array->dynarray_header.used - 1)
+      || !char_array_resize (array, size + 1))
+    return false;
+
+  array->dynarray_header.array[size] = '\0';
+  return true;
+}
+
+/* Prepend the contents of string 'str' to char_array 'array', including the
+   final '\0' byte.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_prepend_str (struct char_array *array, const char *str)
+{
+  size_t size = strlen (str);
+  /* Resizing the array might change its used elements and we need below
+     to correct copy the elements.  */
+  size_t used = array->dynarray_header.used;
+
+  size_t newsize;
+  if (check_add_overflow_size_t (used, size, &newsize))
+    __libc_dynarray_overflow_failure (used, size);
+
+  /* Make room for the string and copy it.  */
+  if (!char_array_resize (array, newsize))
+    return false;
+  __char_array_prepend_str_size (&array->dynarray_abstract, str, size, used);
+  return true;
+}
+
+/* Append the contents of string 'str' to char_array 'array, including the
+   final '\0' byte.  */
+__attribute__ ((unused, nonnull (1, 2)))
+static bool
+char_array_append_str (struct char_array *array, const char *str)
+{
+  size_t size = strlen (str);
+  /* Resizing the array might change its used elements and it used it below
+     to correct copy the elements.  */
+  size_t used = array->dynarray_header.used - 1;
+
+  /* 'used' does account for final '\0', so there is no need to add
+     an extra element to calculate the final required size.  */
+  size_t newsize;
+  if (check_add_overflow_size_t (used + 1, size, &newsize))
+    __libc_dynarray_overflow_failure (used + 1, size);
+
+  if (!char_array_resize (array, newsize))
+    return false;
+
+  /* Start to append at '\0' up to string length and add a final '\0'.  */
+  *(char*) mempcpy (array->dynarray_header.array + used, str, size) = '\0';
+  return true;
+}
+
+/* Replace the contents starting of position 'pos' of char_array 'array'
+   with the contents of string 'str' up to 'len' bytes.  A final '\0'
+   is appended in the string.  */
+__attribute__ ((unused, nonnull (1, 3)))
+static bool
+char_array_replace_str_pos (struct char_array *array, size_t pos,
+                            const char *str, size_t len)
+{
+  if (pos > array->dynarray_header.used)
+    __libc_dynarray_at_failure (array->dynarray_header.used, pos);
+
+  size_t newsize;
+  if (check_add_overflow_size_t (pos, len, &newsize)
+      || check_add_overflow_size_t (newsize, 1, &newsize)
+      || !char_array_resize (array, newsize))
+    return false;
+
+  __char_array_replace_str_pos (&array->dynarray_abstract, pos, str, len);
+  return true;
+}
diff --git a/malloc/char_array.h b/malloc/char_array.h
new file mode 100644
index 0000000..c696673
--- /dev/null
+++ b/malloc/char_array.h
@@ -0,0 +1,53 @@
+/* Specialized dynarray for C strings.  Shared definitions.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CHAR_ARRAY_H
+#define _CHAR_ARRAY_H
+
+#include <malloc/dynarray.h>
+
+/* Internal funciton.  Set the dynarray to the content of the string STR up
+   to SIZE bytes.  The dynarray must be resized previously.  */
+void __char_array_set_str_size (struct dynarray_header *, const char *str,
+				size_t size);
+
+/* Internal function.  Remove the character at position POS from dynarray.
+   The position must be a valid one.  */
+void __char_array_erase (struct dynarray_header *, size_t pos);
+
+/* Internal function.  Prepend the content of string STR up to SIZE bytes to
+   dynarray by moving USED bytes forward.  The dynarray must be resized
+   previously.  */
+void __char_array_prepend_str_size (struct dynarray_header *,
+				    const char *str, size_t size,
+				    size_t used);
+
+/* Internal function.  Replace the content of dynarray starting at position
+   POS with the content of string STR up to LEN bytes.  The dynarray must
+   be resize previously and STR must contain at least LEN bytes.  */
+void __char_array_replace_str_pos (struct dynarray_header *, size_t pos,
+				   const char *str, size_t len);
+
+#ifndef _ISOMAC
+libc_hidden_proto (__char_array_set_str_size)
+libc_hidden_proto (__char_array_erase)
+libc_hidden_proto (__char_array_prepend_str_size)
+libc_hidden_proto (__char_array_replace_str_pos)
+#endif
+
+#endif
diff --git a/malloc/dynarray.h b/malloc/dynarray.h
index 5888bcb..bb52b0f 100644
--- a/malloc/dynarray.h
+++ b/malloc/dynarray.h
@@ -168,12 +168,21 @@ bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
 void __libc_dynarray_at_failure (size_t size, size_t index)
   __attribute__ ((noreturn));
 
+/* Internal function.  TErminate the process after an overflow in
+   new size allocation.  SIZE is the current number of elements in
+   dynamic array and INCR is the new elements to add on current
+   size.  */
+void __libc_dynarray_overflow_failure (size_t size, size_t incr)
+  __attribute__ ((noreturn));
+
 #ifndef _ISOMAC
 libc_hidden_proto (__libc_dynarray_emplace_enlarge)
 libc_hidden_proto (__libc_dynarray_resize)
 libc_hidden_proto (__libc_dynarray_resize_clear)
 libc_hidden_proto (__libc_dynarray_finalize)
 libc_hidden_proto (__libc_dynarray_at_failure)
+libc_hidden_proto (__libc_dynarray_overflow_failure)
+
 #endif
 
 #endif /* _DYNARRAY_H */
diff --git a/malloc/dynarray_overflow_failure.c b/malloc/dynarray_overflow_failure.c
new file mode 100644
index 0000000..14936b0
--- /dev/null
+++ b/malloc/dynarray_overflow_failure.c
@@ -0,0 +1,31 @@
+/* Report an dynamic array size overflow condition.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dynarray.h>
+#include <stdio.h>
+
+void
+__libc_dynarray_overflow_failure (size_t size, size_t incr)
+{
+  char buf[200];
+  __snprintf (buf, sizeof (buf), "Fatal glibc error: "
+              "new size overflows (old %zu and increment %zu)\n",
+              size, incr);
+ __libc_fatal (buf);
+}
+libc_hidden_def (__libc_dynarray_overflow_failure)
diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h
index 6a62717..ac845dc 100644
--- a/malloc/malloc-internal.h
+++ b/malloc/malloc-internal.h
@@ -91,4 +91,18 @@ check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
 #endif
 }
 
+/* Set *R = A + B.  Return true if the answer is mathematically incorrect due
+   to overflow; in this case, *R is the low order bits of the correct
+   answer.  */
+static inline bool
+check_add_overflow_size_t (size_t a, size_t b, size_t *r)
+{
+#if 5 <= __GNUC__
+  return __builtin_add_overflow (a, b, r);
+#else
+  *r = a + b;
+  return *r < a;
+#endif
+}
+
 #endif /* _MALLOC_INTERNAL_H */
diff --git a/malloc/tst-char_array.c b/malloc/tst-char_array.c
new file mode 100644
index 0000000..f55acb9
--- /dev/null
+++ b/malloc/tst-char_array.c
@@ -0,0 +1,110 @@
+/* Test for char_array.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+
+#include <malloc/char_array-skeleton.c>
+
+#include <malloc.h>
+#include <mcheck.h>
+#include <stdint.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+  mtrace ();
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_empty (&str) == true);
+    TEST_VERIFY_EXIT (char_array_length (&str) == 0);
+    TEST_VERIFY_EXIT (char_array_is_empty (&str) == true);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "") == 0);
+    char_array_free (&str);
+  }
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_str (&str, "testing"));
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("testing"));
+    TEST_VERIFY_EXIT (char_array_pos (&str, 2) == 's');
+    TEST_VERIFY_EXIT (char_array_is_empty (&str) == false);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "testing") == 0);
+    char_array_free (&str);
+  }
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_str_size (&str, "testing", 4));
+    TEST_VERIFY_EXIT (char_array_length (&str) == 4);
+    TEST_VERIFY_EXIT (char_array_pos (&str, 2) == 's');
+    TEST_VERIFY_EXIT (char_array_is_empty (&str) == false);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "test") == 0);
+    char_array_free (&str);
+  }
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_str (&str, "testing"));
+    TEST_VERIFY_EXIT (char_array_set_str (&str, "abcdef"));
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "abcdef") == 0);
+    TEST_VERIFY_EXIT (char_array_set_str_size (&str, "abcdef", 4));
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "abcd") == 0);
+    char_array_free (&str);
+  }
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_str (&str, "testing"));
+    TEST_VERIFY_EXIT (char_array_erase (&str, 4) == true);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("testing") - 1);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "testng") == 0);
+    TEST_VERIFY_EXIT (char_array_erase (&str, char_array_length (&str))
+		      == false);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("testing") - 1);
+    TEST_VERIFY_EXIT (char_array_erase (&str, char_array_length (&str) - 1)
+		      == true);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("testing") - 2);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "testn") == 0);
+    char_array_free (&str);
+  }
+
+  {
+    struct char_array str;
+    TEST_VERIFY_EXIT (char_array_init_str (&str, "test"));
+    TEST_VERIFY_EXIT (char_array_prepend_str (&str, "123"));
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "123test") == 0);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("123test"));
+    TEST_VERIFY_EXIT (char_array_append_str (&str, "456"));
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "123test456") == 0);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("123test456"));
+    TEST_VERIFY_EXIT (char_array_replace_str_pos (&str, 7, "789", 3));
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "123test789") == 0);
+    TEST_VERIFY_EXIT (char_array_length (&str) == strlen ("123test789"));
+    TEST_VERIFY_EXIT (char_array_crop (&str, 7));
+    TEST_VERIFY_EXIT (char_array_length (&str) == 7);
+    TEST_VERIFY_EXIT (strcmp (char_array_str (&str), "123test") == 0);
+    char_array_free (&str);
+  }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 14/18] posix: Use char_array for home_dir in glob
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (10 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 07/18] posix: User LOGIN_NAME_MAX for all user names " Adhemerval Zanella
@ 2017-08-11 14:51 ` Adhemerval Zanella
  2017-08-11 14:51 ` [PATCH 15/18] posix: Add common function to get home directory Adhemerval Zanella
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-11 14:51 UTC (permalink / raw)
  To: libc-alpha

This patch uses char_array for home directory discovery.  It simplifies
the buffer management.

Checked on x86_64-linux-gnu.

	* posix/glob.c (glob): Use char_array for home directory.
---
 posix/glob.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/posix/glob.c b/posix/glob.c
index 67196fd..d2cb871 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -607,8 +607,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  || char_array_pos (&dirname, 2) == '/')))
 	{
 	  /* Look up home directory.  */
-	  char *home_dir = getenv ("HOME");
-	  int malloc_home_dir = 0;
+	  struct char_array home_dir;
+
+	  const char *home_env = getenv ("HOME");
+	  home_env = home_env == NULL ? "" : home_env;
+	  if (!char_array_init_str (&home_dir, home_env))
+	    {
+	      retval = GLOB_NOSPACE;
+	      goto out;
+	    }
 # ifdef _AMIGA
 	  if (home_dir == NULL || home_dir[0] == '\0')
 	    home_dir = "SYS:";
@@ -635,7 +642,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		home_dir = "c:/users/default"; /* poor default */
 	    }
 #  else
-	  if (home_dir == NULL || home_dir[0] == '\0')
+	  if (char_array_is_empty (&home_dir))
 	    {
 	      int success;
 	      char user_name[LOGIN_NAME_MAX];
@@ -668,9 +675,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 #   endif
 		  if (p != NULL)
 		    {
-		      home_dir = strdup (p->pw_dir);
-		      malloc_home_dir = 1;
-		      if (home_dir == NULL)
+		      if (!char_array_set_str (&home_dir, p->pw_dir))
 			{
 			  scratch_buffer_free (&pwtmpbuf);
 			  goto err_nospace;
@@ -679,10 +684,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		  scratch_buffer_free (&pwtmpbuf);
 		}
 	    }
-	  if (home_dir == NULL || home_dir[0] == '\0')
+	  if (char_array_is_empty (&home_dir))
 	    {
-	      if (__glibc_unlikely (malloc_home_dir))
-		free (home_dir);
 	      if (flags & GLOB_TILDE_CHECK)
 		{
 		  retval = GLOB_NOMATCH;
@@ -690,8 +693,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 		}
 	      else
 		{
-		  home_dir = (char *) "~"; /* No luck.  */
-		  malloc_home_dir = 0;
+		  if (!char_array_set_str (&home_dir, "~"))
+		    {
+		      retval = GLOB_NOSPACE;
+		      goto out;
+		    }
 		}
 	    }
 #  endif /* WINDOWS32 */
@@ -699,7 +705,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	  /* Now construct the full directory.  */
 	  if (char_array_pos (&dirname, 1) == '\0')
 	    {
-	      if (!char_array_set_str (&dirname, home_dir))
+	      if (!char_array_set_str (&dirname, char_array_str (&home_dir)))
 		goto err_nospace;
 	      dirlen = char_array_size (&dirname) - 1;
 	    }
@@ -707,9 +713,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
 	    {
 	      /* Replaces '~' by the obtained HOME dir.  */
 	      char_array_erase (&dirname, 0);
-	      if (!char_array_prepend_str (&dirname, home_dir))
+	      if (!char_array_prepend_str (&dirname,
+					   char_array_str (&home_dir)))
 		goto err_nospace;
 	    }
+	  char_array_free (&home_dir);
 	  dirname_modified = true;
 	}
 # if !defined _AMIGA && !defined WINDOWS32
-- 
2.7.4

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 08/18] malloc: Add specialized dynarray for C strings
  2017-08-11 14:51 ` [PATCH 08/18] malloc: Add specialized dynarray for C strings Adhemerval Zanella
@ 2017-08-17 10:12   ` Florian Weimer
  2017-08-17 12:39     ` Adhemerval Zanella
  2017-08-17 14:48     ` Pedro Alves
  0 siblings, 2 replies; 33+ messages in thread
From: Florian Weimer @ 2017-08-17 10:12 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 08/11/2017 04:50 PM, Adhemerval Zanella wrote:
> This patch adds an specialized dynarray to manage C strings using the
> dynarray internal implementation.  It uses some private fields from
> dynarray and thus it provided specific files to access and manage
> the internal string buffer.

There is a lot of complexity in this code to maintain the invariant that
the stored array is NUL-terminated.  It also means that the dynarray
functions must not be used directly.

std::string in C++ has a c_str() method which adds the null termination
on demand.  This means that char_array_str could fail due to memory
allocation, but I think it would simplify the code in general.  I tried
to write the dynarray functions in such a way that errors are sticky, so
one error check (char_array_str returns NULL) could cover all the
previous manipulations.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
                   ` (17 preceding siblings ...)
  2017-08-11 14:51 ` [PATCH 09/18] posix: Use char_array for internal glob dirname Adhemerval Zanella
@ 2017-08-17 10:19 ` Florian Weimer
  2017-08-17 12:07   ` Adhemerval Zanella
  18 siblings, 1 reply; 33+ messages in thread
From: Florian Weimer @ 2017-08-17 10:19 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 08/11/2017 04:50 PM, Adhemerval Zanella wrote:
> I did not and will not take care of the non-unix code on glob.c (it
> is probably broken after the glob refactor).  In fact I would like
> to cleanup this implementation even further to remove all code not
> really used for GLIBC if gnulib developers want to not keep the
> implementation at sync.

I would prefer to de-couple from gnulib and run unifdef as a first step.
 When working on the code the last time, I had too many moments of “this
code isn't for us, how can we keep it”.

Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 10:19 ` [PATCH v2 00/18] posix: glob fixes and refactor Florian Weimer
@ 2017-08-17 12:07   ` Adhemerval Zanella
  2017-08-17 14:11     ` Paul Eggert
  0 siblings, 1 reply; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-17 12:07 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 17/08/2017 07:19, Florian Weimer wrote:
> On 08/11/2017 04:50 PM, Adhemerval Zanella wrote:
>> I did not and will not take care of the non-unix code on glob.c (it
>> is probably broken after the glob refactor).  In fact I would like
>> to cleanup this implementation even further to remove all code not
>> really used for GLIBC if gnulib developers want to not keep the
>> implementation at sync.
> 
> I would prefer to de-couple from gnulib and run unifdef as a first step.
>  When working on the code the last time, I had too many moments of “this
> code isn't for us, how can we keep it”.

That was my feeling as well, but my understanding was the general idea is to
keep the code compatible for future sync.  However due the changes I agree
that decoupling should be a better strategy.  I can update the 01/18 with
a general cleanup or adding another upcoming patch with this change, which
do you think it is better?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 08/18] malloc: Add specialized dynarray for C strings
  2017-08-17 10:12   ` Florian Weimer
@ 2017-08-17 12:39     ` Adhemerval Zanella
  2017-08-17 14:48     ` Pedro Alves
  1 sibling, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-17 12:39 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 17/08/2017 07:12, Florian Weimer wrote:
> On 08/11/2017 04:50 PM, Adhemerval Zanella wrote:
>> This patch adds an specialized dynarray to manage C strings using the
>> dynarray internal implementation.  It uses some private fields from
>> dynarray and thus it provided specific files to access and manage
>> the internal string buffer.
> 
> There is a lot of complexity in this code to maintain the invariant that
> the stored array is NUL-terminated.  It also means that the dynarray
> functions must not be used directly.

Not really, however some won't follow the NUL-terminated semantic (I use
the provided free function for instance).  Unfortunately we do not have
access modifiers as for c++ to avoid issues calling these functions, but
since it is an internal API I think we can live with it. 

> 
> std::string in C++ has a c_str() method which adds the null termination
> on demand.  This means that char_array_str could fail due to memory
> allocation, but I think it would simplify the code in general.  I tried
> to write the dynarray functions in such a way that errors are sticky, so
> one error check (char_array_str returns NULL) could cover all the
> previous manipulations.

That is not my understanding checking on libstd++v3 code:

include/bits/basic_string.h:

2222       const _CharT*
2223       c_str() const _GLIBCXX_NOEXCEPT
2224       { return _M_data(); }

 158       pointer
 159       _M_data() const
 160       { return _M_dataplus._M_p; }

AFAIK current std::string does not add the NUL on demand, but rather keep
the internal buffer NUL-terminated.  Some basic checking with gdb does
hold it true:

(gdb) l
3
4       int main ()
5       {
6         std::string str("test");
7
8         printf ("%s\n", str.c_str());
9         return 0;
10      }
(gdb) p str
$10 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fffffffd6d0 "test"}, 
  _M_string_length = 4, {_M_local_buf = "test", '\000' <repeats 11 times>, _M_allocated_capacity = 1953719668}}
(gdb) x/6c str._M_dataplus._M_p
0x7fffffffd6d0: 116 't' 101 'e' 115 's' 116 't' 0 '\000'        0 '\000'

Also, c++ does not try to use the same API for both containers and string
because they do differ exactly regarding NUL-termination and C-string
handling.  And I think this make sense to provide different ABI to 
different data types and hiding all the required internal handling.

Also, it will still require some char_array wrappers to hiding the C-string
manipulation (append/prepend/initialization) and it will add complications
if char_array_str adds an emplace element at the end. I assume it will 
add the element in place instead of returning a copy (since the idea is 
to actually have inplace data for char_array manipulation), so all other 
functions will need to actually check if the char_array buffer has the 
NULL-byte before manipulating. It only adds complexity and more possible
code patch for internal buffer manipulation.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 12:07   ` Adhemerval Zanella
@ 2017-08-17 14:11     ` Paul Eggert
  2017-08-17 17:32       ` Adhemerval Zanella
  0 siblings, 1 reply; 33+ messages in thread
From: Paul Eggert @ 2017-08-17 14:11 UTC (permalink / raw)
  To: Adhemerval Zanella, Florian Weimer, libc-alpha

Adhemerval Zanella wrote:

> I agree
> that decoupling should be a better strategy.  I can update the 01/18 with
> a general cleanup or adding another upcoming patch with this change, which
> do you think it is better?

I'd like to keep the code in sync as much as possible. Perhaps the best way to 
do this would be for you to present the "decoupled" version, then I can attempt 
to merge the Gnulib-needed changes into it, in such a way that they don't 
disrupt the mainline code.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 08/18] malloc: Add specialized dynarray for C strings
  2017-08-17 10:12   ` Florian Weimer
  2017-08-17 12:39     ` Adhemerval Zanella
@ 2017-08-17 14:48     ` Pedro Alves
  1 sibling, 0 replies; 33+ messages in thread
From: Pedro Alves @ 2017-08-17 14:48 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella, libc-alpha

On 08/17/2017 11:12 AM, Florian Weimer wrote:

> There is a lot of complexity in this code to maintain the invariant that
> the stored array is NUL-terminated.  It also means that the dynarray
> functions must not be used directly.
> 
> std::string in C++ has a c_str() method which adds the null termination
> on demand.  

Depends on what you mean by C++.  std::string::c_str() must not throw
and must be constant time since C++11.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 14:11     ` Paul Eggert
@ 2017-08-17 17:32       ` Adhemerval Zanella
  2017-08-17 18:07         ` Florian Weimer
  2017-08-17 19:51         ` Paul Eggert
  0 siblings, 2 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-17 17:32 UTC (permalink / raw)
  To: Paul Eggert, Florian Weimer, libc-alpha



On 17/08/2017 11:10, Paul Eggert wrote:
> Adhemerval Zanella wrote:
> 
>> I agree
>> that decoupling should be a better strategy.  I can update the 01/18 with
>> a general cleanup or adding another upcoming patch with this change, which
>> do you think it is better?
> 
> I'd like to keep the code in sync as much as possible. Perhaps the best way to do this would be for you to present the "decoupled" version, then I can attempt to merge the Gnulib-needed changes into it, in such a way that they don't disrupt the mainline code.

My understanding from Florian comment is the 'decoupled' version would be the
code with both win32/amiga/etc code striped and LIBC defines set to only glibc.
Would it be acceptable for gnulib?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 17:32       ` Adhemerval Zanella
@ 2017-08-17 18:07         ` Florian Weimer
  2017-08-17 19:51         ` Paul Eggert
  1 sibling, 0 replies; 33+ messages in thread
From: Florian Weimer @ 2017-08-17 18:07 UTC (permalink / raw)
  To: Adhemerval Zanella, Paul Eggert, libc-alpha

On 08/17/2017 07:32 PM, Adhemerval Zanella wrote:
> 
> 
> On 17/08/2017 11:10, Paul Eggert wrote:
>> Adhemerval Zanella wrote:
>>
>>> I agree
>>> that decoupling should be a better strategy.  I can update the 01/18 with
>>> a general cleanup or adding another upcoming patch with this change, which
>>> do you think it is better?
>>
>> I'd like to keep the code in sync as much as possible. Perhaps the best way to do this would be for you to present the "decoupled" version, then I can attempt to merge the Gnulib-needed changes into it, in such a way that they don't disrupt the mainline code.
> 
> My understanding from Florian comment is the 'decoupled' version would be the
> code with both win32/amiga/etc code striped and LIBC defines set to only glibc.

Right.  This would also remove the d_ino/d_type abstraction macros.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 17:32       ` Adhemerval Zanella
  2017-08-17 18:07         ` Florian Weimer
@ 2017-08-17 19:51         ` Paul Eggert
  2017-08-17 20:05           ` Adhemerval Zanella
  1 sibling, 1 reply; 33+ messages in thread
From: Paul Eggert @ 2017-08-17 19:51 UTC (permalink / raw)
  To: Adhemerval Zanella, Florian Weimer, libc-alpha

On 08/17/2017 10:32 AM, Adhemerval Zanella wrote:
> My understanding from Florian comment is the 'decoupled' version would be the
> code with both win32/amiga/etc code striped and LIBC defines set to only glibc.
> Would it be acceptable for gnulib?

We don't need Amiga code any more. MS-Windows support is still used, 
though. However, the current style in glob.c with the forest of ifdefs 
is pretty bad, and it'd be good to see it go. Instead, I'd rather have 
the Gnulib-specific stuff put into a section that is relatively 
independent of the rest of the code. To do that, I suggest that you just 
rip out all the MS-Windows code, and I'll do my best to reintroduce it 
in a cleaner way.

> This would also remove the d_ino/d_type abstraction macros.
We'll still need some form of abstraction. For fts.c Gnulib is using 
something like the following, and we could do this sort of thing in 
glob.c too. It's not much of a burden to write 'D_INO (dp)' instead of 
'dp->d_ino' in the mainline code.


#if defined _LIBC || defined D_INO_IN_DIRENT
# define D_INO(dp) (dp)->d_ino
#else
# define D_INO(dp) 0
#endif

By the way, I've lost track: have you looked at the Gnulib fixes for 
glob.c, and merged them into your glibc patch?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 00/18] posix: glob fixes and refactor
  2017-08-17 19:51         ` Paul Eggert
@ 2017-08-17 20:05           ` Adhemerval Zanella
  0 siblings, 0 replies; 33+ messages in thread
From: Adhemerval Zanella @ 2017-08-17 20:05 UTC (permalink / raw)
  To: Paul Eggert, Florian Weimer, libc-alpha



On 17/08/2017 16:51, Paul Eggert wrote:
> On 08/17/2017 10:32 AM, Adhemerval Zanella wrote:
>> My understanding from Florian comment is the 'decoupled' version would be the
>> code with both win32/amiga/etc code striped and LIBC defines set to only glibc.
>> Would it be acceptable for gnulib?
> 
> We don't need Amiga code any more. MS-Windows support is still used, though. However, the current style in glob.c with the forest of ifdefs is pretty bad, and it'd be good to see it go. Instead, I'd rather have the Gnulib-specific stuff put into a section that is relatively independent of the rest of the code. To do that, I suggest that you just rip out all the MS-Windows code, and I'll do my best to reintroduce it in a cleaner way.

Right, I will remove both amiga and win32 code in a subsequent patch to 
gnulib sync.

> 
>> This would also remove the d_ino/d_type abstraction macros.
> We'll still need some form of abstraction. For fts.c Gnulib is using something like the following, and we could do this sort of thing in glob.c too. It's not much of a burden to write 'D_INO (dp)' instead of 'dp->d_ino' in the mainline code.
> 
> 
> #if defined _LIBC || defined D_INO_IN_DIRENT
> # define D_INO(dp) (dp)->d_ino
> #else
> # define D_INO(dp) 0
> #endif

I think it is feasible, I will check this out.


> 
> By the way, I've lost track: have you looked at the Gnulib fixes for glob.c, and merged them into your glibc patch?
> 

By [01/18] patch [1], I synced with 1dc82a77fa606e18edf (which is
still latest gnulib glob version), with some exceptions:

  1. Commit 44c637c (Properly initialize glob structure with
     GLOB_BRACE|GLOB_DOOFFS) which fixes BZ# 20707.

  2. No inclusion of flexmember.h header and its usage on glob.
     The code is meant to be rewritten and header is not required in
     following patches in this set.

  3. An additional define (GLOB_COMPAT_BUILD) to avoid building
     size_and_wrapv and gblo_use_alloca twice on some configurations
     (i368 compat code) due multiple inclusion.

[1] https://sourceware.org/ml/libc-alpha/2017-08/msg00441.html

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866]
  2017-08-11 14:51 ` [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866] Adhemerval Zanella
@ 2017-08-31 22:11   ` Paul Eggert
  0 siblings, 0 replies; 33+ messages in thread
From: Paul Eggert @ 2017-08-31 22:11 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: libc-alpha, Gnulib bugs

[-- Attachment #1: Type: text/plain, Size: 1534 bytes --]

Thanks for working to clear up this longstanding problem in glibc glob. 
Here are the issues I found with the proposed patch:

* glob_in_dir calls gl_stat, which is a typo; it should call gl_lstat.

* The GLOB_MARK checks should use stat not lstat, since symlinks to 
directories should be marked.

* For GLOB_TILDE and GLOB_TILDE_CHECK, the revised code still tests for 
existence of the home directory as a directory. Other glob 
implementations merely expand the ~ or ~foo and treat the result as a 
literal string, and glibc should be consistent here.  This is simpler 
and avoids a stat call.

* __lstat64 needs to be defined in the !_LIBC case too.

* While looking into this I noticed that glob ignores directory entries 
with d_ino == 0. Although currently GNU/Linux cannot return such 
entries, POSIX allows d_ino == 0 so this assumption is not portable, and 
this bug is in the neighborhood so we might as well fix it in a 
separated patch (which simplifies the code).

* commit message says "remove tst-glob3" but the patch actually removes 
bug-glob1-ARGS.

I fixed these problems (except for the last one) while merging the patch 
into Gnulib, and this resulted in the attached patches which I installed 
into Gnulib master. I'll CC: this to bug-gnulib accordingly. Please 
consider merging the glibc-relevant parts of these patches back into 
glibc, for your next iteration of this glibc proposal. As always, the 
goal is for glob.c and related files to be identical in glibc and Gnulib.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-glob-simplify-symlink-detection.patch --]
[-- Type: text/x-patch; name="0001-glob-simplify-symlink-detection.patch", Size: 6088 bytes --]

From 1e19d5ce886d7db7c7ad074d8a7f64f75ec6f42e Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 31 Aug 2017 14:34:24 -0700
Subject: [PATCH 1/3] glob: simplify symlink detection

* lib/glob.c (dirent_type): New type.  Use uint_fast8_t not
uint8_t, as C99 does not require uint8_t.
(DT_UNKNOWN, DT_DIR, DT_LNK) [!HAVE_STRUCT_DIRENT_D_TYPE]:
New macros.
(struct readdir_result): Use dirent_type.  Do not define skip_entry
unless it is needed; this saves a byte on platforms lacking d_ino.
(readdir_result_type, readdir_result_skip_entry):
New functions, replacing ...
(readdir_result_might_be_symlink, readdir_result_might_be_dir):
... these functions, which were removed.  This makes the callers
easier to read.  All callers changed.
(D_INO_TO_RESULT): Now empty if there is no d_ino.
---
 ChangeLog  | 14 ++++++++++++
 lib/glob.c | 75 ++++++++++++++++++++++++++++++++------------------------------
 2 files changed, 53 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 58f42dbf6..f372dbd38 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2017-08-31  Paul Eggert  <eggert@cs.ucla.edu>
+
+	glob: simplify symlink detection
+	* lib/glob.c (dirent_type): New type.  Use uint_fast8_t not
+	uint8_t, as C99 does not require uint8_t.
+	(struct readdir_result): Use it.  Do not define skip_entry unless
+	it is needed; this saves a byte on platforms lacking d_ino.
+	(readdir_result_type, readdir_result_skip_entry):
+	New functions, replacing ...
+	(readdir_result_might_be_symlink, readdir_result_might_be_dir):
+	... these functions, which were removed.  This makes the callers
+	easier to read.  All callers changed.
+	(D_INO_TO_RESULT): Now empty if there is no d_ino.
+
 2017-08-30  Pádraig Brady  <P@draigBrady.com>
 
 	fts-tests: tag as a longrunning-test so not included by default
diff --git a/lib/glob.c b/lib/glob.c
index a1e50d709..6113af6fd 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -92,61 +92,59 @@
 \f
 static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
 
+typedef uint_fast8_t dirent_type;
+
+#ifndef HAVE_STRUCT_DIRENT_D_TYPE
+/* Any distinct values will do here.
+   Undef any existing macros out of the way.  */
+# undef DT_UNKNOWN
+# undef DT_DIR
+# undef DT_LNK
+# define DT_UNKNOWN 0
+# define DT_DIR 1
+# define DT_LNK 2
+#endif
+
 /* A representation of a directory entry which does not depend on the
    layout of struct dirent, or the size of ino_t.  */
 struct readdir_result
 {
   const char *name;
 #if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE
-  uint8_t type;
+  dirent_type type;
 #endif
+#if defined _LIBC || defined D_INO_IN_DIRENT
   bool skip_entry;
+#endif
 };
 
+/* Initialize and return type member of struct readdir_result.  */
+static dirent_type
+readdir_result_type (struct readdir_result d)
+{
 #if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE
-/* Initializer based on the d_type member of struct dirent.  */
 # define D_TYPE_TO_RESULT(source) (source)->d_type,
-
-/* True if the directory entry D might be a symbolic link.  */
-static bool
-readdir_result_might_be_symlink (struct readdir_result d)
-{
-  return d.type == DT_UNKNOWN || d.type == DT_LNK;
-}
-
-/* True if the directory entry D might be a directory.  */
-static bool
-readdir_result_might_be_dir (struct readdir_result d)
-{
-  return d.type == DT_DIR || readdir_result_might_be_symlink (d);
-}
-#else /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */
+  return d.type;
+#else
 # define D_TYPE_TO_RESULT(source)
-
-/* If we do not have type information, symbolic links and directories
-   are always a possibility.  */
-
-static bool
-readdir_result_might_be_symlink (struct readdir_result d)
-{
-  return true;
+  return DT_UNKNOWN;
+#endif
 }
 
+/* Initialize and return skip_entry member of struct readdir_result.  */
 static bool
-readdir_result_might_be_dir (struct readdir_result d)
+readdir_result_skip_entry (struct readdir_result d)
 {
-  return true;
-}
-
-#endif /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */
-
 /* Initializer for skip_entry.  POSIX does not require that the d_ino
    field be present, and some systems do not provide it. */
 #if defined _LIBC || defined D_INO_IN_DIRENT
 # define D_INO_TO_RESULT(source) (source)->d_ino == 0,
+  return d.skip_entry;
 #else
-# define D_INO_TO_RESULT(source) false,
+# define D_INO_TO_RESULT(source)
+  return false;
 #endif
+}
 
 /* Construct an initializer for a struct readdir_result object from a
    struct dirent *.  No copy of the name is made.  */
@@ -1545,19 +1543,24 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
               }
               if (d.name == NULL)
                 break;
-              if (d.skip_entry)
+              if (readdir_result_skip_entry (d))
                 continue;
 
               /* If we shall match only directories use the information
                  provided by the dirent call if possible.  */
-              if ((flags & GLOB_ONLYDIR) && !readdir_result_might_be_dir (d))
-                continue;
+              if (flags & GLOB_ONLYDIR)
+                switch (readdir_result_type (d))
+                  {
+                  case DT_DIR: case DT_LNK: case DT_UNKNOWN: break;
+                  default: continue;
+                  }
 
               if (fnmatch (pattern, d.name, fnm_flags) == 0)
                 {
                   /* If the file we found is a symlink we have to
                      make sure the target file exists.  */
-                  if (!readdir_result_might_be_symlink (d)
+                  dirent_type type = readdir_result_type (d);
+                  if (! (type == DT_LNK || type == DT_UNKNOWN)
                       || link_exists_p (dfd, directory, dirlen, d.name,
                                         pglob, flags))
                     {
-- 
2.13.5


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-glob-backupfile-inode-0-is-a-valid-inode-number.patch --]
[-- Type: text/x-patch; name="0002-glob-backupfile-inode-0-is-a-valid-inode-number.patch", Size: 7115 bytes --]

From 972a46f6061a5147c0c8f5e24c48bb83dd9f8d1b Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 31 Aug 2017 14:34:24 -0700
Subject: [PATCH 2/3] glob, backupfile: inode 0 is a valid inode number
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* doc/posix-functions/readdir.texi (readdir):
* doc/posix-headers/dirent.texi (dirent.h):
Document more readdir portability issues.
* lib/backupfile.c (REAL_DIR_ENTRY): Remove.
(numbered_backup): Don’t treat inode 0 any differently from
other inode values.
* lib/glob.c (struct readdir_result): Remove skip_entry member.
(readdir_result_skip_entry, D_INO_TO_RESULT): Remove.
All uses removed.
* modules/glob (Depends-on): Remove d-ino.
---
 ChangeLog                        | 12 ++++++++++++
 doc/posix-functions/readdir.texi |  8 ++++++++
 doc/posix-headers/dirent.texi    | 25 +++++++++++++++++++++++++
 lib/backupfile.c                 |  7 +------
 lib/glob.c                       | 21 ---------------------
 modules/glob                     |  1 -
 6 files changed, 46 insertions(+), 28 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f372dbd38..53945d434 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2017-08-31  Paul Eggert  <eggert@cs.ucla.edu>
 
+	glob, backupfile: inode 0 is a valid inode number
+	* doc/posix-functions/readdir.texi (readdir):
+	* doc/posix-headers/dirent.texi (dirent.h):
+	Document more readdir portability issues.
+	* lib/backupfile.c (REAL_DIR_ENTRY): Remove.
+	(numbered_backup): Don’t treat inode 0 any differently from
+	other inode values.
+	* lib/glob.c (struct readdir_result): Remove skip_entry member.
+	(readdir_result_skip_entry, D_INO_TO_RESULT): Remove.
+	All uses removed.
+	* modules/glob (Depends-on): Remove d-ino.
+
 	glob: simplify symlink detection
 	* lib/glob.c (dirent_type): New type.  Use uint_fast8_t not
 	uint8_t, as C99 does not require uint8_t.
diff --git a/doc/posix-functions/readdir.texi b/doc/posix-functions/readdir.texi
index a9da77a14..701a10faf 100644
--- a/doc/posix-functions/readdir.texi
+++ b/doc/posix-functions/readdir.texi
@@ -20,4 +20,12 @@ incorrectly.  (Cf. @code{AC_SYS_LARGEFILE}.)
 
 Portability problems not fixed by Gnulib:
 @itemize
+@item
+Although POSIX places no restrictions on @code{d_ino} values, some
+older systems are rumored to return @code{d_ino} values equal to zero
+for directory entries that do not really exist.  Although Gnulib
+formerly attempted to cater to these older systems, this caused
+misbehavior on standard systems and so Gnulib does not attempt to
+cater to them any more.  If you know of any problems caused by this,
+please send a bug report.
 @end itemize
diff --git a/doc/posix-headers/dirent.texi b/doc/posix-headers/dirent.texi
index b61f82687..0a19fd3c2 100644
--- a/doc/posix-headers/dirent.texi
+++ b/doc/posix-headers/dirent.texi
@@ -17,4 +17,29 @@ Portability problems not fixed by Gnulib:
 @item
 This header file is missing on some platforms:
 MSVC 14.
+
+@item
+Although many systems define a @code{struct dirent} member named
+@code{d_type} and directory entry type macros like @code{DT_DIR} and
+@code{DT_LINK}, some do not:
+AIX 7.2, HP-UX 11, Solaris 11, probably others.
+
+@item
+On systems with @code{d_type}, not every filesystem supports
+@code{d_type}, and those lacking support will set it to @code{DT_UNKNOWN}.
+
+@item
+Some systems define a @code{struct dirent} member named @code{d_namlen}
+containing the string length of @code{d_name}, but others do not.
+
+@item
+Some systems define a @code{struct dirent} member named @code{d_off}
+containing a magic cookie suitable as an argument to @code{seekdir},
+but others do not.
+
+@item
+Some systems define a @code{struct dirent} member named
+@code{d_reclen} containing the number of bytes in the directory entry
+record, but others do not.  This member has limited utility, as it is
+an implementation detail.
 @end itemize
diff --git a/lib/backupfile.c b/lib/backupfile.c
index d7115bd61..ae171ecee 100644
--- a/lib/backupfile.c
+++ b/lib/backupfile.c
@@ -40,11 +40,6 @@
 #ifndef _D_EXACT_NAMLEN
 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
 #endif
-#if D_INO_IN_DIRENT
-# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
-#else
-# define REAL_DIR_ENTRY(dp) 1
-#endif
 
 #if ! (HAVE_PATHCONF && defined _PC_NAME_MAX)
 # define pathconf(file, option) (errno = -1)
@@ -225,7 +220,7 @@ numbered_backup (char **buffer, size_t buffer_size, size_t filelen,
       bool all_9s;
       size_t versionlen;
 
-      if (! REAL_DIR_ENTRY (dp) || _D_EXACT_NAMLEN (dp) < baselen + 4)
+      if (_D_EXACT_NAMLEN (dp) < baselen + 4)
         continue;
 
       if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0)
diff --git a/lib/glob.c b/lib/glob.c
index 6113af6fd..106f4cb26 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -113,9 +113,6 @@ struct readdir_result
 #if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE
   dirent_type type;
 #endif
-#if defined _LIBC || defined D_INO_IN_DIRENT
-  bool skip_entry;
-#endif
 };
 
 /* Initialize and return type member of struct readdir_result.  */
@@ -131,28 +128,12 @@ readdir_result_type (struct readdir_result d)
 #endif
 }
 
-/* Initialize and return skip_entry member of struct readdir_result.  */
-static bool
-readdir_result_skip_entry (struct readdir_result d)
-{
-/* Initializer for skip_entry.  POSIX does not require that the d_ino
-   field be present, and some systems do not provide it. */
-#if defined _LIBC || defined D_INO_IN_DIRENT
-# define D_INO_TO_RESULT(source) (source)->d_ino == 0,
-  return d.skip_entry;
-#else
-# define D_INO_TO_RESULT(source)
-  return false;
-#endif
-}
-
 /* Construct an initializer for a struct readdir_result object from a
    struct dirent *.  No copy of the name is made.  */
 #define READDIR_RESULT_INITIALIZER(source) \
   {                                        \
     source->d_name,                        \
     D_TYPE_TO_RESULT (source)              \
-    D_INO_TO_RESULT (source)               \
   }
 
 /* Call gl_readdir on STREAM.  This macro can be overridden to reduce
@@ -1543,8 +1524,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
               }
               if (d.name == NULL)
                 break;
-              if (readdir_result_skip_entry (d))
-                continue;
 
               /* If we shall match only directories use the information
                  provided by the dirent call if possible.  */
diff --git a/modules/glob b/modules/glob
index 48cf387bd..0d6209de6 100644
--- a/modules/glob
+++ b/modules/glob
@@ -20,7 +20,6 @@ snippet/warn-on-use
 alloca          [test -n "$GLOB_H"]
 builtin-expect  [test -n "$GLOB_H"]
 closedir        [test -n "$GLOB_H"]
-d-ino           [test -n "$GLOB_H"]
 d-type          [test -n "$GLOB_H"]
 dirfd           [test -n "$GLOB_H"]
 flexmember      [test -n "$GLOB_H"]
-- 
2.13.5


[-- Attachment #4: 0003-glob-match-dangling-symlinks.patch --]
[-- Type: text/x-patch, Size: 18476 bytes --]

From f43e4827d2ae03d01979d89c9bdf294bf10af7e1 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 31 Aug 2017 14:34:25 -0700
Subject: [PATCH 3/3] glob: match dangling symlinks

This fixes a bug I inadvertently introduced to Gnulib when I
merged glibc glob back into gnulib on 2007-10-16.  This fix is
inspired by a patch proposed for glibc by Adhemerval Zanella in:
https://sourceware.org/ml/libc-alpha/2017-08/msg00446.html
* doc/posix-functions/glob.texi: Update list of affected platforms.
* lib/glob.c (__lstat64): New macro.
(is_dir): New function.
(glob, glob_in_dir): Match symlinks even if they are dangling.
(link_stat, link_exists_p): Remove.  All uses removed.
* lib/glob.in.h (__attribute_noinline__): Remove; no longer used.
* m4/glob.m4 (gl_PREREQ_GLOB): Do not check for fstatat.
* modules/glob (Depends-on): Remove dirfd.
* modules/glob-tests (Depends-on): Add symlink.
* tests/test-glob.c: Include errno.h, unistd.h.
(BASE): New macro.
(main): Test dangling symlinks, if symlinks are supported.
---
 ChangeLog                     |  17 ++++
 doc/posix-functions/glob.texi |   2 +-
 lib/glob.c                    | 227 +++++++++++++++---------------------------
 lib/glob.in.h                 |   8 --
 m4/glob.m4                    |   4 +-
 modules/glob                  |   1 -
 modules/glob-tests            |   1 +
 tests/test-glob.c             |  20 ++++
 8 files changed, 121 insertions(+), 159 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 53945d434..960c56029 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2017-08-31  Paul Eggert  <eggert@cs.ucla.edu>
 
+	glob: match dangling symlinks
+	This fixes a bug I inadvertently introduced to Gnulib when I
+	merged glibc glob back into gnulib on 2007-10-16.  This fix is
+	inspired by a patch proposed for glibc by Adhemerval Zanella in:
+	https://sourceware.org/ml/libc-alpha/2017-08/msg00446.html
+	* doc/posix-functions/glob.texi: Update list of affected platforms.
+	* lib/glob.c (__lstat64): New macro.
+	(is_dir): New function.
+	(glob, glob_in_dir): Match symlinks even if they are dangling.
+	(link_stat, link_exists_p): Remove.  All uses removed.
+	* lib/glob.in.h (__attribute_noinline__): Remove; no longer used.
+	* m4/glob.m4 (gl_PREREQ_GLOB): Do not check for fstatat.
+	* modules/glob-tests (Depends-on): Add symlink.
+	* tests/test-glob.c: Include errno.h, unistd.h.
+	(BASE): New macro.
+	(main): Test dangling symlinks, if symlinks are supported.
+
 	glob, backupfile: inode 0 is a valid inode number
 	* doc/posix-functions/readdir.texi (readdir):
 	* doc/posix-headers/dirent.texi (dirent.h):
diff --git a/doc/posix-functions/glob.texi b/doc/posix-functions/glob.texi
index 382fe5234..5ea89a87f 100644
--- a/doc/posix-functions/glob.texi
+++ b/doc/posix-functions/glob.texi
@@ -14,7 +14,7 @@ IRIX 5.3, mingw, MSVC 14, BeOS.
 @item
 This function does not list symbolic links to nonexistent files among the results,
 on some platforms:
-glibc 2.14, AIX 7.1, HP-UX 11, Solaris 11 2011-11.
+glibc 2.26, AIX 7.2, HP-UX 11, Solaris 11 2011-11.
 @item
 On platforms where @code{off_t} is a 32-bit type, this function may not
 work correctly on huge directories larger than 2 GB.
diff --git a/lib/glob.c b/lib/glob.c
index 106f4cb26..610d50a7f 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -57,6 +57,9 @@
 # define readdir(str) __readdir64 (str)
 # define getpwnam_r(name, bufp, buf, len, res) \
     __getpwnam_r (name, bufp, buf, len, res)
+# ifndef __lstat64
+#  define __lstat64(fname, buf) __lxstat64 (_STAT_VER, fname, buf)
+# endif
 # ifndef __stat64
 #  define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
 # endif
@@ -64,6 +67,7 @@
 # define FLEXIBLE_ARRAY_MEMBER
 #else /* !_LIBC */
 # define __getlogin_r(buf, len) getlogin_r (buf, len)
+# define __lstat64(fname, buf)  lstat (fname, buf)
 # define __stat64(fname, buf)   stat (fname, buf)
 # define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag)
 # define struct_stat64          struct stat
@@ -226,6 +230,18 @@ static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL;
 static int collated_compare (const void *, const void *) __THROWNL;
 
 
+/* Return true if FILENAME is a directory or a symbolic link to a directory.
+   Use FLAGS and PGLOB to resolve the filename.  */
+static bool
+is_dir (char const *filename, int flags, glob_t const *pglob)
+{
+  struct stat st;
+  struct_stat64 st64;
+  return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)
+          ? pglob->gl_stat (filename, &st) == 0 && S_ISDIR (st.st_mode)
+          : __stat64 (filename, &st64) == 0 && S_ISDIR (st64.st_mode));
+}
+
 /* Find the end of the sub-pattern in a brace expression.  */
 static const char *
 next_brace_sub (const char *cp, int flags)
@@ -976,68 +992,53 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
      can give the answer now.  */
   if (filename == NULL)
     {
-      struct stat st;
-      struct_stat64 st64;
-
-      /* Return the directory if we don't check for error or if it exists.  */
-      if ((flags & GLOB_NOCHECK)
-          || (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
-               ? ((*pglob->gl_stat) (dirname, &st) == 0
-                  && S_ISDIR (st.st_mode))
-               : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode)))))
+      size_t newcount = pglob->gl_pathc + pglob->gl_offs;
+      char **new_gl_pathv;
+
+      if (newcount > SIZE_MAX / sizeof (char *) - 2)
         {
-          size_t newcount = pglob->gl_pathc + pglob->gl_offs;
-          char **new_gl_pathv;
+        nospace:
+          free (pglob->gl_pathv);
+          pglob->gl_pathv = NULL;
+          pglob->gl_pathc = 0;
+          retval = GLOB_NOSPACE;
+          goto out;
+        }
 
-          if (newcount > SIZE_MAX / sizeof (char *) - 2)
-            {
-            nospace:
-              free (pglob->gl_pathv);
-              pglob->gl_pathv = NULL;
-              pglob->gl_pathc = 0;
-              retval = GLOB_NOSPACE;
-              goto out;
-            }
+      new_gl_pathv = realloc (pglob->gl_pathv,
+                              (newcount + 2) * sizeof (char *));
+      if (new_gl_pathv == NULL)
+        goto nospace;
+      pglob->gl_pathv = new_gl_pathv;
 
-          new_gl_pathv = realloc (pglob->gl_pathv,
-                                  (newcount + 2) * sizeof (char *));
-          if (new_gl_pathv == NULL)
+      if (flags & GLOB_MARK && is_dir (dirname, flags, pglob))
+        {
+          char *p;
+          pglob->gl_pathv[newcount] = malloc (dirlen + 2);
+          if (pglob->gl_pathv[newcount] == NULL)
             goto nospace;
-          pglob->gl_pathv = new_gl_pathv;
-
-          if (flags & GLOB_MARK)
+          p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
+          p[0] = '/';
+          p[1] = '\0';
+          if (__glibc_unlikely (malloc_dirname))
+            free (dirname);
+        }
+      else
+        {
+          if (__glibc_unlikely (malloc_dirname))
+            pglob->gl_pathv[newcount] = dirname;
+          else
             {
-              char *p;
-              pglob->gl_pathv[newcount] = malloc (dirlen + 2);
+              pglob->gl_pathv[newcount] = strdup (dirname);
               if (pglob->gl_pathv[newcount] == NULL)
                 goto nospace;
-              p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
-              p[0] = '/';
-              p[1] = '\0';
-              if (__glibc_unlikely (malloc_dirname))
-                free (dirname);
             }
-          else
-            {
-              if (__glibc_unlikely (malloc_dirname))
-                pglob->gl_pathv[newcount] = dirname;
-              else
-                {
-                  pglob->gl_pathv[newcount] = strdup (dirname);
-                  if (pglob->gl_pathv[newcount] == NULL)
-                    goto nospace;
-                }
-            }
-          pglob->gl_pathv[++newcount] = NULL;
-          ++pglob->gl_pathc;
-          pglob->gl_flags = flags;
-
-          return 0;
         }
+      pglob->gl_pathv[++newcount] = NULL;
+      ++pglob->gl_pathc;
+      pglob->gl_flags = flags;
 
-      /* Not found.  */
-      retval = GLOB_NOMATCH;
-      goto out;
+      return 0;
     }
 
   meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
@@ -1245,15 +1246,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
     {
       /* Append slashes to directory names.  */
       size_t i;
-      struct stat st;
-      struct_stat64 st64;
 
       for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i)
-        if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-             ? ((*pglob->gl_stat) (pglob->gl_pathv[i], &st) == 0
-                && S_ISDIR (st.st_mode))
-             : (__stat64 (pglob->gl_pathv[i], &st64) == 0
-                && S_ISDIR (st64.st_mode))))
+        if (is_dir (pglob->gl_pathv[i], flags, pglob))
           {
             size_t len = strlen (pglob->gl_pathv[i]) + 2;
             char *new = realloc (pglob->gl_pathv[i], len);
@@ -1359,56 +1354,6 @@ prefix_array (const char *dirname, char **array, size_t n)
   return 0;
 }
 
-/* We put this in a separate function mainly to allow the memory
-   allocated with alloca to be recycled.  */
-static int
-__attribute_noinline__
-link_stat (const char *dir, size_t dirlen, const char *fname,
-           glob_t *pglob
-# if !defined _LIBC && !HAVE_FSTATAT
-           , int flags
-# endif
-           )
-{
-  size_t fnamelen = strlen (fname);
-  char *fullname = __alloca (dirlen + 1 + fnamelen + 1);
-  struct stat st;
-
-  mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1),
-           fname, fnamelen + 1);
-
-# if !defined _LIBC && !HAVE_FSTATAT
-  if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) == 0, 1))
-    {
-      struct_stat64 st64;
-      return __stat64 (fullname, &st64);
-    }
-# endif
-  return (*pglob->gl_stat) (fullname, &st);
-}
-
-/* Return true if DIR/FNAME exists.  */
-static int
-link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname,
-               glob_t *pglob, int flags)
-{
-  int status;
-# if defined _LIBC || HAVE_FSTATAT
-  if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
-    status = link_stat (dir, dirlen, fname, pglob);
-  else
-    {
-      /* dfd cannot be -1 here, because dirfd never returns -1 on
-         glibc, or on hosts that have fstatat.  */
-      struct_stat64 st64;
-      status = __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0);
-    }
-# else
-  status = link_stat (dir, dirlen, fname, pglob, flags);
-# endif
-  return status == 0 || errno == EOVERFLOW;
-}
-
 /* Like 'glob', but PATTERN is a final pathname component,
    and matches are searched for in DIRECTORY.
    The GLOB_NOSORT bit in FLAGS is ignored.  No sorting is ever done.
@@ -1450,8 +1395,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
     }
   else if (meta == 0)
     {
-      /* Since we use the normal file functions we can also use stat()
-         to verify the file is there.  */
       union
       {
         struct stat st;
@@ -1476,8 +1419,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
                         "/", 1),
                pattern, patlen + 1);
       if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-            ? (*pglob->gl_stat) (fullname, &ust.st)
-            : __stat64 (fullname, &ust.st64))
+            ? (*pglob->gl_lstat) (fullname, &ust.st)
+            : __lstat64 (fullname, &ust.st64))
            == 0)
           || errno == EOVERFLOW)
         /* We found this file to be existing.  Now tell the rest
@@ -1501,8 +1444,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
         }
       else
         {
-          int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
-                     ? -1 : dirfd ((DIR *) stream));
           int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
                            | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
           flags |= GLOB_MAGCHAR;
@@ -1536,42 +1477,34 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 
               if (fnmatch (pattern, d.name, fnm_flags) == 0)
                 {
-                  /* If the file we found is a symlink we have to
-                     make sure the target file exists.  */
-                  dirent_type type = readdir_result_type (d);
-                  if (! (type == DT_LNK || type == DT_UNKNOWN)
-                      || link_exists_p (dfd, directory, dirlen, d.name,
-                                        pglob, flags))
+                  if (cur == names->count)
                     {
-                      if (cur == names->count)
-                        {
-                          struct globnames *newnames;
-                          size_t count = names->count * 2;
-                          size_t nameoff = offsetof (struct globnames, name);
-                          size_t size = FLEXSIZEOF (struct globnames, name,
-                                                    count * sizeof (char *));
-                          if ((SIZE_MAX - nameoff) / 2 / sizeof (char *)
-                              < names->count)
-                            goto memory_error;
-                          if (glob_use_alloca (alloca_used, size))
-                            newnames = names_alloca
-                              = alloca_account (size, alloca_used);
-                          else if ((newnames = malloc (size))
-                                   == NULL)
-                            goto memory_error;
-                          newnames->count = count;
-                          newnames->next = names;
-                          names = newnames;
-                          cur = 0;
-                        }
-                      names->name[cur] = strdup (d.name);
-                      if (names->name[cur] == NULL)
+                      struct globnames *newnames;
+                      size_t count = names->count * 2;
+                      size_t nameoff = offsetof (struct globnames, name);
+                      size_t size = FLEXSIZEOF (struct globnames, name,
+                                                count * sizeof (char *));
+                      if ((SIZE_MAX - nameoff) / 2 / sizeof (char *)
+                          < names->count)
                         goto memory_error;
-                      ++cur;
-                      ++nfound;
-                      if (SIZE_MAX - pglob->gl_offs <= nfound)
+                      if (glob_use_alloca (alloca_used, size))
+                        newnames = names_alloca
+                          = alloca_account (size, alloca_used);
+                      else if ((newnames = malloc (size))
+                               == NULL)
                         goto memory_error;
+                      newnames->count = count;
+                      newnames->next = names;
+                      names = newnames;
+                      cur = 0;
                     }
+                  names->name[cur] = strdup (d.name);
+                  if (names->name[cur] == NULL)
+                    goto memory_error;
+                  ++cur;
+                  ++nfound;
+                  if (SIZE_MAX - pglob->gl_offs <= nfound)
+                    goto memory_error;
                 }
             }
         }
diff --git a/lib/glob.in.h b/lib/glob.in.h
index cfb7e4996..b0d27cff6 100644
--- a/lib/glob.in.h
+++ b/lib/glob.in.h
@@ -49,14 +49,6 @@
 
 #define attribute_hidden
 
-#ifndef __attribute_noinline__
-# if 3 < __GNUC__ + (1 <= __GNUC_MINOR__)
-#  define __attribute_noinline__ __attribute__ ((__noinline__))
-#else
-#  define __attribute_noinline__ /* Ignore */
-# endif
-#endif
-
 #if __GNUC__ < 3
 # define __glibc_unlikely(cond) (cond)
 #else
diff --git a/m4/glob.m4 b/m4/glob.m4
index 23fc80219..189cd3a18 100644
--- a/m4/glob.m4
+++ b/m4/glob.m4
@@ -1,4 +1,4 @@
-# glob.m4 serial 14
+# glob.m4 serial 15
 dnl Copyright (C) 2005-2007, 2009-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -72,5 +72,5 @@ AC_DEFUN([gl_PREREQ_GLOB],
     HAVE_SYS_CDEFS_H=0
   fi
   AC_SUBST([HAVE_SYS_CDEFS_H])
-  AC_CHECK_FUNCS_ONCE([fstatat getlogin_r getpwnam_r])dnl
+  AC_CHECK_FUNCS_ONCE([getlogin_r getpwnam_r])dnl
 ])
diff --git a/modules/glob b/modules/glob
index 0d6209de6..98d49e34f 100644
--- a/modules/glob
+++ b/modules/glob
@@ -21,7 +21,6 @@ alloca          [test -n "$GLOB_H"]
 builtin-expect  [test -n "$GLOB_H"]
 closedir        [test -n "$GLOB_H"]
 d-type          [test -n "$GLOB_H"]
-dirfd           [test -n "$GLOB_H"]
 flexmember      [test -n "$GLOB_H"]
 fnmatch         [test -n "$GLOB_H"]
 getlogin_r      [test -n "$GLOB_H"]
diff --git a/modules/glob-tests b/modules/glob-tests
index 151bc06b3..abc3666ed 100644
--- a/modules/glob-tests
+++ b/modules/glob-tests
@@ -5,6 +5,7 @@ tests/macros.h
 
 Depends-on:
 glob-c++-tests
+symlink
 
 configure.ac:
 
diff --git a/tests/test-glob.c b/tests/test-glob.c
index 7e755d9c4..eb3472c6e 100644
--- a/tests/test-glob.c
+++ b/tests/test-glob.c
@@ -20,6 +20,9 @@
 
 #include <glob.h>
 
+#include <errno.h>
+#include <unistd.h>
+
 #include "signature.h"
 SIGNATURE_CHECK (glob, int, (char const *, int, int (*) (char const *, int),
                              glob_t *));
@@ -29,6 +32,7 @@ SIGNATURE_CHECK (globfree, void, (glob_t *));
 
 #include "macros.h"
 
+#define BASE "test-glob.t"
 #define GL_NO_SUCH_FILE "/gnulib-magic-does-not-exist"
 
 int
@@ -73,5 +77,21 @@ main ()
   ASSERT (strcmp (g.gl_pathv[0], GL_NO_SUCH_FILE) == 0);
   globfree (&g);
 
+  if ((symlink (GL_NO_SUCH_FILE, BASE "globlink1") == 0 || errno == EEXIST)
+      && (symlink (".", BASE "globlink2") == 0 || errno == EEXIST))
+    {
+      res = glob (BASE "globlink[12]", 0, NULL, &g);
+      ASSERT (res == 0 && g.gl_pathc == 2);
+      ASSERT (strcmp (g.gl_pathv[0], BASE "globlink1") == 0);
+      ASSERT (strcmp (g.gl_pathv[1], BASE "globlink2") == 0);
+      globfree (&g);
+
+      res = glob (BASE "globlink[12]", GLOB_MARK, NULL, &g);
+      ASSERT (res == 0 && g.gl_pathc == 2);
+      ASSERT (strcmp (g.gl_pathv[0], BASE "globlink1") == 0);
+      ASSERT (strcmp (g.gl_pathv[1], BASE "globlink2/") == 0);
+      globfree (&g);
+    }
+
   return 0;
 }
-- 
2.13.5


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca
  2017-08-11 14:51 ` [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca Adhemerval Zanella
@ 2017-09-01 23:50   ` Paul Eggert
  2017-09-02 10:40   ` Paul Eggert
  1 sibling, 0 replies; 33+ messages in thread
From: Paul Eggert @ 2017-09-01 23:50 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha; +Cc: Florian Weimer

[-- Attachment #1: Type: text/plain, Size: 419 bytes --]

Thanks, I merged that patch into Gnulib; see:

http://lists.gnu.org/archive/html/bug-gnulib/2017-09/msg00002.html

This means Gnulib starts sharing scratch_buffer.h etc. with glibc, which 
entails some minor and safe changes to those files on the glibc side. 
Plus, glibc glob.c can be simplified slightly now. Please see the 
attached patch, which I hope can be folded into the next iteration of 
this glibc patchset.

[-- Attachment #2: 0001-Merge-glob-related-changes-from-Gnulib.patch --]
[-- Type: text/x-patch, Size: 5095 bytes --]

From 05cc71685fb0e177233655c4d499ad223ffe9a6f Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Fri, 1 Sep 2017 16:37:15 -0700
Subject: [PATCH] Merge glob-related changes from Gnulib

* include/scratch_buffer.h (struct scratch_buffer):
Use portable method to align buffer, instead of relying
on GCC's __attribute__ ((aligned (...))), so that this
can be compiled with non-GCC compilers when used as
part of Gnulib.
* malloc/scratch_buffer_grow.c:
* malloc/scratch_buffer_grow_preserve.c:
* malloc/scratch_buffer_set_array_size.c:
Include <libc-config.h> first thing, if !_LIBC.
Remove stray top-level ";" that non-GCC compilers reject.
* misc/sys/cdefs.h: Do not include <bits/wordsize.h> and
<bits/long-double.h> if __WORDSIZE is already defined, so that
Gnulib-using code does not include these nonexistent files.
* posix/glob.c: Minor simplifications due to Gnulib changes:
do not include <config.h>, and do not define __set_errno.
does that via another method for glob.c.
---
 include/scratch_buffer.h               | 3 +--
 malloc/scratch_buffer_grow.c           | 6 +++++-
 malloc/scratch_buffer_grow_preserve.c  | 6 +++++-
 malloc/scratch_buffer_set_array_size.c | 6 +++++-
 misc/sys/cdefs.h                       | 8 ++++++--
 posix/glob.c                           | 8 --------
 6 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/include/scratch_buffer.h b/include/scratch_buffer.h
index dd17a4a7e1..bb04662eb2 100644
--- a/include/scratch_buffer.h
+++ b/include/scratch_buffer.h
@@ -66,8 +66,7 @@
 struct scratch_buffer {
   void *data;    /* Pointer to the beginning of the scratch area.  */
   size_t length; /* Allocated space at the data pointer, in bytes.  */
-  char __space[1024]
-    __attribute__ ((aligned (__alignof__ (max_align_t))));
+  max_align_t __space[(1023 + sizeof (max_align_t)) / sizeof (max_align_t)];
 };
 
 /* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
diff --git a/malloc/scratch_buffer_grow.c b/malloc/scratch_buffer_grow.c
index 22bae506a1..d2df028654 100644
--- a/malloc/scratch_buffer_grow.c
+++ b/malloc/scratch_buffer_grow.c
@@ -16,6 +16,10 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
 #include <scratch_buffer.h>
 #include <errno.h>
 
@@ -49,4 +53,4 @@ __libc_scratch_buffer_grow (struct scratch_buffer *buffer)
   buffer->length = new_length;
   return true;
 }
-libc_hidden_def (__libc_scratch_buffer_grow);
+libc_hidden_def (__libc_scratch_buffer_grow)
diff --git a/malloc/scratch_buffer_grow_preserve.c b/malloc/scratch_buffer_grow_preserve.c
index 18543ef85b..9268615311 100644
--- a/malloc/scratch_buffer_grow_preserve.c
+++ b/malloc/scratch_buffer_grow_preserve.c
@@ -16,6 +16,10 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
 #include <scratch_buffer.h>
 #include <errno.h>
 #include <string.h>
@@ -60,4 +64,4 @@ __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
   buffer->length = new_length;
   return true;
 }
-libc_hidden_def (__libc_scratch_buffer_grow_preserve);
+libc_hidden_def (__libc_scratch_buffer_grow_preserve)
diff --git a/malloc/scratch_buffer_set_array_size.c b/malloc/scratch_buffer_set_array_size.c
index 8ab6d9d300..6fcc115340 100644
--- a/malloc/scratch_buffer_set_array_size.c
+++ b/malloc/scratch_buffer_set_array_size.c
@@ -16,6 +16,10 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
 #include <scratch_buffer.h>
 #include <errno.h>
 #include <limits.h>
@@ -57,4 +61,4 @@ __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
   buffer->length = new_length;
   return true;
 }
-libc_hidden_def (__libc_scratch_buffer_set_array_size);
+libc_hidden_def (__libc_scratch_buffer_set_array_size)
diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index cfd39d5302..d4dd8d0418 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -415,8 +415,12 @@
       [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]
 #endif
 
-#include <bits/wordsize.h>
-#include <bits/long-double.h>
+/* The #ifndef lets Gnulib avoid including these on non-glibc
+   platforms, where the includes typically do not exist.  */
+#ifndef __WORDSIZE
+# include <bits/wordsize.h>
+# include <bits/long-double.h>
+#endif
 
 #if defined __LONG_DOUBLE_MATH_OPTIONAL && defined __NO_LONG_DOUBLE_MATH
 # define __LDBL_COMPAT 1
diff --git a/posix/glob.c b/posix/glob.c
index c0ee4dacb5..7ddc2c8d70 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -15,10 +15,6 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#ifndef _LIBC
-# include <config.h>
-#endif
-
 #include <glob.h>
 
 #include <errno.h>
@@ -39,10 +35,6 @@
 #endif
 
 #include <errno.h>
-#ifndef __set_errno
-# define __set_errno(val) errno = (val)
-#endif
-
 #include <dirent.h>
 #include <stdlib.h>
 #include <string.h>
-- 
2.13.5


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca
  2017-08-11 14:51 ` [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca Adhemerval Zanella
  2017-09-01 23:50   ` Paul Eggert
@ 2017-09-02 10:40   ` Paul Eggert
  1 sibling, 0 replies; 33+ messages in thread
From: Paul Eggert @ 2017-09-02 10:40 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha; +Cc: Florian Weimer, Gnulib bugs

[-- Attachment #1: Type: text/plain, Size: 214 bytes --]

Adhemerval Zanella wrote:
> +		  p = getpwnam (pwtmpbuf.data);

That won't work on non-glibc platforms that lack getpwnam_r, as the argument 
should be 'name'. I installed the attached patch to Gnulib to fix this.

[-- Attachment #2: 0001-glob-fix-typo-in-recent-change.patch --]
[-- Type: text/x-patch, Size: 1212 bytes --]

From 88855bf9f1cfc2e5165e4200bf62aa949e76db90 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 2 Sep 2017 03:37:46 -0700
Subject: [PATCH] glob: fix typo in recent change

* lib/glob.c (glob) [!HAVE_GETPWNAM_R && !_LIBC]:
Fix recently-introduced typo.
---
 ChangeLog  | 4 ++++
 lib/glob.c | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 867662d..4dd0367 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2017-09-02  Paul Eggert  <eggert@cs.ucla.edu>
 
+	glob: fix typo in recent change
+	* lib/glob.c (glob) [!HAVE_GETPWNAM_R && !_LIBC]:
+	Fix recently-introduced typo.
+
 	glob: don't save and restore errno unnecessarily
 	* lib/glob.c (glob): Don't save and restore errno
 	merely because we have getpwnam_r.
diff --git a/lib/glob.c b/lib/glob.c
index 8de2d5f..f1b30ee 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -658,7 +658,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
                         }
                     }
 # else
-                  p = getpwnam (pwtmpbuf.data);
+                  p = getpwnam (name);
 # endif
                   if (p != NULL)
                     {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage
  2017-08-11 14:51 ` [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage Adhemerval Zanella
@ 2017-09-02 22:50   ` Paul Eggert
  0 siblings, 0 replies; 33+ messages in thread
From: Paul Eggert @ 2017-09-02 22:50 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: libc-alpha, Gnulib bugs

[-- Attachment #1: Type: text/plain, Size: 1524 bytes --]

On 08/11/2017 07:50 AM, Adhemerval Zanella wrote:

> There is no actual login to resize the buffer in case of the resizing
> the buffer in case of ERANGE, so a static buffer using glibc default
> LOGIN_NAME_MAX is suffice.

Although I had trouble parsing that, I think you're saying that because 
the current glob.c goes awry when sysconf (_SC_LOGIN_NAME_MAX) < 0, it's 
OK if we change glob.c to insist on a fixed-size limit of 255 bytes on 
user name length so that glob continues to mishandle (presumably 
mostly-theoretical) environments with longer user names. But that's not 
the GNU style, which is to avoid arbitrary limits. Instead, let's fix 
glob.c so that it doesn't need to know the user name length limit. 
Obviously glob should use heap allocation for anything large, which 
suggests that it should use a scratch buffer for the login name.

I looked into this, and it's easy enough to change glob.c to use the 
tail of the scratch buffer that it's already using for getpwnam_r (given 
your previously-proposed patches), and this simplifies glob's 
memory-allocation code. I installed the attached patch into Gnulib to do 
that. Please take a look at it for your next go-round with glibc. Thanks.

This is mostly-theoretical stuff, of course, as this code is exercised 
only when $HOME is unset or empty.

> +	      char user_name[LOGIN_NAME_MAX];

A nit: that array needs to be one byte bigger, for the trailing NULL. 
This point is irrelevant to the attached Gnulib patch, which doesn't use 
LOGIN_NAME_MAX.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-glob-fix-bugs-with-long-login-names.patch --]
[-- Type: text/x-patch; name="0001-glob-fix-bugs-with-long-login-names.patch", Size: 5647 bytes --]

From 47688d5de93a7baf7b203a7b687d3f4809667dcd Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 2 Sep 2017 15:39:16 -0700
Subject: [PATCH] glob: fix bugs with long login names
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Adhemerval Zanella in:
https://sourceware.org/ml/libc-alpha/2017-08/msg00455.html
* lib/glob.c (GET_LOGIN_NAME_MAX): Remove.
(glob): Use the same scratch buffer for both getlogin_r and
getpwnam_r.  Don’t require preallocation of the login name.  This
simplifies storage allocation, and corrects the handling of
long login names.
---
 ChangeLog  | 11 ++++++++
 lib/glob.c | 88 +++++++++++++++++++++-----------------------------------------
 2 files changed, 41 insertions(+), 58 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b67d21799..351495b2f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2017-09-02  Paul Eggert  <eggert@cs.ucla.edu>
+
+	glob: fix bugs with long login names
+	Problem reported by Adhemerval Zanella in:
+	https://sourceware.org/ml/libc-alpha/2017-08/msg00455.html
+	* lib/glob.c (GET_LOGIN_NAME_MAX): Remove.
+	(glob): Use the same scratch buffer for both getlogin_r and
+	getpwnam_r.  Don’t require preallocation of the login name.  This
+	simplifies storage allocation, and corrects the handling of
+	long login names.
+
 2017-09-02  Bruno Haible  <bruno@clisp.org>
 
 	dirent: Update doc.
diff --git a/lib/glob.c b/lib/glob.c
index 7ca11361e..8eb2b9730 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -75,12 +75,6 @@
 #include <flexmember.h>
 #include <glob_internal.h>
 #include <scratch_buffer.h>
-
-#ifdef _SC_LOGIN_NAME_MAX
-# define GET_LOGIN_NAME_MAX()   sysconf (_SC_LOGIN_NAME_MAX)
-#else
-# define GET_LOGIN_NAME_MAX()   (-1)
-#endif
 \f
 static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
 
@@ -611,67 +605,45 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
               else
                 home_dir = "c:/users/default"; /* poor default */
 #else
-              int success;
-              char *name;
-              int malloc_name = 0;
-              size_t buflen = GET_LOGIN_NAME_MAX () + 1;
-
-              if (buflen == 0)
-                /* 'sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
-                   a moderate value.  */
-                buflen = 20;
-              if (glob_use_alloca (alloca_used, buflen))
-                name = alloca_account (buflen, alloca_used);
-              else
+              int err;
+              struct passwd *p;
+              struct passwd pwbuf;
+              struct scratch_buffer s;
+              scratch_buffer_init (&s);
+              while (true)
                 {
-                  name = malloc (buflen);
-                  if (name == NULL)
+                  p = NULL;
+                  err = __getlogin_r (s.data, s.length);
+                  if (err == 0)
                     {
-                      retval = GLOB_NOSPACE;
-                      goto out;
-                    }
-                  malloc_name = 1;
-                }
-
-              success = __getlogin_r (name, buflen) == 0;
-              if (success)
-                {
-                  struct passwd *p;
-                  struct scratch_buffer pwtmpbuf;
-                  scratch_buffer_init (&pwtmpbuf);
 # if defined HAVE_GETPWNAM_R || defined _LIBC
-                  struct passwd pwbuf;
-
-                  while (getpwnam_r (name, &pwbuf,
-                                     pwtmpbuf.data, pwtmpbuf.length, &p)
-                         == ERANGE)
-                    {
-                      if (!scratch_buffer_grow (&pwtmpbuf))
-                        {
-                          retval = GLOB_NOSPACE;
-                          goto out;
-                        }
-                    }
+                      size_t ssize = strlen (s.data) + 1;
+                      err = getpwnam_r (s.data, &pwbuf, s.data + ssize,
+                                        s.length - ssize, &p);
 # else
-                  p = getpwnam (name);
+                      p = getpwnam (s.data);
+                      if (p == NULL)
+                        err = errno;
 # endif
-                  if (p != NULL)
+                    }
+                  if (err != ERANGE)
+                    break;
+                  if (!scratch_buffer_grow (&s))
                     {
-                      home_dir = strdup (p->pw_dir);
-                      malloc_home_dir = 1;
-                      if (home_dir == NULL)
-                        {
-                          scratch_buffer_free (&pwtmpbuf);
-                          retval = GLOB_NOSPACE;
-                          goto out;
-                        }
+                      retval = GLOB_NOSPACE;
+                      goto out;
                     }
-                  scratch_buffer_free (&pwtmpbuf);
                 }
-              else
+              if (err == 0)
+                {
+                  home_dir = strdup (p->pw_dir);
+                  malloc_home_dir = 1;
+                }
+              scratch_buffer_free (&s);
+              if (err == 0 && home_dir == NULL)
                 {
-                  if (__glibc_unlikely (malloc_name))
-                    free (name);
+                  retval = GLOB_NOSPACE;
+                  goto out;
                 }
 #endif /* WINDOWS32 */
             }
-- 
2.13.5


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2017-09-02 22:50 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-11 14:50 [PATCH v2 00/18] posix: glob fixes and refactor Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 11/18] posix: Remove alloca usage on glob dirname Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 18/18] posix: Fix glob with GLOB_NOCHECK returning modified patterns (BZ#10246) Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 04/18] posix: Allow glob to match dangling symlinks [BZ #866] Adhemerval Zanella
2017-08-31 22:11   ` Paul Eggert
2017-08-11 14:51 ` [PATCH 08/18] malloc: Add specialized dynarray for C strings Adhemerval Zanella
2017-08-17 10:12   ` Florian Weimer
2017-08-17 12:39     ` Adhemerval Zanella
2017-08-17 14:48     ` Pedro Alves
2017-08-11 14:51 ` [PATCH 05/18] posix: Rewrite to use struct scratch_buffer instead of extend_alloca Adhemerval Zanella
2017-09-01 23:50   ` Paul Eggert
2017-09-02 10:40   ` Paul Eggert
2017-08-11 14:51 ` [PATCH 13/18] posix: Remove all alloca usage in glob Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 12/18] posix: Use dynarray for globname " Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 01/18] posix: Sync glob with gnulib [BZ #1062] Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 03/18] posix: Consolidate glob implementation Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 16/18] posix: More check for overflow allocation in glob Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 07/18] posix: User LOGIN_NAME_MAX for all user names " Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 14/18] posix: Use char_array for home_dir " Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 15/18] posix: Add common function to get home directory Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 10/18] posix: Remove alloca usage for GLOB_BRACE on glob Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 02/18] posix: Adjust glob tests to libsupport Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 06/18] posix: Remove glob GET_LOGIN_NAME_MAX usage Adhemerval Zanella
2017-09-02 22:50   ` Paul Eggert
2017-08-11 14:51 ` [PATCH 17/18] posix: Use enum for __glob_pattern_type result Adhemerval Zanella
2017-08-11 14:51 ` [PATCH 09/18] posix: Use char_array for internal glob dirname Adhemerval Zanella
2017-08-17 10:19 ` [PATCH v2 00/18] posix: glob fixes and refactor Florian Weimer
2017-08-17 12:07   ` Adhemerval Zanella
2017-08-17 14:11     ` Paul Eggert
2017-08-17 17:32       ` Adhemerval Zanella
2017-08-17 18:07         ` Florian Weimer
2017-08-17 19:51         ` Paul Eggert
2017-08-17 20:05           ` Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).