xref: /freebsd/lib/libc/gen/scandir.c (revision 8ebc0768663b0ea9f78a0b8cffb93ee1684a9b08)
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