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