diff --git a/ChangeLog b/ChangeLog index 00079ec3b3..6ecfde29c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,23 @@ 2015-05-13 Roland McGrath + * dirent/scandir64-tail.c: New file. + * dirent/Makefile (routines): Add them. + * include/dirent.h (__scandir_tail, __scandir64_tail): Declare them. + * dirent/scandir.c [!SCANDIR] (SCANDIRAT): Macro removed. + [!SCANDIR] (SCANDIR_TAIL): New macro. + (SCANDIR): Call __opendir and __scandir_tail, not __scandirat. + * dirent/scandir64.c [!_DIRENT_MATCHES_DIRENT64] + (SCANDIRAT): Macro removed. + (SCANDIR_TAIL): New macro. + * dirent/scandirat.c [!SCANDIRAT] (READDIR): Macro removed. + [!SCANDIRAT] (SCANDIR_TAIL): New macro. + (SCANDIRAT): Just call __opendirat and __scandir_tail. + * dirent/scandirat64.c [!_DIRENT_MATCHES_DIRENT64] + (READDIR): Macro removed. + (SCANDIR_TAIL): New macro. + * sysdeps/unix/sysv/linux/i386/scandir64.c (READDIR): Macro removed. + (SCANDIR_TAIL): New macro. + * dirent/scandirat.c (__scandir_cancel_handler): Function moved ... * dirent/scandir-cancel.c: ... to this new file. * dirent/Makefile (routines): Add it. diff --git a/dirent/Makefile b/dirent/Makefile index 2a97649c04..551372ebba 100644 --- a/dirent/Makefile +++ b/dirent/Makefile @@ -27,7 +27,8 @@ routines := opendir closedir readdir readdir_r rewinddir \ seekdir telldir scandir alphasort versionsort \ getdents getdents64 dirfd readdir64 readdir64_r scandir64 \ alphasort64 versionsort64 fdopendir \ - scandirat scandirat64 scandir-cancel + scandirat scandirat64 \ + scandir-cancel scandir-tail scandir64-tail tests := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \ tst-fdopendir2 tst-scandir tst-scandir64 diff --git a/dirent/scandir-tail.c b/dirent/scandir-tail.c new file mode 100644 index 0000000000..4560834af5 --- /dev/null +++ b/dirent/scandir-tail.c @@ -0,0 +1,110 @@ +/* Logic guts of scandir*. + Copyright (C) 1992-2015 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 + +#ifndef SCANDIR_TAIL +# define SCANDIR_TAIL __scandir_tail +# define READDIR __readdir +# define DIRENT_TYPE struct dirent +#endif + +internal_function +int +SCANDIR_TAIL (DIR *dp, + DIRENT_TYPE ***namelist, + int (*select) (const DIRENT_TYPE *), + int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **)) +{ + if (dp == NULL) + return -1; + + int save = errno; + __set_errno (0); + + int result; + struct scandir_cancel_struct c = { .dp = dp }; + __libc_cleanup_push (&__scandir_cancel_handler, &c); + + DIRENT_TYPE **v = NULL; + size_t vsize = 0; + DIRENT_TYPE *d; + while ((d = READDIR (dp)) != NULL) + { + if (select != NULL) + { + int selected = (*select) (d); + + /* The SELECT function might have changed errno. It was + zero before and it need to be again to make the later + tests work. */ + __set_errno (0); + + if (!selected) + continue; + } + else + __set_errno (0); + + if (__glibc_unlikely (c.cnt == vsize)) + { + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + DIRENT_TYPE **new = realloc (v, vsize * sizeof *v); + if (new == NULL) + break; + c.v = v = new; + } + + size_t dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + DIRENT_TYPE *vnew = malloc (dsize); + if (vnew == NULL) + break; + v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); + } + + if (__glibc_likely (errno == 0)) + { + __closedir (dp); + + /* Sort the list if we have a comparison function to sort with. */ + if (cmp != NULL) + qsort (v, c.cnt, sizeof *v, (__compar_fn_t) cmp); + + *namelist = v; + result = c.cnt; + } + else + { + /* This frees everything and calls closedir. */ + __scandir_cancel_handler (&c); + result = -1; + } + + __libc_cleanup_pop (0); + + if (result >= 0) + __set_errno (save); + return result; +} diff --git a/dirent/scandir.c b/dirent/scandir.c index 99c968181c..c0c3f7a3cf 100644 --- a/dirent/scandir.c +++ b/dirent/scandir.c @@ -24,23 +24,20 @@ #undef scandir64 -#include - #ifndef SCANDIR -# define SCANDIR scandir -# define SCANDIRAT __scandirat -# define DIRENT_TYPE struct dirent +# define SCANDIR scandir +# define SCANDIR_TAIL __scandir_tail +# define DIRENT_TYPE struct dirent #endif int -SCANDIR (dir, namelist, select, cmp) - const char *dir; - DIRENT_TYPE ***namelist; - int (*select) (const DIRENT_TYPE *); - int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **); +SCANDIR (const char *dir, + DIRENT_TYPE ***namelist, + int (*select) (const DIRENT_TYPE *), + int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **)) { - return SCANDIRAT (AT_FDCWD, dir, namelist, select, cmp); + return SCANDIR_TAIL (__opendir (dir), namelist, select, cmp); } #ifdef _DIRENT_MATCHES_DIRENT64 diff --git a/dirent/scandir64-tail.c b/dirent/scandir64-tail.c new file mode 100644 index 0000000000..b10dd0a5a8 --- /dev/null +++ b/dirent/scandir64-tail.c @@ -0,0 +1,26 @@ +/* Logic guts of scandir*64. + Copyright (C) 2015 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 + +#ifndef _DIRENT_MATCHES_DIRENT64 +# define SCANDIR_TAIL __scandir64_tail +# define READDIR __readdir64 +# define DIRENT_TYPE struct dirent64 +# include +#endif diff --git a/dirent/scandir64.c b/dirent/scandir64.c index d9f780fa33..fbb1866348 100644 --- a/dirent/scandir64.c +++ b/dirent/scandir64.c @@ -20,10 +20,10 @@ /* scandir.c defines scandir64 as an alias if _DIRENT_MATCHES_DIRENT64. */ #ifndef _DIRENT_MATCHES_DIRENT64 -#define SCANDIR scandir64 -#define SCANDIRAT scandirat64 -#define DIRENT_TYPE struct dirent64 +# define SCANDIR scandir64 +# define SCANDIR_TAIL __scandir64_tail +# define DIRENT_TYPE struct dirent64 -#include +# include #endif diff --git a/dirent/scandirat.c b/dirent/scandirat.c index d7ae5e52c8..41725af39a 100644 --- a/dirent/scandirat.c +++ b/dirent/scandirat.c @@ -24,114 +24,20 @@ #undef scandirat64 -#include -#include -#include -#include - #ifndef SCANDIRAT -# define SCANDIRAT __scandirat -# define READDIR __readdir -# define DIRENT_TYPE struct dirent +# define SCANDIRAT __scandirat +# define SCANDIR_TAIL __scandir_tail +# define DIRENT_TYPE struct dirent # define SCANDIRAT_WEAK_ALIAS #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 **); +SCANDIRAT (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 (__glibc_unlikely (c.cnt == vsize)) - { - 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 SCANDIR_TAIL (__opendirat (dfd, dir), namelist, select, cmp); } libc_hidden_def (SCANDIRAT) #ifdef SCANDIRAT_WEAK_ALIAS diff --git a/dirent/scandirat64.c b/dirent/scandirat64.c index fcdd1a9a73..e330a7bba6 100644 --- a/dirent/scandirat64.c +++ b/dirent/scandirat64.c @@ -20,10 +20,10 @@ /* scandirat.c defines scandirat64 as an alias if _DIRENT_MATCHES_DIRENT64. */ #ifndef _DIRENT_MATCHES_DIRENT64 -#define SCANDIRAT scandirat64 -#define READDIR __readdir64 -#define DIRENT_TYPE struct dirent64 +# define SCANDIRAT scandirat64 +# define SCANDIR_TAIL __scandir64_tail +# define DIRENT_TYPE struct dirent64 -#include "scandirat.c" +# include #endif diff --git a/include/dirent.h b/include/dirent.h index c50bd23783..258f2d0791 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -49,6 +49,26 @@ extern DIR *__alloc_dir (int fd, bool close_fd, int flags, extern __typeof (rewinddir) __rewinddir; extern void __scandir_cancel_handler (void *arg) attribute_hidden; +extern int __scandir_tail (DIR *dp, + struct dirent ***namelist, + int (*select) (const struct dirent *), + int (*cmp) (const struct dirent **, + const struct dirent **)) + internal_function attribute_hidden; +# ifdef _DIRENT_MATCHES_DIRENT64 +# define __scandir64_tail (dp, namelist, select, cmp) \ + __scandir_tail (dp, (struct dirent ***) (namelist), \ + (int (*) (const struct dirent *)) (select), \ + (int (*) (const struct dirent **, \ + const struct dirent **)) (cmp)) +# else +extern int __scandir64_tail (DIR *dp, + struct dirent64 ***namelist, + int (*select) (const struct dirent64 *), + int (*cmp) (const struct dirent64 **, + const struct dirent64 **)) + internal_function attribute_hidden; +# endif libc_hidden_proto (__rewinddir) extern __typeof (scandirat) __scandirat; diff --git a/sysdeps/unix/sysv/linux/i386/scandir64.c b/sysdeps/unix/sysv/linux/i386/scandir64.c index 824bf84bd2..86290780c4 100644 --- a/sysdeps/unix/sysv/linux/i386/scandir64.c +++ b/sysdeps/unix/sysv/linux/i386/scandir64.c @@ -17,22 +17,21 @@ #include -#define SCANDIR __scandir64 -#define SCANDIRAT scandirat64 -#define READDIR __readdir64 -#define DIRENT_TYPE struct dirent64 +#define SCANDIR __scandir64 +#define SCANDIR_TAIL __scandir64_tail +#define DIRENT_TYPE struct dirent64 #include -#undef SCANDIR -#undef READDIR -#undef DIRENT_TYPE +#undef SCANDIR +#undef SCANDIR_TAIL +#undef DIRENT_TYPE #include versioned_symbol (libc, __scandir64, scandir64, GLIBC_2_2); -#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +#if SHLIB_COMPAT (libc, GLIBC_2_1, GLIBC_2_2) # include # include # include "olddirent.h"