1 /* Common implementation for scandir{at}.
2    Copyright (C) 2018-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <string.h>
20 #include <errno.h>
21 
22 int
SCANDIR_TAIL(DIR * dp,DIRENT_TYPE *** namelist,int (* select)(const DIRENT_TYPE *),int (* cmp)(const DIRENT_TYPE **,const DIRENT_TYPE **))23 SCANDIR_TAIL (DIR *dp,
24               DIRENT_TYPE ***namelist,
25               int (*select) (const DIRENT_TYPE *),
26               int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **))
27 {
28   if (dp == NULL)
29     return -1;
30 
31   int save = errno;
32   __set_errno (0);
33 
34   int result;
35   struct scandir_cancel_struct c = { .dp = dp };
36   __libc_cleanup_push (&__scandir_cancel_handler, &c);
37 
38   DIRENT_TYPE **v = NULL;
39   size_t vsize = 0;
40   DIRENT_TYPE *d;
41   while ((d = READDIR (dp)) != NULL)
42     {
43       if (select != NULL)
44         {
45           int selected = (*select) (d);
46 
47 	  /* The SELECT function might have set errno to non-zero on
48 	     success.  It was zero before and it needs to be again to
49 	     make the later tests work.  */
50 	  __set_errno (0);
51 
52           if (!selected)
53             continue;
54         }
55 
56       if (__glibc_unlikely (c.cnt == vsize))
57         {
58           if (vsize == 0)
59             vsize = 10;
60           else
61             vsize *= 2;
62           DIRENT_TYPE **new = realloc (v, vsize * sizeof *v);
63           if (new == NULL)
64             break;
65           c.v = v = new;
66         }
67 
68       size_t dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
69       DIRENT_TYPE *vnew = malloc (dsize);
70       if (vnew == NULL)
71         break;
72       v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
73 
74       /* Ignore errors from readdir, malloc or realloc.  These functions
75 	 might have set errno to non-zero on success.  It was zero before
76 	 and it needs to be again to make the latter tests work.  */
77       __set_errno (0);
78     }
79 
80   if (__glibc_likely (errno == 0))
81     {
82       __closedir (dp);
83 
84       /* Sort the list if we have a comparison function to sort with.  */
85       if (cmp != NULL)
86 	qsort (v, c.cnt, sizeof *v, (__compar_fn_t) cmp);
87 
88       *namelist = v;
89       result = c.cnt;
90     }
91   else
92     {
93       /* This frees everything and calls closedir.  */
94       __scandir_cancel_handler (&c);
95       result = -1;
96     }
97 
98   __libc_cleanup_pop (0);
99 
100   if (result >= 0)
101     __set_errno (save);
102   return result;
103 }
104