From patchwork Tue Sep 5 20:25:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 111739 Delivered-To: patch@linaro.org Received: by 10.140.94.166 with SMTP id g35csp3323022qge; Tue, 5 Sep 2017 13:26:15 -0700 (PDT) X-Received: by 10.84.175.195 with SMTP id t61mr5628103plb.206.1504643175797; Tue, 05 Sep 2017 13:26:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1504643175; cv=none; d=google.com; s=arc-20160816; b=iRJj3aj7F4CvkAj4jGE18oBP8ArtDzcWeTOkwqfhmgusicJUjXgY623/o6fzjRdWBI SYiKCzs71yqcJvkzK07nimbV++Fmj+34hPOa+cV3yDuFL5ZJY4p/nTlSZgyg4/PKzibb 7sxJQjWzYvjo//hMqYBlxvecC7bOfUS2MBHVAZ0JofhVe5PpxUp8nDIZz6U9bMTAjiGF EREcS6g44yHskWTFiXw0kRJfA8fG5fvnTKvCL1zvn3pBb37MBK2ELhV9Iq57bstSiON/ BpcaHLYqFRypQhC/KrcUqttEqgWYBI4kzt2FWN5mnjSw3/D9mrw8uJkdV+M9QsrcboVF 1tYQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:message-id:date:subject:cc:to:from :delivered-to:sender:list-help:list-post:list-archive:list-subscribe :list-unsubscribe:list-id:precedence:mailing-list:dkim-signature :domainkey-signature:arc-authentication-results; bh=x9vclMRVmZjTdmlKgQW9AfGs77ZpKgqfSqBQMhUKScM=; b=YXRix3m3hI9mhC8iISltWERm29lQqXH/dPWS9uY95Vacc3PnhSLOivhYwxtWdoniLb 8erfu/285saCOFo6g/7e4H3FdZ/INJgspl7FpdBMHU7nvEWskLMXsZJNnqaWyBEq4wCv bXnKcelB72FUNgdEmAgdJZHRzhqxH6KaA4Izk+UdWEC3zd6oJ+3k0WH2lHRysc+zHn39 mTwplEL5aH1hEG59olFLlCttAW/HpiaWhcrH0CvS4ww4OF9zCBLtmmlBzvvmUoCgeQVC Kp0Znu7j34ZDNr6I1QMHQLLIAOt1CHQE0oZnmGHHpo/bLKbiso1/mnGCoy4Do6y9BDLZ D/8A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@sourceware.org header.s=default header.b=smjj8yDz; spf=pass (google.com: domain of libc-alpha-return-84209-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=libc-alpha-return-84209-patch=linaro.org@sourceware.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id f1si836166pfg.496.2017.09.05.13.26.15 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 05 Sep 2017 13:26:15 -0700 (PDT) Received-SPF: pass (google.com: domain of libc-alpha-return-84209-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@sourceware.org header.s=default header.b=smjj8yDz; spf=pass (google.com: domain of libc-alpha-return-84209-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=libc-alpha-return-84209-patch=linaro.org@sourceware.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; q=dns; s=default; b=BkHWmBWUcqX2BB3fLcrw0qWWHYAPa7N HLypyPGVZgVevaZ0iMxelSx5zphkwirulHrl3R694O7Zq/KFHtAl7PMCMPPl2Em8 9OB5TkkPwo9EMIovNIpjtl0jFqv3qPbPDzz1R7zRTOxjZnniTz5wXaZy2xg50uH+ US3MiPakavUg= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id:in-reply-to :references; s=default; bh=kCo/qEwQzdbWoBDEo3s3MefvCQc=; b=smjj8 yDz1+OEq5rutGhLD1SCXoXY9yqfBb4Ep0Z6Q2qBfK4HF5aP+QShGWFvNyPVPXG5F 8VcBcDBEoOv4pQ20v4jARxUx3wChoYzNvNN+QgudGVS4Q7oeVxKcE20UuYZRb9ji fgurPduo1HxVhfJZRFd4b5l7wehJkgwsf7ZWUo= Received: (qmail 49216 invoked by alias); 5 Sep 2017 20:25:53 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 48586 invoked by uid 89); 5 Sep 2017 20:25:46 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=DIRECTORY, recycled X-HELO: mail-qt0-f177.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=x9vclMRVmZjTdmlKgQW9AfGs77ZpKgqfSqBQMhUKScM=; b=Oi5tqaUJhvDmqubkheXSbAPjOu+WLH9yk7ukznmxtKC5W40ZCY4T94/caZE+9fweCM N0dvbGJE+bJBIDC2sNGkp4FBB/F36+3g4WM2r50LFF2RD5WisXWSqK1aHDL3OO8JnX5+ YmZEWnc+yhoGGswzhc4WIp6KOmdurXu9Ybx7sIgN2ylXdMT+YVB0XOoRxf4N7euLJ/0O Ms5tVx680ImNOSzHY29gewctmIZ93v8YLT/ZreGhMmI4bHn070GJSTD4JpSlGDuXVVji i+Pf78KVknzrVNUigX5Xv0ItqjqBg81CtptmWuCOr2LC8vSaPWx0ql1GxWAs1G/i87ob XWVA== X-Gm-Message-State: AHPjjUgvGHojRJJsskwexDw5gY02PZY9ahL3gWElNmPFvjOUWQoJdKQI 6UcaLRBiVYvqa5rcHLi5RA== X-Google-Smtp-Source: ADKCNb7SkLZrl+FPgz/W6vERBUlfRrGp6EQjHzFvsV3k/hEaO3rX2c4CKhE3UxcpT8w/x4i2uqRN8g== X-Received: by 10.200.53.216 with SMTP id l24mr454408qtb.7.1504643136789; Tue, 05 Sep 2017 13:25:36 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Paul Eggert Subject: [PATCH 3/9] posix: Allow glob to match dangling symlinks [BZ #866] Date: Tue, 5 Sep 2017 17:25:16 -0300 Message-Id: <1504643122-14874-4-git-send-email-adhemerval.zanella@linaro.org> In-Reply-To: <1504643122-14874-1-git-send-email-adhemerval.zanella@linaro.org> References: <1504643122-14874-1-git-send-email-adhemerval.zanella@linaro.org> 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). This also sync glob with gnulib commit fd1daf4 (glob: match dangling symlinks). Checked on x86_64-linux-gnu and on a build using build-many-glibcs.py for all major architectures. [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. * 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. --- ChangeLog | 10 ++ posix/Makefile | 4 +- posix/bug-glob1.c | 88 ----------------- posix/glob.c | 239 +++++++++++++++++----------------------------- posix/tst-glob_symlinks.c | 132 +++++++++++++++++++++++++ 5 files changed, 230 insertions(+), 243 deletions(-) delete mode 100644 posix/bug-glob1.c create mode 100644 posix/tst-glob_symlinks.c -- 2.7.4 diff --git a/posix/Makefile b/posix/Makefile index 0ff8f5c..7188cba 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 05c2da7..0000000 --- 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 36d9e5f..15c6295 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 @@ -227,6 +231,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; + 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)); @@ -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/posix/tst-glob_symlinks.c b/posix/tst-glob_symlinks.c new file mode 100644 index 0000000..3aeac89 --- /dev/null +++ b/posix/tst-glob_symlinks.c @@ -0,0 +1,132 @@ +/* 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 + +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 ? 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; +}