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)
73int
74scandir64(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
146fail:
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
158int
159scandir(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
231fail:
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 */
244int
245alphasort(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)
252int
253alphasort64(const struct dirent64 **d1, const struct dirent64 **d2)
254{
255	return (strcoll((*d1)->d_name,
256	    (*d2)->d_name));
257}
258#endif
259