xref: /illumos-gate/usr/src/lib/libc/port/gen/scandir.c (revision d17be682)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * Based on usr/src/ucblib/libucb/port/gen/scandir.c
42  */
43 
44 /*
45  * Scan the directory dirname calling select to make a list of selected
46  * directory entries then sort using qsort and compare routine dcomp.
47  * Returns the number of entries and a pointer to a list of pointers to
48  * struct direct (through namelist). Returns -1 if there were any errors.
49  */
50 
51 #include <sys/feature_tests.h>
52 
53 #pragma weak _scandir = scandir
54 #pragma weak _alphasort = alphasort
55 #if !defined(_LP64)
56 #pragma weak _scandir64 = scandir64
57 #pragma weak _alphasort64 = alphasort64
58 #endif
59 
60 #include "lint.h"
61 #include <dirent.h>
62 #include <errno.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <limits.h>
68 
69 
70 #if !defined(_LP64)
71 int
72 scandir64(const char *dirname, struct dirent64 *(*namelist[]),
73     int (*select)(const struct dirent64 *),
74     int (*dcomp)(const struct dirent64 **, const struct dirent64 **))
75 {
76 	struct dirent64	*d, *p, **names = NULL;
77 	size_t	nitems = 0;
78 	size_t	arraysz, entlen;
79 	struct stat64	stb;
80 	DIR	*dirp;
81 	u_longlong_t	tmp_arraysz;
82 
83 	if ((dirp = opendir(dirname)) == NULL)
84 		return (-1);
85 	if (fstat64(dirp->dd_fd, &stb) < 0)
86 		goto fail;
87 
88 	/*
89 	 * estimate the array size by taking the size of the directory file
90 	 * and dividing it by a multiple of the minimum size entry.
91 	 */
92 	tmp_arraysz = stb.st_size / 24;	/* 24 bytes on a 64-bit system */
93 	if (tmp_arraysz > INT_MAX)
94 		arraysz = INT_MAX;
95 	else
96 		arraysz = (size_t)tmp_arraysz;
97 	names = malloc(arraysz * sizeof (struct dirent64 *));
98 	if (names == NULL)
99 		goto fail;
100 
101 	while ((d = readdir64(dirp)) != NULL) {
102 		if (select != NULL && !(*select)(d))
103 			continue;	/* just selected names */
104 
105 		entlen = d->d_reclen;
106 		/*
107 		 * Make a minimum size copy of the data
108 		 */
109 		p = malloc(entlen);
110 		if (p == NULL)
111 			goto fail;
112 		(void) memcpy(p, d, entlen);
113 		/*
114 		 * Check to make sure the array has space left and
115 		 * realloc the maximum size.
116 		 */
117 		if (nitems >= arraysz) {
118 			struct dirent64	**tmp;
119 			if (nitems == INT_MAX) {
120 				/* overflow */
121 				free(p);
122 				errno = EOVERFLOW;
123 				goto fail;
124 			}
125 			arraysz += 512;		/* no science here */
126 			tmp = realloc(names,
127 			    arraysz * sizeof (struct dirent64 *));
128 			if (tmp == NULL) {
129 				free(p);
130 				goto fail;
131 			}
132 			names = tmp;
133 		}
134 		names[nitems++] = p;
135 	}
136 	(void) closedir(dirp);
137 	if (nitems && dcomp != NULL)
138 		qsort(names, nitems, sizeof (struct dirent64 *),
139 		    (int(*)(const void *, const void *))dcomp);
140 	*namelist = names;
141 
142 	return ((int)nitems);
143 
144 fail:
145 	while (nitems != 0) {
146 		free(names[--nitems]);
147 	}
148 	if (names)
149 		free(names);
150 	(void) closedir(dirp);
151 	return (-1);
152 }
153 #endif
154 
155 
156 int
157 scandir(const char *dirname, struct dirent *(*namelist[]),
158     int (*select)(const struct dirent *),
159     int (*dcomp)(const struct dirent **, const struct dirent **))
160 {
161 	struct dirent	*d, *p, **names = NULL;
162 	size_t	nitems = 0;
163 	size_t	arraysz, entlen;
164 	struct stat64	stb;
165 	DIR	*dirp;
166 	u_longlong_t	tmp_arraysz;
167 
168 	if ((dirp = opendir(dirname)) == NULL)
169 		return (-1);
170 	if (fstat64(dirp->dd_fd, &stb) < 0)
171 		goto fail;
172 
173 	/*
174 	 * estimate the array size by taking the size of the directory file
175 	 * and dividing it by a multiple of the minimum size entry.
176 	 */
177 	tmp_arraysz = stb.st_size / 24;	/* 24 bytes on a 64-bit system */
178 	if (tmp_arraysz > INT_MAX)
179 		arraysz = INT_MAX;
180 	else
181 		arraysz = (size_t)tmp_arraysz;
182 	names = malloc(arraysz * sizeof (struct dirent *));
183 	if (names == NULL)
184 		goto fail;
185 
186 	while ((d = readdir(dirp)) != NULL) {
187 		if (select != NULL && !(*select)(d))
188 			continue;	/* just selected names */
189 
190 		entlen = d->d_reclen;
191 		/*
192 		 * Make a minimum size copy of the data
193 		 */
194 		p = malloc(entlen);
195 		if (p == NULL)
196 			goto fail;
197 		(void) memcpy(p, d, entlen);
198 		/*
199 		 * Check to make sure the array has space left and
200 		 * realloc the maximum size.
201 		 */
202 		if (nitems >= arraysz) {
203 			struct dirent **tmp;
204 			if (nitems == INT_MAX) {
205 				/* overflow */
206 				free(p);
207 				errno = EOVERFLOW;
208 				goto fail;
209 			}
210 			arraysz += 512;		/* no science here */
211 			tmp = realloc(names,
212 			    arraysz * sizeof (struct dirent *));
213 			if (tmp == NULL) {
214 				free(p);
215 				goto fail;
216 			}
217 			names = tmp;
218 		}
219 		names[nitems++] = p;
220 	}
221 	(void) closedir(dirp);
222 	if (nitems && dcomp != NULL)
223 		qsort(names, nitems, sizeof (struct dirent *),
224 		    (int(*)(const void *, const void *))dcomp);
225 	*namelist = names;
226 
227 	return ((int)nitems);
228 
229 fail:
230 	while (nitems != 0) {
231 		free(names[--nitems]);
232 	}
233 	if (names)
234 		free(names);
235 	(void) closedir(dirp);
236 	return (-1);
237 }
238 
239 /*
240  * Alphabetic order comparison routine for those who want it.
241  */
242 int
243 alphasort(const struct dirent **d1, const struct dirent **d2)
244 {
245 	return (strcoll((*d1)->d_name,
246 	    (*d2)->d_name));
247 }
248 
249 #if !defined(_LP64)
250 int
251 alphasort64(const struct dirent64 **d1, const struct dirent64 **d2)
252 {
253 	return (strcoll((*d1)->d_name,
254 	    (*d2)->d_name));
255 }
256 #endif
257