]> git.saurik.com Git - apple/libc.git/blame - gen/opendir-fbsd.c
Libc-763.13.tar.gz
[apple/libc.git] / gen / opendir-fbsd.c
CommitLineData
1f2f436a 1/*-
224c7076
A
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.
224c7076
A
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if defined(LIBC_SCCS) && !defined(lint)
31static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95";
32#endif /* LIBC_SCCS and not lint */
33#include <sys/cdefs.h>
1f2f436a 34__FBSDID("$FreeBSD: src/lib/libc/gen/opendir.c,v 1.24 2008/04/16 18:40:52 delphij Exp $");
224c7076
A
35
36#include "namespace.h"
37#include <sys/param.h>
38#include <sys/mount.h>
39#include <sys/stat.h>
40
41#include <dirent.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <pthread.h>
48#include "un-namespace.h"
49
50#include "telldir.h"
51/*
52 * Open a directory.
53 */
54DIR *
1f2f436a 55opendir(const char *name)
224c7076
A
56{
57
58 return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
59}
60
61DIR *
1f2f436a 62__opendir2(const char *name, int flags)
224c7076
A
63{
64 DIR *dirp;
65 int fd;
66 int incr;
67 int saved_errno;
68 int unionstack;
69
70 /*
71 * Use O_DIRECTORY to only open directories (because opening of
72 * special files may be harmful). errno is set to ENOTDIR if
73 * not a directory.
74 */
75 if ((fd = _open(name, O_RDONLY | O_NONBLOCK | O_DIRECTORY)) == -1)
76 return (NULL);
77 dirp = NULL;
78 if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
79 (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
80 goto fail;
81
82 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
83 LIST_INIT(&dirp->dd_td->td_locq);
84 dirp->dd_td->td_loccnt = 0;
85
86 /*
87 * Use the system page size if that is a multiple of DIRBLKSIZ.
88 * Hopefully this can be a big win someday by allowing page
89 * trades to user space to be done by _getdirentries().
90 */
91 incr = getpagesize();
92 if ((incr % DIRBLKSIZ) != 0)
93 incr = DIRBLKSIZ;
94
95 /*
96 * Determine whether this directory is the top of a union stack.
97 */
98 if (flags & DTF_NODUP) {
99 struct statfs sfb;
100
101 if (_fstatfs(fd, &sfb) < 0)
102 goto fail;
103 unionstack = !strcmp(sfb.f_fstypename, "unionfs")
104 || (sfb.f_flags & MNT_UNION);
105 } else {
106 unionstack = 0;
107 }
108
109 if (unionstack) {
110 int len = 0;
111 int space = 0;
112 char *buf = 0;
113 char *ddptr = 0;
114 char *ddeptr;
115 int n;
116 struct dirent **dpv;
117
118 /*
119 * The strategy here is to read all the directory
120 * entries into a buffer, sort the buffer, and
121 * remove duplicate entries by setting the inode
122 * number to zero.
123 */
124
125 do {
126 /*
127 * Always make at least DIRBLKSIZ bytes
128 * available to _getdirentries
129 */
130 if (space < DIRBLKSIZ) {
131 space += incr;
132 len += incr;
133 buf = reallocf(buf, len);
134 if (buf == NULL)
135 goto fail;
136 ddptr = buf + (len - space);
137 }
138
139#if __DARWIN_64_BIT_INO_T
140 n = __getdirentries64(fd, ddptr, space, &dirp->dd_td->seekoff);
141#else /* !__DARWIN_64_BIT_INO_T */
142 n = _getdirentries(fd, ddptr, space, &dirp->dd_seek);
143#endif /* __DARWIN_64_BIT_INO_T */
144 if (n > 0) {
145 ddptr += n;
146 space -= n;
147 }
148 } while (n > 0);
149
150 ddeptr = ddptr;
151 flags |= __DTF_READALL;
152
153 /*
154 * Re-open the directory.
155 * This has the effect of rewinding back to the
156 * top of the union stack and is needed by
157 * programs which plan to fchdir to a descriptor
158 * which has also been read -- see fts.c.
159 */
160 if (flags & DTF_REWIND) {
161 (void)_close(fd);
162 if ((fd = _open(name, O_RDONLY)) == -1) {
163 saved_errno = errno;
164 free(buf);
165 free(dirp);
166 errno = saved_errno;
167 return (NULL);
168 }
169 }
170
171 /*
172 * There is now a buffer full of (possibly) duplicate
173 * names.
174 */
175 dirp->dd_buf = buf;
176
177 /*
178 * Go round this loop twice...
179 *
180 * Scan through the buffer, counting entries.
181 * On the second pass, save pointers to each one.
182 * Then sort the pointers and remove duplicate names.
183 */
184 for (dpv = 0;;) {
185 n = 0;
186 ddptr = buf;
187 while (ddptr < ddeptr) {
188 struct dirent *dp;
189
190 dp = (struct dirent *) ddptr;
191 if ((long)dp & 03L)
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 }
229 if (dp->d_type == DT_WHT &&
230 (flags & DTF_HIDEW))
231 dp->d_fileno = 0;
232 }
233
234 free(dpv);
235 break;
236 } else {
237 dpv = malloc((n+1) * sizeof(struct dirent *));
238 if (dpv == NULL)
239 break;
240 }
241 }
242
243 dirp->dd_len = len;
244 dirp->dd_size = ddptr - dirp->dd_buf;
245 } else {
246 dirp->dd_len = incr;
247 dirp->dd_size = 0;
248 dirp->dd_buf = malloc(dirp->dd_len);
249 if (dirp->dd_buf == NULL)
250 goto fail;
251#if __DARWIN_64_BIT_INO_T
252 dirp->dd_td->seekoff = 0;
253#else /* !__DARWIN_64_BIT_INO_T */
254 dirp->dd_seek = 0;
255#endif /* __DARWIN_64_BIT_INO_T */
256 flags &= ~DTF_REWIND;
257 }
258
259 dirp->dd_loc = 0;
260 dirp->dd_fd = fd;
261 dirp->dd_flags = flags;
262 dirp->dd_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
263
264 /*
265 * Set up seek point for rewinddir.
266 */
267 dirp->dd_rewind = telldir(dirp);
268
269 return (dirp);
270
271fail:
272 saved_errno = errno;
273 free(dirp);
274 (void)_close(fd);
275 errno = saved_errno;
276 return (NULL);
277}