1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Scan the directory dirname calling select to make a list of selected
34 * directory entries then sort using qsort and compare routine dcomp.
35 * Returns the number of entries and a pointer to a list of pointers to
36 * struct dirent (through namelist). Returns -1 if there were any errors.
37 */
38
39 #include "namespace.h"
40 #include <dirent.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "un-namespace.h"
47
48 #ifdef I_AM_SCANDIR_B
49 #include "block_abi.h"
50 #define SELECT(x) CALL_BLOCK(select, x)
51 #ifndef __BLOCKS__
52 void qsort_b(void *, size_t, size_t, void *);
53 #endif
54 #else
55 #define SELECT(x) select(x)
56 #endif
57
58 #ifdef I_AM_SCANDIR_B
59 typedef DECLARE_BLOCK(int, select_block, const struct dirent *);
60 typedef DECLARE_BLOCK(int, dcomp_block, const struct dirent **,
61 const struct dirent **);
62 #else
63 static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk);
64 #endif
65
66 static int
67 #ifdef I_AM_SCANDIR_B
scandir_dirp_b(DIR * dirp,struct dirent *** namelist,select_block select,dcomp_block dcomp)68 scandir_dirp_b(DIR *dirp, struct dirent ***namelist, select_block select,
69 dcomp_block dcomp)
70 #else
71 scandir_dirp(DIR *dirp, struct dirent ***namelist,
72 int (*select)(const struct dirent *),
73 int (*dcomp)(const struct dirent **, const struct dirent **))
74 #endif
75 {
76 struct dirent *d, *p = NULL, **names = NULL, **names2;
77 size_t arraysz = 32, numitems = 0;
78 int serrno;
79
80 names = malloc(arraysz * sizeof(*names));
81 if (names == NULL)
82 return (-1);
83
84 while (errno = 0, (d = readdir(dirp)) != NULL) {
85 if (select != NULL && !SELECT(d))
86 continue; /* just selected names */
87 /*
88 * Make a minimum size copy of the data
89 */
90 p = malloc(_GENERIC_DIRSIZ(d));
91 if (p == NULL)
92 goto fail;
93 p->d_fileno = d->d_fileno;
94 p->d_type = d->d_type;
95 p->d_reclen = d->d_reclen;
96 p->d_namlen = d->d_namlen;
97 memcpy(p->d_name, d->d_name, p->d_namlen + 1);
98 /*
99 * Check to make sure the array has space left and
100 * realloc the maximum size.
101 */
102 if (numitems >= arraysz) {
103 arraysz = arraysz * 2;
104 names2 = reallocarray(names, arraysz, sizeof(*names));
105 if (names2 == NULL)
106 goto fail;
107 names = names2;
108 }
109 names[numitems++] = p;
110 }
111 /*
112 * Since we can't simultaneously return both -1 and a count, we
113 * must either suppress the error or discard the partial result.
114 * The latter seems the lesser of two evils.
115 */
116 if (errno != 0)
117 goto fail;
118 if (numitems > 0 && dcomp != NULL) {
119 #ifdef I_AM_SCANDIR_B
120 qsort_b(names, numitems, sizeof(struct dirent *),
121 (void *)dcomp);
122 #else
123 qsort_r(names, numitems, sizeof(struct dirent *),
124 scandir_thunk_cmp, &dcomp);
125 #endif
126 }
127 *namelist = names;
128 return (numitems);
129
130 fail:
131 serrno = errno;
132 if (numitems == 0 || names[numitems - 1] != p)
133 free(p);
134 while (numitems > 0)
135 free(names[--numitems]);
136 free(names);
137 errno = serrno;
138 return (-1);
139 }
140
141 int
142 #ifdef I_AM_SCANDIR_B
scandir_b(const char * dirname,struct dirent *** namelist,select_block select,dcomp_block dcomp)143 scandir_b(const char *dirname, struct dirent ***namelist, select_block select,
144 dcomp_block dcomp)
145 #else
146 scandir(const char *dirname, struct dirent ***namelist,
147 int (*select)(const struct dirent *),
148 int (*dcomp)(const struct dirent **, const struct dirent **))
149 #endif
150 {
151 DIR *dirp;
152 int ret, serrno;
153
154 dirp = opendir(dirname);
155 if (dirp == NULL)
156 return (-1);
157 ret =
158 #ifdef I_AM_SCANDIR_B
159 scandir_dirp_b
160 #else
161 scandir_dirp
162 #endif
163 (dirp, namelist, select, dcomp);
164 serrno = errno;
165 closedir(dirp);
166 errno = serrno;
167 return (ret);
168 }
169
170 int
171 #ifdef I_AM_SCANDIR_B
fdscandir_b(int dirfd,struct dirent *** namelist,select_block select,dcomp_block dcomp)172 fdscandir_b(int dirfd, struct dirent ***namelist, select_block select,
173 dcomp_block dcomp)
174 #else
175 fdscandir(int dirfd, struct dirent ***namelist,
176 int (*select)(const struct dirent *),
177 int (*dcomp)(const struct dirent **, const struct dirent **))
178 #endif
179 {
180 DIR *dirp;
181 int ret, serrno;
182
183 dirp = fdopendir(dirfd);
184 if (dirp == NULL)
185 return (-1);
186 ret =
187 #ifdef I_AM_SCANDIR_B
188 scandir_dirp_b
189 #else
190 scandir_dirp
191 #endif
192 (dirp, namelist, select, dcomp);
193 serrno = errno;
194 fdclosedir(dirp);
195 errno = serrno;
196 return (ret);
197 }
198
199 int
200 #ifdef I_AM_SCANDIR_B
scandirat_b(int dirfd,const char * dirname,struct dirent *** namelist,select_block select,dcomp_block dcomp)201 scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
202 select_block select, dcomp_block dcomp)
203 #else
204 scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
205 int (*select)(const struct dirent *),
206 int (*dcomp)(const struct dirent **, const struct dirent **))
207 #endif
208 {
209 int fd, ret, serrno;
210
211 fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
212 if (fd == -1)
213 return (-1);
214 ret =
215 #ifdef I_AM_SCANDIR_B
216 fdscandir_b
217 #else
218 fdscandir
219 #endif
220 (fd, namelist, select, dcomp);
221 serrno = errno;
222 _close(fd);
223 errno = serrno;
224 return (ret);
225 }
226
227 #ifndef I_AM_SCANDIR_B
228 /*
229 * Alphabetic order comparison routine for those who want it.
230 * POSIX 2008 requires that alphasort() uses strcoll().
231 */
232 int
alphasort(const struct dirent ** d1,const struct dirent ** d2)233 alphasort(const struct dirent **d1, const struct dirent **d2)
234 {
235
236 return (strcoll((*d1)->d_name, (*d2)->d_name));
237 }
238
239 int
versionsort(const struct dirent ** d1,const struct dirent ** d2)240 versionsort(const struct dirent **d1, const struct dirent **d2)
241 {
242
243 return (strverscmp((*d1)->d_name, (*d2)->d_name));
244 }
245
246 static int
scandir_thunk_cmp(const void * p1,const void * p2,void * thunk)247 scandir_thunk_cmp(const void *p1, const void *p2, void *thunk)
248 {
249 int (*dc)(const struct dirent **, const struct dirent **);
250
251 dc = *(int (**)(const struct dirent **, const struct dirent **))thunk;
252 return (dc((const struct dirent **)p1, (const struct dirent **)p2));
253 }
254 #endif
255
256 #ifdef I_AM_SCANDIR_B
257 __weak_reference(fdscandir_b, fscandir_b);
258 #else
259 __weak_reference(fdscandir, fscandir);
260 #endif
261