xref: /freebsd/lib/libc/gen/scandir.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
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
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 *), int (*dcomp)(const struct dirent **,
73     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 ((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 ? arraysz * 2 : 32;
104 			names2 = reallocarray(names, arraysz, sizeof(*names));
105 			if (names2 == NULL)
106 				goto fail;
107 			names = names2;
108 		}
109 		names[numitems++] = p;
110 	}
111 	if (numitems && dcomp != NULL)
112 #ifdef I_AM_SCANDIR_B
113 		qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp);
114 #else
115 		qsort_r(names, numitems, sizeof(struct dirent *),
116 		    scandir_thunk_cmp, &dcomp);
117 #endif
118 	*namelist = names;
119 	return (numitems);
120 
121 fail:
122 	serrno = errno;
123 	free(p);
124 	while (numitems > 0)
125 		free(names[--numitems]);
126 	free(names);
127 	errno = serrno;
128 	return (-1);
129 }
130 
131 int
132 #ifdef I_AM_SCANDIR_B
133 scandir_b(const char *dirname, struct dirent ***namelist, select_block select,
134     dcomp_block dcomp)
135 #else
136 scandir(const char *dirname, struct dirent ***namelist,
137     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
138     const struct dirent **))
139 #endif
140 {
141 	DIR *dirp;
142 	int ret, serrno;
143 
144 	dirp = opendir(dirname);
145 	if (dirp == NULL)
146 		return (-1);
147 	ret =
148 #ifdef I_AM_SCANDIR_B
149 	    scandir_dirp_b
150 #else
151 	    scandir_dirp
152 #endif
153 	    (dirp, namelist, select, dcomp);
154 	serrno = errno;
155 	closedir(dirp);
156 	errno = serrno;
157 	return (ret);
158 }
159 
160 int
161 #ifdef I_AM_SCANDIR_B
162 fscandir_b(int dirfd, struct dirent ***namelist, select_block select,
163     dcomp_block dcomp)
164 #else
165 fscandir(int dirfd, struct dirent ***namelist,
166     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
167     const struct dirent **))
168 #endif
169 {
170 	DIR *dirp;
171 	int ret, serrno;
172 
173 	dirp = fdopendir(dirfd);
174 	if (dirp == NULL)
175 		return (-1);
176 	ret =
177 #ifdef I_AM_SCANDIR_B
178 	    scandir_dirp_b
179 #else
180 	    scandir_dirp
181 #endif
182 	    (dirp, namelist, select, dcomp);
183 	serrno = errno;
184 	fdclosedir(dirp);
185 	errno = serrno;
186 	return (ret);
187 }
188 
189 int
190 #ifdef I_AM_SCANDIR_B
191 scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
192     select_block select, dcomp_block dcomp)
193 #else
194 scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
195     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
196     const struct dirent **))
197 #endif
198 {
199 	int fd, ret, serrno;
200 
201 	fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
202 	if (fd == -1)
203 		return (-1);
204 	ret =
205 #ifdef I_AM_SCANDIR_B
206 	    fscandir_b
207 #else
208 	    fscandir
209 #endif
210 	    (fd, namelist, select, dcomp);
211 	serrno = errno;
212 	_close(fd);
213 	errno = serrno;
214 	return (ret);
215 }
216 
217 #ifndef I_AM_SCANDIR_B
218 /*
219  * Alphabetic order comparison routine for those who want it.
220  * POSIX 2008 requires that alphasort() uses strcoll().
221  */
222 int
223 alphasort(const struct dirent **d1, const struct dirent **d2)
224 {
225 
226 	return (strcoll((*d1)->d_name, (*d2)->d_name));
227 }
228 
229 int
230 versionsort(const struct dirent **d1, const struct dirent **d2)
231 {
232 
233 	return (strverscmp((*d1)->d_name, (*d2)->d_name));
234 }
235 
236 static int
237 scandir_thunk_cmp(const void *p1, const void *p2, void *thunk)
238 {
239 	int (*dc)(const struct dirent **, const struct dirent **);
240 
241 	dc = *(int (**)(const struct dirent **, const struct dirent **))thunk;
242 	return (dc((const struct dirent **)p1, (const struct dirent **)p2));
243 }
244 #endif
245