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