From c55fbd1ea768f9fdef34a01377702c0d72cbc213 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Tue, 9 Aug 2011 09:57:55 -0400 Subject: [PATCH] Implement scandirat function --- ChangeLog | 23 ++++ NEWS | 5 +- Versions.def | 1 + dirent/Makefile | 5 +- dirent/Versions | 3 + dirent/dirent.h | 45 ++++++- dirent/opendir.c | 13 ++- dirent/scandir.c | 122 +------------------ dirent/scandir64.c | 9 +- dirent/scandirat.c | 143 +++++++++++++++++++++++ dirent/scandirat64.c | 28 +++++ include/dirent.h | 14 +++ sysdeps/unix/opendir.c | 21 +++- sysdeps/unix/sysv/linux/i386/scandir64.c | 102 +++++++++++++++- sysdeps/wordsize-64/scandirat.c | 6 + sysdeps/wordsize-64/scandirat64.c | 1 + 16 files changed, 398 insertions(+), 143 deletions(-) create mode 100644 dirent/scandirat.c create mode 100644 dirent/scandirat64.c create mode 100644 sysdeps/wordsize-64/scandirat.c create mode 100644 sysdeps/wordsize-64/scandirat64.c diff --git a/ChangeLog b/ChangeLog index 4b6a62f07a..3ca8232372 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2011-08-09 Ulrich Drepper + + * Versions.def [libc]: Add GLIBC_2.15. + * dirent/Makefile (routines): Add scandirat and scandirat64. + * dirent/Versions [libc]: Export scandirat and scandirat64 for + GLIBC_2.15. + * dirent/dirent.h: Declare scandirat and scandirat64. + * dirent/scandirat.c: New file. + * dirent/scandirat64.c: New file. + * sysdeps/wordsize-64/scandirat.c: New file. + * sysdeps/wordsize-64/scandirat64.c: New file. + * dirent/opendir.c: Define opendirat. + * dirent/scandir.c: Move code to scandirat.c. Implement scandir + using scandirat. + * dirent/scandir64.c: Adjust for scandir.c change. + * include/dirent.h: Define scandir_cancel_struct. Declare __opendirat, + __scandirat64, and __scandir_cancel_handler. + * sysdeps/unix/opendir.c: Rename __opendir to __opendirat. Take + additional parameter and use openat instead of open (outside of ld.so). + Add new __opendir as wrapper around __opendirat. + * sysdeps/unix/sysv/linux/i386/scandir64.c: Reimplement __old_scandir64 + here without requiring old scandirat implementation. + 2011-08-08 Ulrich Drepper * dirent/scandir.c (cancel_handler): Renamed to diff --git a/NEWS b/NEWS index 1abcae1f9d..78810a9d5e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU C Library NEWS -- history of user-visible changes. 2011-7-23 +GNU C Library NEWS -- history of user-visible changes. 2011-8-8 Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc. See the end for copying conditions. @@ -26,6 +26,9 @@ Version 2.15 * Optimized strcat and strncat on x86-64. Contributed by Liubov Dmitrieva. + +* New interfaces: scandirat, scandirat64 + Implemented by Ulrich Drepper. Version 2.14 diff --git a/Versions.def b/Versions.def index 666ea73108..98c28001ac 100644 --- a/Versions.def +++ b/Versions.def @@ -31,6 +31,7 @@ libc { GLIBC_2.12 GLIBC_2.13 GLIBC_2.14 + GLIBC_2.15 %ifdef USE_IN_LIBIO HURD_CTHREADS_0.3 %endif diff --git a/dirent/Makefile b/dirent/Makefile index ef639f2095..07f706e678 100644 --- a/dirent/Makefile +++ b/dirent/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1991-2000,2002,2003,2005,2006 Free Software Foundation, Inc. +# Copyright (C) 1991-2000,2002,2003,2005,2006,2011 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 @@ -25,7 +25,8 @@ headers := dirent.h bits/dirent.h routines := opendir closedir readdir readdir_r rewinddir \ seekdir telldir scandir alphasort versionsort \ getdents getdents64 dirfd readdir64 readdir64_r scandir64 \ - alphasort64 versionsort64 fdopendir + alphasort64 versionsort64 fdopendir \ + scandirat scandirat64 distribute := dirstream.h tests := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \ diff --git a/dirent/Versions b/dirent/Versions index 41c1584426..d976d373f6 100644 --- a/dirent/Versions +++ b/dirent/Versions @@ -44,4 +44,7 @@ libc { GLIBC_2.4 { fdopendir; } + GLIBC_2.15 { + scandirat; scandirat64; + } } diff --git a/dirent/dirent.h b/dirent/dirent.h index bb4ede1580..6a5a0ef81c 100644 --- a/dirent/dirent.h +++ b/dirent/dirent.h @@ -1,4 +1,5 @@ -/* Copyright (C) 1991-2000,2003-2005,2009,2010 Free Software Foundation, Inc. +/* Copyright (C) 1991-2000,2003-2005,2009,2010,2011 + 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 @@ -247,7 +248,10 @@ extern int dirfd (DIR *__dirp) __THROW __nonnull ((1)); /* Scan the directory DIR, calling SELECTOR on each directory entry. Entries for which SELECT returns nonzero are individually malloc'd, sorted using qsort with CMP, and collected in a malloc'd array in - *NAMELIST. Returns the number of entries selected, or -1 on error. */ + *NAMELIST. Returns the number of entries selected, or -1 on error. + + This function is a cancellation point and therefore not marked with + __THROW. */ # ifndef __USE_FILE_OFFSET64 extern int scandir (__const char *__restrict __dir, struct dirent ***__restrict __namelist, @@ -280,6 +284,43 @@ extern int scandir64 (__const char *__restrict __dir, __nonnull ((1, 2)); # endif +# ifdef __USE_GNU +/* Similar to `scandir' but a relative DIR name is interpreted relative + to the directory for which DFD is a descriptor. + + This function is a cancellation point and therefore not marked with + __THROW. */ +# ifndef __USE_FILE_OFFSET64 +extern int scandirat (int __dfd, __const char *__restrict __dir, + struct dirent ***__restrict __namelist, + int (*__selector) (__const struct dirent *), + int (*__cmp) (__const struct dirent **, + __const struct dirent **)) + __nonnull ((2, 3)); +# else +# ifdef __REDIRECT +extern int __REDIRECT (scandirat, + (int __dfd, __const char *__restrict __dir, + struct dirent ***__restrict __namelist, + int (*__selector) (__const struct dirent *), + int (*__cmp) (__const struct dirent **, + __const struct dirent **)), + scandirat64) __nonnull ((2, 3)); +# else +# define scandirat scandirat64 +# endif +# endif + +/* This function is like `scandir' but it uses the 64bit dirent structure. + Please note that the CMP function must now work with struct dirent64 **. */ +extern int scandirat64 (int __dfd, __const char *__restrict __dir, + struct dirent64 ***__restrict __namelist, + int (*__selector) (__const struct dirent64 *), + int (*__cmp) (__const struct dirent64 **, + __const struct dirent64 **)) + __nonnull ((2, 3)); +# endif + /* Function to compare two `struct dirent's alphabetically. */ # ifndef __USE_FILE_OFFSET64 extern int alphasort (__const struct dirent **__e1, diff --git a/dirent/opendir.c b/dirent/opendir.c index 771013f6eb..6375b6786a 100644 --- a/dirent/opendir.c +++ b/dirent/opendir.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1995, 1996, 1997, 2011 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,12 +21,19 @@ #include +DIR * +__opendirat (int dfd, const char *name) +{ + __set_errno (ENOSYS); + return NULL; +} + + /* Open a directory stream on NAME. */ DIR * __opendir (const char *name) { - __set_errno (ENOSYS); - return NULL; + return __opendirat (AT_FDCWD, name); } weak_alias (__opendir, opendir) diff --git a/dirent/scandir.c b/dirent/scandir.c index e90b942b1d..3f69a8db77 100644 --- a/dirent/scandir.c +++ b/dirent/scandir.c @@ -18,44 +18,14 @@ 02111-1307 USA. */ #include -#include -#include -#include -#include +#include #ifndef SCANDIR # define SCANDIR scandir -# define READDIR __readdir +# define SCANDIRAT scandirat # define DIRENT_TYPE struct dirent #endif -#ifndef SCANDIR_CANCEL -# define SCANDIR_CANCEL -struct scandir_cancel_struct -{ - DIR *dp; - void *v; - size_t cnt; -}; - -# ifndef SKIP_SCANDIR_CANCEL -void -__scandir_cancel_handler (void *arg) -{ - struct scandir_cancel_struct *cp = arg; - size_t i; - void **v = cp->v; - - for (i = 0; i < cp->cnt; ++i) - free (v[i]); - free (v); - (void) __closedir (cp->dp); -} -# else -extern void __scandir_cancel_handler (void *arg); -# endif -#endif - int SCANDIR (dir, namelist, select, cmp) @@ -64,91 +34,5 @@ SCANDIR (dir, namelist, select, cmp) int (*select) (const DIRENT_TYPE *); int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **); { - DIR *dp = __opendir (dir); - DIRENT_TYPE **v = NULL; - size_t vsize = 0; - struct scandir_cancel_struct c; - DIRENT_TYPE *d; - int save; - - if (dp == NULL) - return -1; - - save = errno; - __set_errno (0); - - c.dp = dp; - c.v = NULL; - c.cnt = 0; - __libc_cleanup_push (__scandir_cancel_handler, &c); - - while ((d = READDIR (dp)) != NULL) - { - int use_it = select == NULL; - - if (! use_it) - { - use_it = select (d); - /* The select function might have changed errno. It was - zero before and it need to be again to make the latter - tests work. */ - __set_errno (0); - } - - if (use_it) - { - DIRENT_TYPE *vnew; - size_t dsize; - - /* Ignore errors from select or readdir */ - __set_errno (0); - - if (__builtin_expect (c.cnt == vsize, 0)) - { - DIRENT_TYPE **new; - if (vsize == 0) - vsize = 10; - else - vsize *= 2; - new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v)); - if (new == NULL) - break; - v = new; - c.v = (void *) v; - } - - dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; - vnew = (DIRENT_TYPE *) malloc (dsize); - if (vnew == NULL) - break; - - v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); - } - } - - if (__builtin_expect (errno, 0) != 0) - { - save = errno; - - while (c.cnt > 0) - free (v[--c.cnt]); - free (v); - c.cnt = -1; - } - else - { - /* Sort the list if we have a comparison function to sort with. */ - if (cmp != NULL) - qsort (v, c.cnt, sizeof (*v), - (int (*) (const void *, const void *)) cmp); - - *namelist = v; - } - - __libc_cleanup_pop (0); - - (void) __closedir (dp); - __set_errno (save); - - return c.cnt; + return SCANDIRAT (AT_FDCWD, dir, namelist, select, cmp); } diff --git a/dirent/scandir64.c b/dirent/scandir64.c index 274822e125..21a936e4bb 100644 --- a/dirent/scandir64.c +++ b/dirent/scandir64.c @@ -19,14 +19,7 @@ #include #define SCANDIR scandir64 -#define READDIR __readdir64 +#define SCANDIRAT scandirat64 #define DIRENT_TYPE struct dirent64 -#define SKIP_SCANDIR_CANCEL 1 - -int scandir64 (__const char *__restrict __dir, - struct dirent64 ***__restrict __namelist, - int (*__selector) (__const struct dirent64 *), - int (*__cmp) (__const struct dirent64 **, - __const struct dirent64 **)); #include diff --git a/dirent/scandirat.c b/dirent/scandirat.c new file mode 100644 index 0000000000..e6f5ece76f --- /dev/null +++ b/dirent/scandirat.c @@ -0,0 +1,143 @@ +/* Copyright (C) 1992-1998,2000,2002,2003,2009,2011 + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + +#ifndef SCANDIRAT +# define SCANDIRAT scandirat +# define READDIR __readdir +# define DIRENT_TYPE struct dirent +#endif + +#ifndef SKIP_SCANDIR_CANCEL +void +__scandir_cancel_handler (void *arg) +{ + struct scandir_cancel_struct *cp = arg; + size_t i; + void **v = cp->v; + + for (i = 0; i < cp->cnt; ++i) + free (v[i]); + free (v); + (void) __closedir (cp->dp); +} +#endif + + +int +SCANDIRAT (dfd, dir, namelist, select, cmp) + int dfd; + const char *dir; + DIRENT_TYPE ***namelist; + int (*select) (const DIRENT_TYPE *); + int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **); +{ + DIR *dp = __opendirat (dfd, dir); + DIRENT_TYPE **v = NULL; + size_t vsize = 0; + struct scandir_cancel_struct c; + DIRENT_TYPE *d; + int save; + + if (dp == NULL) + return -1; + + save = errno; + __set_errno (0); + + c.dp = dp; + c.v = NULL; + c.cnt = 0; + __libc_cleanup_push (__scandir_cancel_handler, &c); + + while ((d = READDIR (dp)) != NULL) + { + int use_it = select == NULL; + + if (! use_it) + { + use_it = select (d); + /* The select function might have changed errno. It was + zero before and it need to be again to make the latter + tests work. */ + __set_errno (0); + } + + if (use_it) + { + DIRENT_TYPE *vnew; + size_t dsize; + + /* Ignore errors from select or readdir */ + __set_errno (0); + + if (__builtin_expect (c.cnt == vsize, 0)) + { + DIRENT_TYPE **new; + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v)); + if (new == NULL) + break; + v = new; + c.v = (void *) v; + } + + dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + vnew = (DIRENT_TYPE *) malloc (dsize); + if (vnew == NULL) + break; + + v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); + } + } + + if (__builtin_expect (errno, 0) != 0) + { + save = errno; + + while (c.cnt > 0) + free (v[--c.cnt]); + free (v); + c.cnt = -1; + } + else + { + /* Sort the list if we have a comparison function to sort with. */ + if (cmp != NULL) + qsort (v, c.cnt, sizeof (*v), + (int (*) (const void *, const void *)) cmp); + + *namelist = v; + } + + __libc_cleanup_pop (0); + + (void) __closedir (dp); + __set_errno (save); + + return c.cnt; +} diff --git a/dirent/scandirat64.c b/dirent/scandirat64.c new file mode 100644 index 0000000000..c4afb90d3d --- /dev/null +++ b/dirent/scandirat64.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2000, 2009, 2011 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +#define SCANDIRAT __scandirat64 +#define READDIR __readdir64 +#define DIRENT_TYPE struct dirent64 +#define SKIP_SCANDIR_CANCEL 1 + +#include "scandirat.c" + +weak_alias (__scandirat64, scandirat64) diff --git a/include/dirent.h b/include/dirent.h index 0f8f1cbe88..9ed5876892 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -4,8 +4,16 @@ # include # include +struct scandir_cancel_struct +{ + DIR *dp; + void *v; + size_t cnt; +}; + /* Now define the internal interfaces. */ extern DIR *__opendir (__const char *__name); +extern DIR *__opendirat (int dfd, __const char *__name) internal_function; extern DIR *__fdopendir (int __fd); extern int __closedir (DIR *__dirp); extern struct dirent *__readdir (DIR *__dirp); @@ -19,6 +27,11 @@ extern int __scandir64 (__const char * __dir, int (*__selector) (__const struct dirent64 *), int (*__cmp) (__const struct dirent64 **, __const struct dirent64 **)); +extern int __scandirat64 (int __dfd, __const char * __dir, + struct dirent64 *** __namelist, + int (*__selector) (__const struct dirent64 *), + int (*__cmp) (__const struct dirent64 **, + __const struct dirent64 **)); extern __ssize_t __getdents (int __fd, char *__buf, size_t __nbytes) internal_function; extern __ssize_t __getdents64 (int __fd, char *__buf, size_t __nbytes) @@ -31,6 +44,7 @@ extern int __versionsort64 (const struct dirent64 **a, extern DIR *__alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) internal_function; +extern void __scandir_cancel_handler (void *arg); libc_hidden_proto (rewinddir) diff --git a/sysdeps/unix/opendir.c b/sysdeps/unix/opendir.c index c2d1ddaf88..58d31764da 100644 --- a/sysdeps/unix/opendir.c +++ b/sysdeps/unix/opendir.c @@ -17,6 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include #include #include #include @@ -76,9 +77,9 @@ tryopen_o_directory (void) #endif -/* Open a directory stream on NAME. */ DIR * -__opendir (const char *name) +internal_function +__opendirat (int dfd, const char *name) { struct stat64 statbuf; struct stat64 *statp = NULL; @@ -116,7 +117,13 @@ __opendir (const char *name) #ifdef O_CLOEXEC flags |= O_CLOEXEC; #endif - int fd = open_not_cancel_2 (name, flags); + int fd; +#ifdef IS_IN_rtld + assert (dfd == AT_FDCWD); + fd = open_not_cancel_2 (name, flags); +#else + fd = openat_not_cancel_3 (dfd, name, flags); +#endif if (__builtin_expect (fd, 0) < 0) return NULL; @@ -140,6 +147,14 @@ __opendir (const char *name) return __alloc_dir (fd, true, 0, statp); } + + +/* Open a directory stream on NAME. */ +DIR * +__opendir (const char *name) +{ + return __opendirat (AT_FDCWD, name); +} weak_alias (__opendir, opendir) diff --git a/sysdeps/unix/sysv/linux/i386/scandir64.c b/sysdeps/unix/sysv/linux/i386/scandir64.c index 837e1b9438..dacac0a44c 100644 --- a/sysdeps/unix/sysv/linux/i386/scandir64.c +++ b/sysdeps/unix/sysv/linux/i386/scandir64.c @@ -19,6 +19,7 @@ #include #define SCANDIR __scandir64 +#define SCANDIRAT __scandirat64 #define READDIR __readdir64 #define DIRENT_TYPE struct dirent64 #define SKIP_SCANDIR_CANCEL 1 @@ -34,15 +35,106 @@ versioned_symbol (libc, __scandir64, scandir64, GLIBC_2_2); #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +# include +# include "olddirent.h" -#include +int +__old_scandir64 (dir, namelist, select, cmp) + const char *dir; + struct __old_dirent64 ***namelist; + int (*select) (const struct __old_dirent64 *); + int (*cmp) (const struct __old_dirent64 **, + const struct __old_dirent64 **); +{ + DIR *dp = __opendir (dir); + struct __old_dirent64 **v = NULL; + size_t vsize = 0; + struct scandir_cancel_struct c; + struct __old_dirent64 *d; + int save; -#define SCANDIR attribute_compat_text_section __old_scandir64 -#define READDIR __old_readdir64 -#define DIRENT_TYPE struct __old_dirent64 + if (dp == NULL) + return -1; -#include + save = errno; + __set_errno (0); + c.dp = dp; + c.v = NULL; + c.cnt = 0; + __libc_cleanup_push (__scandir_cancel_handler, &c); + + while ((d = __old_readdir64 (dp)) != NULL) + { + int use_it = select == NULL; + + if (! use_it) + { + use_it = select (d); + /* The select function might have changed errno. It was + zero before and it need to be again to make the latter + tests work. */ + __set_errno (0); + } + + if (use_it) + { + struct __old_dirent64 *vnew; + size_t dsize; + + /* Ignore errors from select or readdir */ + __set_errno (0); + + if (__builtin_expect (c.cnt == vsize, 0)) + { + struct __old_dirent64 **new; + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + new = (struct __old_dirent64 **) realloc (v, + vsize * sizeof (*v)); + if (new == NULL) + break; + v = new; + c.v = (void *) v; + } + + dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + vnew = (struct __old_dirent64 *) malloc (dsize); + if (vnew == NULL) + break; + + v[c.cnt++] = (struct __old_dirent64 *) memcpy (vnew, d, dsize); + } + } + + if (__builtin_expect (errno, 0) != 0) + { + save = errno; + + while (c.cnt > 0) + free (v[--c.cnt]); + free (v); + c.cnt = -1; + } + else + { + /* Sort the list if we have a comparison function to sort with. */ + if (cmp != NULL) + qsort (v, c.cnt, sizeof (*v), + (int (*) (const void *, const void *)) cmp); + + *namelist = v; + } + + __libc_cleanup_pop (0); + + (void) __closedir (dp); + __set_errno (save); + + return c.cnt; +} compat_symbol (libc, __old_scandir64, scandir64, GLIBC_2_1); #endif diff --git a/sysdeps/wordsize-64/scandirat.c b/sysdeps/wordsize-64/scandirat.c new file mode 100644 index 0000000000..02b8fdee0f --- /dev/null +++ b/sysdeps/wordsize-64/scandirat.c @@ -0,0 +1,6 @@ +#define scandirat64 scandirat64_renamed + +#include "../../dirent/scandirat.c" + +#undef scandirat64 +weak_alias (scandirat, scandirat64) diff --git a/sysdeps/wordsize-64/scandirat64.c b/sysdeps/wordsize-64/scandirat64.c new file mode 100644 index 0000000000..fb938654a4 --- /dev/null +++ b/sysdeps/wordsize-64/scandirat64.c @@ -0,0 +1 @@ +/* Defined in scandirat.c. */