xref: /illumos-gate/usr/src/lib/libc/port/gen/scandir.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * Based on usr/src/ucblib/libucb/port/gen/scandir.c
44  */
45 
46 /*
47  * Scan the directory dirname calling select to make a list of selected
48  * directory entries then sort using qsort and compare routine dcomp.
49  * Returns the number of entries and a pointer to a list of pointers to
50  * struct direct (through namelist). Returns -1 if there were any errors.
51  */
52 
53 #include <sys/feature_tests.h>
54 
55 #pragma weak _scandir = scandir
56 #pragma weak _alphasort = alphasort
57 #if !defined(_LP64)
58 #pragma weak _scandir64 = scandir64
59 #pragma weak _alphasort64 = alphasort64
60 #endif
61 
62 #include "lint.h"
63 #include <dirent.h>
64 #include <errno.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <limits.h>
70 
71 
72 #if !defined(_LP64)
73 int
74 scandir64(const char *dirname, struct dirent64 *(*namelist[]),
75     int (*select)(const struct dirent64 *),
76     int (*dcomp)(const struct dirent64 **, const struct dirent64 **))
77 {
78 	struct dirent64	*d, *p, **names = NULL;
79 	size_t	nitems = 0;
80 	size_t	arraysz, entlen;
81 	struct stat64	stb;
82 	DIR	*dirp;
83 	u_longlong_t	tmp_arraysz;
84 
85 	if ((dirp = opendir(dirname)) == NULL)
86 		return (-1);
87 	if (fstat64(dirp->dd_fd, &stb) < 0)
88 		goto fail;
89 
90 	/*
91 	 * estimate the array size by taking the size of the directory file
92 	 * and dividing it by a multiple of the minimum size entry.
93 	 */
94 	tmp_arraysz = stb.st_size / 24;	/* 24 bytes on a 64-bit system */
95 	if (tmp_arraysz > INT_MAX)
96 		arraysz = INT_MAX;
97 	else
98 		arraysz = (size_t)tmp_arraysz;
99 	names = malloc(arraysz * sizeof (struct dirent64 *));
100 	if (names == NULL)
101 		goto fail;
102 
103 	while ((d = readdir64(dirp)) != NULL) {
104 		if (select != NULL && !(*select)(d))
105 			continue;	/* just selected names */
106 
107 		entlen = d->d_reclen;
108 		/*
109 		 * Make a minimum size copy of the data
110 		 */
111 		p = malloc(entlen);
112 		if (p == NULL)
113 			goto fail;
114 		(void) memcpy(p, d, entlen);
115 		/*
116 		 * Check to make sure the array has space left and
117 		 * realloc the maximum size.
118 		 */
119 		if (nitems >= arraysz) {
120 			struct dirent64	**tmp;
121 			if (nitems == INT_MAX) {
122 				/* overflow */
123 				free(p);
124 				errno = EOVERFLOW;
125 				goto fail;
126 			}
127 			arraysz += 512;		/* no science here */
128 			tmp = realloc(names,
129 			    arraysz * sizeof (struct dirent64 *));
130 			if (tmp == NULL) {
131 				free(p);
132 				goto fail;
133 			}
134 			names = tmp;
135 		}
136 		names[nitems++] = p;
137 	}
138 	(void) closedir(dirp);
139 	if (nitems && dcomp != NULL)
140 		qsort(names, nitems, sizeof (struct dirent64 *),
141 		    (int(*)(const void *, const void *))dcomp);
142 	*namelist = names;
143 
144 	return ((int)nitems);
145 
146 fail:
147 	while (nitems != 0) {
148 		free(names[--nitems]);
149 	}
150 	if (names)
151 		free(names);
152 	(void) closedir(dirp);
153 	return (-1);
154 }
155 #endif
156 
157 
158 int
159 scandir(const char *dirname, struct dirent *(*namelist[]),
160     int (*select)(const struct dirent *),
161     int (*dcomp)(const struct dirent **, const struct dirent **))
162 {
163 	struct dirent	*d, *p, **names = NULL;
164 	size_t	nitems = 0;
165 	size_t	arraysz, entlen;
166 	struct stat64	stb;
167 	DIR	*dirp;
168 	u_longlong_t	tmp_arraysz;
169 
170 	if ((dirp = opendir(dirname)) == NULL)
171 		return (-1);
172 	if (fstat64(dirp->dd_fd, &stb) < 0)
173 		goto fail;
174 
175 	/*
176 	 * estimate the array size by taking the size of the directory file
177 	 * and dividing it by a multiple of the minimum size entry.
178 	 */
179 	tmp_arraysz = stb.st_size / 24;	/* 24 bytes on a 64-bit system */
180 	if (tmp_arraysz > INT_MAX)
181 		arraysz = INT_MAX;
182 	else
183 		arraysz = (size_t)tmp_arraysz;
184 	names = malloc(arraysz * sizeof (struct dirent *));
185 	if (names == NULL)
186 		goto fail;
187 
188 	while ((d = readdir(dirp)) != NULL) {
189 		if (select != NULL && !(*select)(d))
190 			continue;	/* just selected names */
191 
192 		entlen = d->d_reclen;
193 		/*
194 		 * Make a minimum size copy of the data
195 		 */
196 		p = malloc(entlen);
197 		if (p == NULL)
198 			goto fail;
199 		(void) memcpy(p, d, entlen);
200 		/*
201 		 * Check to make sure the array has space left and
202 		 * realloc the maximum size.
203 		 */
204 		if (nitems >= arraysz) {
205 			struct dirent **tmp;
206 			if (nitems == INT_MAX) {
207 				/* overflow */
208 				free(p);
209 				errno = EOVERFLOW;
210 				goto fail;
211 			}
212 			arraysz += 512;		/* no science here */
213 			tmp = realloc(names,
214 			    arraysz * sizeof (struct dirent *));
215 			if (tmp == NULL) {
216 				free(p);
217 				goto fail;
218 			}
219 			names = tmp;
220 		}
221 		names[nitems++] = p;
222 	}
223 	(void) closedir(dirp);
224 	if (nitems && dcomp != NULL)
225 		qsort(names, nitems, sizeof (struct dirent *),
226 		    (int(*)(const void *, const void *))dcomp);
227 	*namelist = names;
228 
229 	return ((int)nitems);
230 
231 fail:
232 	while (nitems != 0) {
233 		free(names[--nitems]);
234 	}
235 	if (names)
236 		free(names);
237 	(void) closedir(dirp);
238 	return (-1);
239 }
240 
241 /*
242  * Alphabetic order comparison routine for those who want it.
243  */
244 int
245 alphasort(const struct dirent **d1, const struct dirent **d2)
246 {
247 	return (strcoll((*d1)->d_name,
248 	    (*d2)->d_name));
249 }
250 
251 #if !defined(_LP64)
252 int
253 alphasort64(const struct dirent64 **d1, const struct dirent64 **d2)
254 {
255 	return (strcoll((*d1)->d_name,
256 	    (*d2)->d_name));
257 }
258 #endif
259