1 /*
2 * Copyright (c) 1983, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char orig_sccsid[] = "@(#)opendir.c 8.2 (Berkeley) 2/12/94";
10 static char sccsid[] = "@(#)libc.opendir.c 8.2 (Berkeley) 06/15/94";
11 #endif /* LIBC_SCCS and not lint */
12
13 #include <sys/param.h>
14 #include <sys/mount.h>
15
16 #include <dirent.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 /*
22 * open a directory.
23 */
24 DIR *
opendir(name)25 opendir(name)
26 const char *name;
27 {
28 DIR *dirp;
29 int fd;
30 int incr;
31 struct statfs sfb;
32
33 if ((fd = open(name, 0)) == -1)
34 return (NULL);
35 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
36 (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
37 close(fd);
38 return (NULL);
39 }
40
41 /*
42 * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
43 * buffer that it cluster boundary aligned.
44 * Hopefully this can be a big win someday by allowing page
45 * trades trade to user space to be done by getdirentries()
46 */
47 if ((CLBYTES % DIRBLKSIZ) == 0)
48 incr = CLBYTES;
49 else
50 incr = DIRBLKSIZ;
51
52 #ifdef MOUNT_UNION
53 /*
54 * Determine whether this directory is the top of a union stack.
55 */
56 if (fstatfs(fd, &sfb) < 0) {
57 free(dirp);
58 close(fd);
59 return (NULL);
60 }
61
62 if (sfb.f_type == MOUNT_UNION) {
63 int len = 0;
64 int space = 0;
65 char *buf = 0;
66 char *ddptr = 0;
67 int n;
68 struct dirent **dpv;
69
70 /*
71 * The strategy here is to read all the directory
72 * entries into a buffer, sort the buffer, and
73 * remove duplicate entries by setting the inode
74 * number to zero.
75 */
76
77 /*
78 * Fixup dd_loc to be non-zero to fake out readdir
79 */
80 dirp->dd_loc = sizeof(void *);
81
82 do {
83 /*
84 * Always make at least DIRBLKSIZ bytes
85 * available to getdirentries
86 */
87 if (space < DIRBLKSIZ) {
88 space += incr;
89 len += incr;
90 buf = realloc(buf, len);
91 if (buf == NULL) {
92 free(dirp);
93 close(fd);
94 return (NULL);
95 }
96 ddptr = buf + (len - space) + dirp->dd_loc;
97 }
98
99 n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
100 if (n > 0) {
101 ddptr += n;
102 space -= n;
103 }
104 } while (n > 0);
105
106 /*
107 * There is now a buffer full of (possibly) duplicate
108 * names.
109 */
110 dirp->dd_buf = buf;
111
112 /*
113 * Go round this loop twice...
114 *
115 * Scan through the buffer, counting entries.
116 * On the second pass, save pointers to each one.
117 * Then sort the pointers and remove duplicate names.
118 */
119 for (dpv = 0;;) {
120 n = 0;
121 ddptr = buf + dirp->dd_loc;
122 while (ddptr < buf + len) {
123 struct dirent *dp;
124
125 dp = (struct dirent *) ddptr;
126 if ((int)dp & 03)
127 break;
128 if ((dp->d_reclen <= 0) ||
129 (dp->d_reclen > (buf + len + 1 - ddptr)))
130 break;
131 ddptr += dp->d_reclen;
132 if (dp->d_fileno) {
133 if (dpv)
134 dpv[n] = dp;
135 n++;
136 }
137 }
138
139 if (dpv) {
140 struct dirent *xp;
141
142 /*
143 * This sort must be stable.
144 */
145 mergesort(dpv, n, sizeof(*dpv), alphasort);
146
147 dpv[n] = NULL;
148 xp = NULL;
149
150 /*
151 * Scan through the buffer in sort order,
152 * zapping the inode number of any
153 * duplicate names.
154 */
155 for (n = 0; dpv[n]; n++) {
156 struct dirent *dp = dpv[n];
157
158 if ((xp == NULL) ||
159 strcmp(dp->d_name, xp->d_name))
160 xp = dp;
161 else
162 dp->d_fileno = 0;
163 }
164
165 free(dpv);
166 break;
167 } else {
168 dpv = malloc((n+1) * sizeof(struct dirent *));
169 if (dpv == NULL)
170 break;
171 }
172 }
173
174 dirp->dd_len = len;
175 dirp->dd_size = ddptr - dirp->dd_buf;
176 } else
177 #endif /* MOUNT_UNION */
178 {
179 dirp->dd_len = incr;
180 dirp->dd_buf = malloc(dirp->dd_len);
181 if (dirp->dd_buf == NULL) {
182 free(dirp);
183 close (fd);
184 return (NULL);
185 }
186 dirp->dd_seek = 0;
187 dirp->dd_loc = 0;
188 }
189
190 dirp->dd_fd = fd;
191
192 /*
193 * Set up seek point for rewinddir.
194 */
195 dirp->dd_rewind = telldir(dirp);
196
197 return (dirp);
198 }
199