1 /* $NetBSD: opendir.c,v 1.30 2006/01/24 19:33:10 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 #if 0 35 static char sccsid[] = "@(#)opendir.c 8.7 (Berkeley) 12/10/94"; 36 #else 37 __RCSID("$NetBSD: opendir.c,v 1.30 2006/01/24 19:33:10 christos Exp $"); 38 #endif 39 #endif /* LIBC_SCCS and not lint */ 40 41 #include "namespace.h" 42 #include "reentrant.h" 43 #include "extern.h" 44 #include <sys/param.h> 45 #include <sys/mount.h> 46 #include <sys/stat.h> 47 48 #include <assert.h> 49 #include <dirent.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 /* 57 * Open a directory. 58 */ 59 DIR * 60 opendir(name) 61 const char *name; 62 { 63 64 _DIAGASSERT(name != NULL); 65 66 return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 67 } 68 69 DIR * 70 __opendir2(name, flags) 71 const char *name; 72 int flags; 73 { 74 DIR *dirp = NULL; 75 int fd; 76 int serrno; 77 struct stat sb; 78 int pagesz; 79 int incr; 80 int unionstack, nfsdir; 81 struct statvfs sfb; 82 83 _DIAGASSERT(name != NULL); 84 85 if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1 || 86 fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 87 goto error; 88 if (fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) { 89 errno = ENOTDIR; 90 goto error; 91 } 92 if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) 93 goto error; 94 dirp->dd_buf = NULL; 95 96 /* 97 * If the machine's page size is an exact multiple of DIRBLKSIZ, 98 * use a buffer that is cluster boundary aligned. 99 * Hopefully this can be a big win someday by allowing page trades 100 * to user space to be done by getdirentries() 101 */ 102 if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) 103 incr = pagesz; 104 else 105 incr = DIRBLKSIZ; 106 107 /* 108 * Determine whether this directory is the top of a union stack. 109 */ 110 111 if (fstatvfs1(fd, &sfb, ST_NOWAIT) < 0) 112 goto error; 113 114 if (flags & DTF_NODUP) 115 unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, 116 MFSNAMELEN)) || (sfb.f_flag & MNT_UNION); 117 else 118 unionstack = 0; 119 120 nfsdir = !(strncmp(sfb.f_fstypename, MOUNT_NFS, MFSNAMELEN)); 121 122 if (unionstack || nfsdir) { 123 size_t len; 124 size_t space; 125 char *buf, *nbuf; 126 char *ddptr; 127 char *ddeptr; 128 int n; 129 struct dirent **dpv; 130 131 /* 132 * The strategy here for directories on top of a union stack 133 * is to read all the directory entries into a buffer, sort 134 * the buffer, and remove duplicate entries by setting the 135 * inode number to zero. 136 * 137 * For directories on an NFS mounted filesystem, we try 138 * to get a consistent snapshot by trying until we have 139 * successfully read all of the directory without errors 140 * (i.e. 'bad cookie' errors from the server because 141 * the directory was modified). These errors should not 142 * happen often, but need to be dealt with. 143 */ 144 retry: 145 len = 0; 146 space = 0; 147 buf = 0; 148 ddptr = 0; 149 150 do { 151 /* 152 * Always make at least DIRBLKSIZ bytes 153 * available to getdirentries 154 */ 155 if (space < DIRBLKSIZ) { 156 space += incr; 157 len += incr; 158 nbuf = realloc(buf, len); 159 if (nbuf == NULL) { 160 dirp->dd_buf = buf; 161 goto error; 162 } 163 buf = nbuf; 164 ddptr = buf + (len - space); 165 } 166 167 dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR); 168 n = getdents(fd, ddptr, space); 169 /* 170 * For NFS: EINVAL means a bad cookie error 171 * from the server. Keep trying to get a 172 * consistent view, in this case this means 173 * starting all over again. 174 */ 175 if (n == -1 && errno == EINVAL && nfsdir) { 176 free(buf); 177 lseek(fd, (off_t)0, SEEK_SET); 178 goto retry; 179 } 180 if (n > 0) { 181 ddptr += n; 182 space -= n; 183 } 184 } while (n > 0); 185 186 ddeptr = ddptr; 187 flags |= __DTF_READALL; 188 189 /* 190 * Re-open the directory. 191 * This has the effect of rewinding back to the 192 * top of the union stack and is needed by 193 * programs which plan to fchdir to a descriptor 194 * which has also been read -- see fts.c. 195 */ 196 if (flags & DTF_REWIND) { 197 (void) close(fd); 198 if ((fd = open(name, O_RDONLY)) == -1 || 199 fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { 200 dirp->dd_buf = buf; 201 goto error; 202 } 203 } 204 205 /* 206 * There is now a buffer full of (possibly) duplicate 207 * names. 208 */ 209 dirp->dd_buf = buf; 210 211 /* 212 * Go round this loop twice... 213 * 214 * Scan through the buffer, counting entries. 215 * On the second pass, save pointers to each one. 216 * Then sort the pointers and remove duplicate names. 217 */ 218 if (!nfsdir) { 219 for (dpv = 0;;) { 220 for (n = 0, ddptr = buf; ddptr < ddeptr;) { 221 struct dirent *dp; 222 223 dp = (struct dirent *)(void *)ddptr; 224 if ((long)dp & _DIRENT_ALIGN(dp)) 225 break; 226 /* 227 * d_reclen is unsigned, 228 * so no need to compare <= 0 229 */ 230 if (dp->d_reclen > (ddeptr + 1 - ddptr)) 231 break; 232 ddptr += dp->d_reclen; 233 if (dp->d_fileno) { 234 if (dpv) 235 dpv[n] = dp; 236 n++; 237 } 238 } 239 240 if (dpv) { 241 struct dirent *xp; 242 243 /* 244 * This sort must be stable. 245 */ 246 mergesort(dpv, (size_t)n, sizeof(*dpv), 247 alphasort); 248 249 dpv[n] = NULL; 250 xp = NULL; 251 252 /* 253 * Scan through the buffer in sort 254 * order, zapping the inode number 255 * of any duplicate names. 256 */ 257 for (n = 0; dpv[n]; n++) { 258 struct dirent *dp = dpv[n]; 259 260 if ((xp == NULL) || 261 strcmp(dp->d_name, 262 xp->d_name)) 263 xp = dp; 264 else 265 dp->d_fileno = 0; 266 if (dp->d_type == DT_WHT && 267 (flags & DTF_HIDEW)) 268 dp->d_fileno = 0; 269 } 270 271 free(dpv); 272 break; 273 } else { 274 dpv = malloc((n + 1) * 275 sizeof(struct dirent *)); 276 if (dpv == NULL) 277 break; 278 } 279 } 280 } 281 282 dirp->dd_len = len; 283 dirp->dd_size = ddptr - dirp->dd_buf; 284 } else { 285 dirp->dd_len = incr; 286 dirp->dd_buf = malloc((size_t)dirp->dd_len); 287 if (dirp->dd_buf == NULL) 288 goto error; 289 dirp->dd_seek = 0; 290 flags &= ~DTF_REWIND; 291 } 292 293 dirp->dd_loc = 0; 294 dirp->dd_fd = fd; 295 dirp->dd_flags = flags; 296 297 /* 298 * Set up seek point for rewinddir. 299 */ 300 #ifdef _REENTRANT 301 if (__isthreaded) { 302 if ((dirp->dd_lock = malloc(sizeof(mutex_t))) == NULL) 303 goto error; 304 mutex_init((mutex_t *)dirp->dd_lock, NULL); 305 } 306 #endif 307 dirp->dd_rewind = _telldir_unlocked(dirp); 308 return (dirp); 309 error: 310 serrno = errno; 311 if (dirp && dirp->dd_buf) 312 free(dirp->dd_buf); 313 if (dirp) 314 free(dirp); 315 if (fd != -1) 316 (void)close(fd); 317 errno = serrno; 318 return NULL; 319 } 320