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