opendir.c revision 1.7.2.1 1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #if defined(LIBC_SCCS) && !defined(lint)
35 /*static char sccsid[] = "from: @(#)opendir.c 8.7 (Berkeley) 12/10/94";*/
36 static char rcsid[] = "$Id: opendir.c,v 1.7.2.1 1995/04/26 01:01:12 jtc Exp $";
37 #endif /* LIBC_SCCS and not lint */
38
39 #include "namespace.h"
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #include <sys/stat.h>
43
44 #include <dirent.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49
50 /*
51 * Open a directory.
52 */
53 DIR *
54 opendir(name)
55 const char *name;
56 {
57 return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
58 }
59 __weak_reference(_opendir,opendir);
60
61
62 DIR *
63 __opendir2(name, flags)
64 const char *name;
65 int flags;
66 {
67 DIR *dirp;
68 int fd;
69 struct stat sb;
70 int pagesz;
71 int incr;
72 int unionstack;
73
74 if ((fd = open(name, O_RDONLY)) == -1)
75 return (NULL);
76 if (fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) {
77 errno = ENOTDIR;
78 close(fd);
79 return (NULL);
80 }
81 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
82 (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
83 close(fd);
84 return (NULL);
85 }
86
87 /*
88 * If the machine's page size is an exact multiple of DIRBLKSIZ,
89 * use a buffer that is cluster boundary aligned.
90 * Hopefully this can be a big win someday by allowing page trades
91 * to user space to be done by getdirentries()
92 */
93 if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0)
94 incr = pagesz;
95 else
96 incr = DIRBLKSIZ;
97
98 /*
99 * Determine whether this directory is the top of a union stack.
100 */
101 if (flags & DTF_NODUP) {
102 struct statfs sfb;
103
104 if (fstatfs(fd, &sfb) < 0) {
105 free(dirp);
106 close(fd);
107 return (NULL);
108 }
109 unionstack = !strcmp(sfb.f_fstypename, MOUNT_UNION);
110 } else {
111 unionstack = 0;
112 }
113
114 if (unionstack) {
115 int len = 0;
116 int space = 0;
117 char *buf = 0;
118 char *ddptr = 0;
119 char *ddeptr;
120 int n;
121 struct dirent **dpv;
122
123 /*
124 * The strategy here is to read all the directory
125 * entries into a buffer, sort the buffer, and
126 * remove duplicate entries by setting the inode
127 * number to zero.
128 */
129
130 do {
131 /*
132 * Always make at least DIRBLKSIZ bytes
133 * available to getdirentries
134 */
135 if (space < DIRBLKSIZ) {
136 space += incr;
137 len += incr;
138 buf = realloc(buf, len);
139 if (buf == NULL) {
140 free(dirp);
141 close(fd);
142 return (NULL);
143 }
144 ddptr = buf + (len - space);
145 }
146
147 n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
148 if (n > 0) {
149 ddptr += n;
150 space -= n;
151 }
152 } while (n > 0);
153
154 ddeptr = ddptr;
155 flags |= __DTF_READALL;
156
157 /*
158 * Re-open the directory.
159 * This has the effect of rewinding back to the
160 * top of the union stack and is needed by
161 * programs which plan to fchdir to a descriptor
162 * which has also been read -- see fts.c.
163 */
164 if (flags & DTF_REWIND) {
165 (void) close(fd);
166 if ((fd = open(name, O_RDONLY)) == -1) {
167 free(buf);
168 free(dirp);
169 return (NULL);
170 }
171 }
172
173 /*
174 * There is now a buffer full of (possibly) duplicate
175 * names.
176 */
177 dirp->dd_buf = buf;
178
179 /*
180 * Go round this loop twice...
181 *
182 * Scan through the buffer, counting entries.
183 * On the second pass, save pointers to each one.
184 * Then sort the pointers and remove duplicate names.
185 */
186 for (dpv = 0;;) {
187 for (n = 0, ddptr = buf; ddptr < ddeptr;) {
188 struct dirent *dp;
189
190 dp = (struct dirent *) ddptr;
191 if ((long)dp & 03)
192 break;
193 if ((dp->d_reclen <= 0) ||
194 (dp->d_reclen > (ddeptr + 1 - ddptr)))
195 break;
196 ddptr += dp->d_reclen;
197 if (dp->d_fileno) {
198 if (dpv)
199 dpv[n] = dp;
200 n++;
201 }
202 }
203
204 if (dpv) {
205 struct dirent *xp;
206
207 /*
208 * This sort must be stable.
209 */
210 mergesort(dpv, n, sizeof(*dpv), alphasort);
211
212 dpv[n] = NULL;
213 xp = NULL;
214
215 /*
216 * Scan through the buffer in sort order,
217 * zapping the inode number of any
218 * duplicate names.
219 */
220 for (n = 0; dpv[n]; n++) {
221 struct dirent *dp = dpv[n];
222
223 if ((xp == NULL) ||
224 strcmp(dp->d_name, xp->d_name))
225 xp = dp;
226 else
227 dp->d_fileno = 0;
228 if (dp->d_type == DT_WHT &&
229 (flags & DTF_HIDEW))
230 dp->d_fileno = 0;
231 }
232
233 free(dpv);
234 break;
235 } else {
236 dpv = malloc((n+1) * sizeof(struct dirent *));
237 if (dpv == NULL)
238 break;
239 }
240 }
241
242 dirp->dd_len = len;
243 dirp->dd_size = ddptr - dirp->dd_buf;
244 } else {
245 dirp->dd_len = incr;
246 dirp->dd_buf = malloc(dirp->dd_len);
247 if (dirp->dd_buf == NULL) {
248 free(dirp);
249 close (fd);
250 return (NULL);
251 }
252 dirp->dd_seek = 0;
253 flags &= ~DTF_REWIND;
254 }
255
256 dirp->dd_loc = 0;
257 dirp->dd_fd = fd;
258 dirp->dd_flags = flags;
259
260 /*
261 * Set up seek point for rewinddir.
262 */
263 dirp->dd_rewind = telldir(dirp);
264
265 return (dirp);
266 }
267