aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--posix/Makefile4
-rw-r--r--posix/bug-glob1.c88
-rw-r--r--posix/glob.c239
-rw-r--r--posix/tst-glob_symlinks.c135
5 files changed, 233 insertions, 243 deletions
diff --git a/ChangeLog b/ChangeLog
index d48cca3c24..5f9bc6244f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
2017-09-08 Adhemerval Zanella <adhemerval.zanella@linaro.org>
+ [BZ #866]
+ [BZ #1062]
+ * posix/Makefile (tests): Remove bug-glob1 and tst-glob_symlinks.
+ * posix/bug-glob1.c: Remove file.
+ * posix/tst-glob_symlinks.c: New file.
+ * posix/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.
+
[BZ #1062]
[BZ #19971]
* posix/glob.c (struct readdir_result): Remove skip_entry member.
diff --git a/posix/Makefile b/posix/Makefile
index 0ff8f5c53b..7188cba04c 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 bug-glob2 bug-glob3 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 \
@@ -93,7 +93,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \
tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \
tst-posix_spawn-fd tst-posix_spawn-setsid \
tst-posix_fadvise tst-posix_fadvise64 \
- tst-sysconf-empty-chroot
+ tst-sysconf-empty-chroot tst-glob_symlinks
tests-internal := bug-regex5 bug-regex20 bug-regex33 \
tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3
xtests := bug-ga2
diff --git a/posix/bug-glob1.c b/posix/bug-glob1.c
deleted file mode 100644
index 05c2da7584..0000000000
--- 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 dbf88ffe2b..29e894819f 100644
--- a/posix/glob.c
+++ b/posix/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)
@@ -975,68 +991,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;
+ size_t newcount = pglob->gl_pathc + pglob->gl_offs;
+ char **new_gl_pathv;
- 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;
+ 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;
+ }
- if (flags & GLOB_MARK)
- {
- char *p;
- pglob->gl_pathv[newcount] = malloc (dirlen + 2);
- 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;
+ new_gl_pathv = realloc (pglob->gl_pathv,
+ (newcount + 2) * sizeof (char *));
+ if (new_gl_pathv == NULL)
+ goto nospace;
+ pglob->gl_pathv = new_gl_pathv;
- return 0;
- }
+ 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;
+ 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;
- /* Not found. */
- retval = GLOB_NOMATCH;
- goto out;
+ return 0;
}
meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
@@ -1244,15 +1245,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);
@@ -1358,56 +1353,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.
@@ -1449,8 +1394,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;
@@ -1475,8 +1418,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
@@ -1500,8 +1443,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;
@@ -1535,42 +1476,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/posix/tst-glob_symlinks.c b/posix/tst-glob_symlinks.c
new file mode 100644
index 0000000000..5c4b4ecf4a
--- /dev/null
+++ b/posix/tst-glob_symlinks.c
@@ -0,0 +1,135 @@
+/* Test glob danglin symlink match (BZ #866).
+ 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>
+
+/* Maximum number of symlink calls for create_link function. */
+#define MAX_CREATE_LINK_TRIES 10
+
+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++ == MAX_CREATE_LINK_TRIES)
+ 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 an 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 a 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;
+}