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