1 /* Copyright (C) 1992-1998, 2000, 2002-2003, 2009-2020 Free Software
2    Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 3, or (at your option) any
8    later version.
9 
10    This program 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
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
17 
18 #include <config.h>
19 
20 #include <dirent.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #if _LIBC
26 # include <bits/libc-lock.h>
27 #endif
28 
29 #undef select
30 
31 #ifndef _D_EXACT_NAMLEN
32 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
33 #endif
34 #ifndef _D_ALLOC_NAMLEN
35 # ifndef __KLIBC__
36 #  define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
37 # else
38 /* On OS/2 kLIBC, d_name is not the last field of struct dirent. See
39    <https://trac.netlabs.org/libc/browser/branches/libc-0.6/src/emx/include/sys/dirent.h#L68>.  */
40 #  include <stddef.h>
41 #  define _D_ALLOC_NAMLEN(d) (sizeof (struct dirent) - \
42                               offsetof (struct dirent, d_name))
43 # endif
44 #endif
45 
46 #if _LIBC
47 # ifndef SCANDIR
48 #  define SCANDIR scandir
49 #  define READDIR __readdir
50 #  define DIRENT_TYPE struct dirent
51 # endif
52 #else
53 # define SCANDIR scandir
54 # define READDIR readdir
55 # define DIRENT_TYPE struct dirent
56 # define __opendir opendir
57 # define __closedir closedir
58 # define __set_errno(val) errno = (val)
59 
60 /* The results of opendir() in this file are not used with dirfd and fchdir,
61    and we do not leak fds to any single-threaded code that could use stdio,
62    therefore save some unnecessary recursion in fchdir.c and opendir_safer.c.
63    FIXME - if the kernel ever adds support for multi-thread safety for
64    avoiding standard fds, then we should use opendir_safer.  */
65 # undef opendir
66 # undef closedir
67 #endif
68 
69 #ifndef SCANDIR_CANCEL
70 # define SCANDIR_CANCEL
71 struct scandir_cancel_struct
72 {
73   DIR *dp;
74   void *v;
75   size_t cnt;
76 };
77 
78 # if _LIBC
79 static void
cancel_handler(void * arg)80 cancel_handler (void *arg)
81 {
82   struct scandir_cancel_struct *cp = arg;
83   size_t i;
84   void **v = cp->v;
85 
86   for (i = 0; i < cp->cnt; ++i)
87     free (v[i]);
88   free (v);
89   (void) __closedir (cp->dp);
90 }
91 # endif
92 #endif
93 
94 
95 int
96 #ifndef __KLIBC__
SCANDIR(const char * dir,DIRENT_TYPE *** namelist,int (* select)(const DIRENT_TYPE *),int (* cmp)(const DIRENT_TYPE **,const DIRENT_TYPE **))97 SCANDIR (const char *dir,
98          DIRENT_TYPE ***namelist,
99          int (*select) (const DIRENT_TYPE *),
100          int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **))
101 #else
102 /* On OS/2 kLIBC, scandir() declaration is different from POSIX. See
103    <https://trac.netlabs.org/libc/browser/branches/libc-0.6/src/emx/include/dirent.h#L141>.  */
104 SCANDIR (const char *dir,
105          DIRENT_TYPE ***namelist,
106          int (*select) (DIRENT_TYPE *),
107          int (*cmp) (const void *, const void *))
108 #endif
109 {
110   DIR *dp = __opendir (dir);
111   DIRENT_TYPE **v = NULL;
112   size_t vsize = 0;
113   struct scandir_cancel_struct c;
114   DIRENT_TYPE *d;
115   int save;
116 
117   if (dp == NULL)
118     return -1;
119 
120   save = errno;
121   __set_errno (0);
122 
123   c.dp = dp;
124   c.v = NULL;
125   c.cnt = 0;
126 #if _LIBC
127   __libc_cleanup_push (cancel_handler, &c);
128 #endif
129 
130   while ((d = READDIR (dp)) != NULL)
131     {
132       int use_it = select == NULL;
133 
134       if (! use_it)
135         {
136           use_it = select (d);
137           /* The select function might have changed errno.  It was
138              zero before and it need to be again to make the latter
139              tests work.  */
140           __set_errno (0);
141         }
142 
143       if (use_it)
144         {
145           DIRENT_TYPE *vnew;
146           size_t dsize;
147 
148           /* Ignore errors from select or readdir */
149           __set_errno (0);
150 
151           if (__builtin_expect (c.cnt == vsize, 0))
152             {
153               DIRENT_TYPE **new;
154               if (vsize == 0)
155                 vsize = 10;
156               else
157                 vsize *= 2;
158               new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
159               if (new == NULL)
160                 break;
161               v = new;
162               c.v = (void *) v;
163             }
164 
165           dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
166           vnew = (DIRENT_TYPE *) malloc (dsize);
167           if (vnew == NULL)
168             break;
169 
170           v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
171         }
172     }
173 
174   if (__builtin_expect (errno, 0) != 0)
175     {
176       save = errno;
177 
178       while (c.cnt > 0)
179         free (v[--c.cnt]);
180       free (v);
181       c.cnt = -1;
182     }
183   else
184     {
185       /* Sort the list if we have a comparison function to sort with.  */
186       if (cmp != NULL)
187         qsort (v, c.cnt, sizeof (*v), (int (*) (const void *, const void *)) cmp);
188 
189       *namelist = v;
190     }
191 
192 #if _LIBC
193   __libc_cleanup_pop (0);
194 #endif
195 
196   (void) __closedir (dp);
197   __set_errno (save);
198 
199   return c.cnt;
200 }
201