diff options
Diffstat (limited to 'lib/glob.c')
-rw-r--r-- | lib/glob.c | 143 |
1 files changed, 90 insertions, 53 deletions
diff --git a/lib/glob.c b/lib/glob.c index 32c88e5d15..dc1a915c05 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-2021 Free Software Foundation, Inc. +/* Copyright (C) 1991-2024 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,16 +21,17 @@ optimizes away the pattern == NULL test below. */ # define _GL_ARG_NONNULL(params) -# include <config.h> +# include <libc-config.h> #endif #include <glob.h> #include <errno.h> +#include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> -#include <stdbool.h> +#include <stdckdint.h> #include <stddef.h> #include <stdint.h> #include <assert.h> @@ -56,28 +57,38 @@ # define sysconf(id) __sysconf (id) # define closedir(dir) __closedir (dir) # define opendir(name) __opendir (name) +# undef dirfd +# define dirfd(str) __dirfd (str) # define readdir(str) __readdir64 (str) # define getpwnam_r(name, bufp, buf, len, res) \ __getpwnam_r (name, bufp, buf, len, res) -# define struct_stat64 struct stat64 # define FLEXIBLE_ARRAY_MEMBER +# ifndef struct_stat +# define struct_stat struct stat +# endif +# ifndef struct_stat64 +# define struct_stat64 struct stat64 +# endif +# ifndef GLOB_LSTAT +# define GLOB_LSTAT gl_lstat +# endif +# ifndef GLOB_FSTATAT64 +# define GLOB_FSTATAT64 __fstatat64 +# endif # include <shlib-compat.h> #else /* !_LIBC */ # define __glob glob # define __getlogin_r(buf, len) getlogin_r (buf, len) -# define __lstat64(fname, buf) lstat (fname, buf) -# if defined _WIN32 && !defined __CYGWIN__ - /* Avoid GCC or clang warning. The original __stat64 macro is unused. */ -# undef __stat64 -# endif -# 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 COMPILE_GLOB64 +# define struct_stat struct stat +# define struct_stat64 struct stat +# define GLOB_LSTAT gl_lstat +# define GLOB_FSTATAT64 fstatat #endif /* _LIBC */ #include <fnmatch.h> @@ -172,16 +183,17 @@ convert_dirent64 (const struct dirent64 *source) #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 +/* The results of opendir() in this file are used with dirfd. But they are + not used with 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. */ +# ifndef GNULIB_defined_DIR +# ifdef GNULIB_defined_opendir +# undef opendir +# endif +# ifdef GNULIB_defined_closedir +# undef closedir +# endif # endif /* Just use malloc. */ @@ -196,44 +208,22 @@ glob_lstat (glob_t *pglob, int flags, const char *fullname) { /* Use on glob-lstat-compat.c to provide a compat symbol which does not use lstat / gl_lstat. */ -#ifdef GLOB_NO_LSTAT -# define GL_LSTAT gl_stat -# define LSTAT64 __stat64 -#else -# define GL_LSTAT gl_lstat -# define LSTAT64 __lstat64 -#endif - union { - struct stat st; + struct_stat st; struct_stat64 st64; } ust; return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC) - ? pglob->GL_LSTAT (fullname, &ust.st) - : LSTAT64 (fullname, &ust.st64)); -} - -/* 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) -{ -#if 7 <= __GNUC__ && !defined __ICC - return __builtin_add_overflow (a, b, r); -#else - *r = a + b; - return *r < a; -#endif + ? pglob->GLOB_LSTAT (fullname, &ust.st) + : GLOB_FSTATAT64 (AT_FDCWD, fullname, &ust.st64, + AT_SYMLINK_NOFOLLOW)); } static bool glob_use_alloca (size_t alloca_used, size_t len) { size_t size; - return (!size_add_wrapv (alloca_used, len, &size) + return (!ckd_add (&size, alloca_used, len) && __libc_use_alloca (size)); } @@ -249,11 +239,12 @@ static int collated_compare (const void *, const void *) __THROWNL; static bool is_dir (char const *filename, int flags, glob_t const *pglob) { - struct stat st; + 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)); + : (GLOB_FSTATAT64 (AT_FDCWD, filename, &st64, 0) == 0 + && S_ISDIR (st64.st_mode))); } /* Find the end of the sub-pattern in a brace expression. */ @@ -743,6 +734,8 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), else { #ifndef WINDOWS32 + /* Recognize ~user as a shorthand for the specified user's home + directory. */ char *end_name = strchr (dirname, '/'); char *user_name; int malloc_user_name = 0; @@ -881,7 +874,22 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } scratch_buffer_free (&pwtmpbuf); } -#endif /* !WINDOWS32 */ +#else /* WINDOWS32 */ + /* On native Windows, access to a user's home directory + (via GetUserProfileDirectory) or to a user's environment + variables (via ExpandEnvironmentStringsForUser) requires + the credentials of the user. Therefore we cannot support + the ~user syntax on this platform. + Handling ~user specially (and treat it like plain ~) if + user is getenv ("USERNAME") would not be a good idea, + since it would make people think that ~user is supported + in general. */ + if (flags & GLOB_TILDE_CHECK) + { + retval = GLOB_NOMATCH; + goto out; + } +#endif /* WINDOWS32 */ } } @@ -1262,6 +1270,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { size_t dirlen = strlen (directory); void *stream = NULL; + struct scratch_buffer s; + scratch_buffer_init (&s); # define GLOBNAMES_MEMBERS(nnames) \ struct globnames *next; size_t count; char *name[nnames]; struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) }; @@ -1295,7 +1305,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, size_t patlen = strlen (pattern); size_t fullsize; bool alloca_fullname - = (! size_add_wrapv (dirlen + 1, patlen + 1, &fullsize) + = (!ckd_add (&fullsize, dirlen + 1, patlen + 1) && glob_use_alloca (alloca_used, fullsize)); char *fullname; if (alloca_fullname) @@ -1333,6 +1343,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags, } else { + DIR *dirp = stream; + int dfd = dirfd (dirp); int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)); flags |= GLOB_MAGCHAR; @@ -1360,8 +1372,32 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (flags & GLOB_ONLYDIR) switch (readdir_result_type (d)) { - case DT_DIR: case DT_LNK: case DT_UNKNOWN: break; default: continue; + case DT_DIR: break; + case DT_LNK: case DT_UNKNOWN: + /* The filesystem was too lazy to give us a hint, + so we have to do it the hard way. */ + if (__glibc_unlikely (dfd < 0 || flags & GLOB_ALTDIRFUNC)) + { + size_t namelen = strlen (d.name); + size_t need = dirlen + 1 + namelen + 1; + if (s.length < need + && !scratch_buffer_set_array_size (&s, need, 1)) + goto memory_error; + char *p = mempcpy (s.data, directory, dirlen); + *p = '/'; + p += p[-1] != '/'; + memcpy (p, d.name, namelen + 1); + if (! is_dir (s.data, flags, pglob)) + continue; + } + else + { + struct_stat64 st64; + if (! (GLOB_FSTATAT64 (dfd, d.name, &st64, 0) == 0 + && S_ISDIR (st64.st_mode))) + continue; + } } if (fnmatch (pattern, d.name, fnm_flags) == 0) @@ -1493,5 +1529,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags, __set_errno (save); } + scratch_buffer_free (&s); return result; } |