diff --git a/ChangeLog b/ChangeLog index d48cca3c24..5f9bc6244f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2017-09-08 Adhemerval Zanella + [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 -#include -#include -#include -#include -#include -#include - - -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; + size_t newcount = pglob->gl_pathc + pglob->gl_offs; + char **new_gl_pathv; - /* 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) + { + 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) - goto nospace; - pglob->gl_pathv = new_gl_pathv; + 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; - 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; - - return 0; - } - - /* 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static void do_prepare (int argc, char *argv[]); +#define PREPARE do_prepare +static int do_test (void); +#include + +/* 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 ? 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; +}