Deleted Added
full compact
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 * 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>
34__FBSDID("$FreeBSD: head/lib/libc/gen/opendir.c 165903 2007-01-09 00:28:16Z imp $");
34__FBSDID("$FreeBSD: head/lib/libc/gen/opendir.c 178253 2008-04-16 18:40:52Z delphij $");
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 "un-namespace.h"
48
49#include "telldir.h"
50/*
51 * Open a directory.
52 */
53DIR *
54opendir(name)
55 const char *name;
54opendir(const char *name)
55{
56
57 return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
58}
59
60DIR *
62__opendir2(name, flags)
63 const char *name;
64 int flags;
61__opendir2(const char *name, int flags)
62{
63 DIR *dirp;
64 int fd;
65 int incr;
66 int saved_errno;
67 int unionstack;
68 struct stat statb;
69
70 /*
71 * stat() before _open() because opening of special files may be
72 * harmful. _fstat() after open because the file may have changed.
73 */
74 if (stat(name, &statb) != 0)
75 return (NULL);
76 if (!S_ISDIR(statb.st_mode)) {
77 errno = ENOTDIR;
78 return (NULL);
79 }
80 if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1)
81 return (NULL);
82 dirp = NULL;
83 if (_fstat(fd, &statb) != 0)
84 goto fail;
85 if (!S_ISDIR(statb.st_mode)) {
86 errno = ENOTDIR;
87 goto fail;
88 }
89 if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
90 (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
91 goto fail;
92
93 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
94 LIST_INIT(&dirp->dd_td->td_locq);
95 dirp->dd_td->td_loccnt = 0;
96
97 /*
98 * Use the system page size if that is a multiple of DIRBLKSIZ.
99 * Hopefully this can be a big win someday by allowing page
100 * trades to user space to be done by _getdirentries().
101 */
102 incr = getpagesize();
103 if ((incr % DIRBLKSIZ) != 0)
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 goto fail;
114 unionstack = !strcmp(sfb.f_fstypename, "unionfs")
115 || (sfb.f_flags & MNT_UNION);
116 } else {
117 unionstack = 0;
118 }
119
120 if (unionstack) {
121 int len = 0;
122 int space = 0;
123 char *buf = 0;
124 char *ddptr = 0;
125 char *ddeptr;
126 int n;
127 struct dirent **dpv;
128
129 /*
130 * The strategy here is to read all the directory
131 * entries into a buffer, sort the buffer, and
132 * remove duplicate entries by setting the inode
133 * number to zero.
134 */
135
136 do {
137 /*
138 * Always make at least DIRBLKSIZ bytes
139 * available to _getdirentries
140 */
141 if (space < DIRBLKSIZ) {
142 space += incr;
143 len += incr;
144 buf = reallocf(buf, len);
145 if (buf == NULL)
146 goto fail;
147 ddptr = buf + (len - space);
148 }
149
150 n = _getdirentries(fd, ddptr, space, &dirp->dd_seek);
151 if (n > 0) {
152 ddptr += n;
153 space -= n;
154 }
155 } while (n > 0);
156
157 ddeptr = ddptr;
158 flags |= __DTF_READALL;
159
160 /*
161 * Re-open the directory.
162 * This has the effect of rewinding back to the
163 * top of the union stack and is needed by
164 * programs which plan to fchdir to a descriptor
165 * which has also been read -- see fts.c.
166 */
167 if (flags & DTF_REWIND) {
168 (void)_close(fd);
169 if ((fd = _open(name, O_RDONLY)) == -1) {
170 saved_errno = errno;
171 free(buf);
172 free(dirp);
173 errno = saved_errno;
174 return (NULL);
175 }
176 }
177
178 /*
179 * There is now a buffer full of (possibly) duplicate
180 * names.
181 */
182 dirp->dd_buf = buf;
183
184 /*
185 * Go round this loop twice...
186 *
187 * Scan through the buffer, counting entries.
188 * On the second pass, save pointers to each one.
189 * Then sort the pointers and remove duplicate names.
190 */
191 for (dpv = 0;;) {
192 n = 0;
193 ddptr = buf;
194 while (ddptr < ddeptr) {
195 struct dirent *dp;
196
197 dp = (struct dirent *) ddptr;
198 if ((long)dp & 03L)
199 break;
200 if ((dp->d_reclen <= 0) ||
201 (dp->d_reclen > (ddeptr + 1 - ddptr)))
202 break;
203 ddptr += dp->d_reclen;
204 if (dp->d_fileno) {
205 if (dpv)
206 dpv[n] = dp;
207 n++;
208 }
209 }
210
211 if (dpv) {
212 struct dirent *xp;
213
214 /*
215 * This sort must be stable.
216 */
217 mergesort(dpv, n, sizeof(*dpv), alphasort);
218
219 dpv[n] = NULL;
220 xp = NULL;
221
222 /*
223 * Scan through the buffer in sort order,
224 * zapping the inode number of any
225 * duplicate names.
226 */
227 for (n = 0; dpv[n]; n++) {
228 struct dirent *dp = dpv[n];
229
230 if ((xp == NULL) ||
231 strcmp(dp->d_name, xp->d_name)) {
232 xp = dp;
233 } else {
234 dp->d_fileno = 0;
235 }
236 if (dp->d_type == DT_WHT &&
237 (flags & DTF_HIDEW))
238 dp->d_fileno = 0;
239 }
240
241 free(dpv);
242 break;
243 } else {
244 dpv = malloc((n+1) * sizeof(struct dirent *));
245 if (dpv == NULL)
246 break;
247 }
248 }
249
250 dirp->dd_len = len;
251 dirp->dd_size = ddptr - dirp->dd_buf;
252 } else {
253 dirp->dd_len = incr;
254 dirp->dd_size = 0;
255 dirp->dd_buf = malloc(dirp->dd_len);
256 if (dirp->dd_buf == NULL)
257 goto fail;
258 dirp->dd_seek = 0;
259 flags &= ~DTF_REWIND;
260 }
261
262 dirp->dd_loc = 0;
263 dirp->dd_fd = fd;
264 dirp->dd_flags = flags;
265 dirp->dd_lock = NULL;
266
267 /*
268 * Set up seek point for rewinddir.
269 */
270 dirp->dd_rewind = telldir(dirp);
271
272 return (dirp);
273
274fail:
275 saved_errno = errno;
276 free(dirp);
277 (void)_close(fd);
278 errno = saved_errno;
279 return (NULL);
280}