opendir.c revision 178256
150397Sobrien/*- 2169689Skan * Copyright (c) 1983, 1993 390075Sobrien * The Regents of the University of California. All rights reserved. 450397Sobrien * 590075Sobrien * Redistribution and use in source and binary forms, with or without 650397Sobrien * modification, are permitted provided that the following conditions 790075Sobrien * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1150397Sobrien * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 4. Neither the name of the University nor the names of its contributors 1490075Sobrien * may be used to endorse or promote products derived from this software 1590075Sobrien * without specific prior written permission. 1650397Sobrien * 1750397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1890075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2150397Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2290075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23117395Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2590075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26117395Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27117395Skan * SUCH DAMAGE. 28117395Skan */ 29169689Skan 30169689Skan#if defined(LIBC_SCCS) && !defined(lint) 31169689Skanstatic char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 32117395Skan#endif /* LIBC_SCCS and not lint */ 3350397Sobrien#include <sys/cdefs.h> 3450397Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/opendir.c 178256 2008-04-16 18:59:36Z delphij $"); 3550397Sobrien 36169689Skan#include "namespace.h" 3750397Sobrien#include <sys/param.h> 3850397Sobrien#include <sys/mount.h> 39169689Skan#include <sys/stat.h> 4050397Sobrien 41169689Skan#include <dirent.h> 4250397Sobrien#include <errno.h> 43169689Skan#include <fcntl.h> 44169689Skan#include <stdlib.h> 45169689Skan#include <string.h> 46169689Skan#include <unistd.h> 47169689Skan#include "un-namespace.h" 48169689Skan 49169689Skan#include "telldir.h" 50169689Skan 5150397Sobrienstatic DIR * __opendir_common(int, const char *, int); 5250397Sobrien 53169689Skan/* 5450397Sobrien * Open a directory. 55169689Skan */ 56169689SkanDIR * 57169689Skanopendir(const char *name) 58169689Skan{ 59169689Skan 60169689Skan return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 61169689Skan} 62169689Skan 63117395Skan/* 6450397Sobrien * Open a directory with existing file descriptor. 6590075Sobrien */ 6690075SobrienDIR * 6790075Sobrienfdopendir(int fd) 68117395Skan{ 6950397Sobrien 7050397Sobrien return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP)); 7150397Sobrien} 72117395Skan 7390075SobrienDIR * 7490075Sobrien__opendir2(const char *name, int flags) 7590075Sobrien{ 76169689Skan int fd; 77169689Skan struct stat statb; 78117395Skan 7990075Sobrien /* 8050397Sobrien * stat() before _open() because opening of special files may be 8150397Sobrien * harmful. 8290075Sobrien */ 83169689Skan if (stat(name, &statb) != 0) 8450397Sobrien return (NULL); 8550397Sobrien if (!S_ISDIR(statb.st_mode)) { 86132718Skan errno = ENOTDIR; 8750397Sobrien return (NULL); 8890075Sobrien } 89132718Skan if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 9050397Sobrien return (NULL); 9190075Sobrien 92169689Skan return __opendir_common(fd, name, flags); 9390075Sobrien} 94169689Skan 95169689Skan/* 9650397Sobrien * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 97169689Skan */ 98169689Skanstatic DIR * 99169689Skan__opendir_common(int fd, const char *name, int flags) 10050397Sobrien{ 101169689Skan DIR *dirp; 102169689Skan int incr; 103169689Skan int saved_errno; 104169689Skan int unionstack; 105169689Skan struct stat statb; 106169689Skan 107169689Skan dirp = NULL; 108169689Skan /* _fstat() the open handler because the file may have changed. */ 109169689Skan if (_fstat(fd, &statb) != 0) 110169689Skan goto fail; 111169689Skan if (!S_ISDIR(statb.st_mode)) { 112169689Skan errno = ENOTDIR; 113169689Skan goto fail; 114169689Skan } 115169689Skan if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 116169689Skan (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 117169689Skan goto fail; 118169689Skan 119169689Skan dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 120169689Skan LIST_INIT(&dirp->dd_td->td_locq); 121169689Skan dirp->dd_td->td_loccnt = 0; 122169689Skan 123169689Skan /* 124169689Skan * Use the system page size if that is a multiple of DIRBLKSIZ. 125169689Skan * Hopefully this can be a big win someday by allowing page 126169689Skan * trades to user space to be done by _getdirentries(). 127169689Skan */ 12850397Sobrien incr = getpagesize(); 129132718Skan if ((incr % DIRBLKSIZ) != 0) 13050397Sobrien incr = DIRBLKSIZ; 13150397Sobrien 132132718Skan /* 13350397Sobrien * Determine whether this directory is the top of a union stack. 13450397Sobrien */ 135132718Skan if (flags & DTF_NODUP) { 13650397Sobrien struct statfs sfb; 13750397Sobrien 138132718Skan if (_fstatfs(fd, &sfb) < 0) 139132718Skan goto fail; 14050397Sobrien unionstack = !strcmp(sfb.f_fstypename, "unionfs") 141132718Skan || (sfb.f_flags & MNT_UNION); 142132718Skan } else { 14350397Sobrien unionstack = 0; 144169689Skan } 145169689Skan 146169689Skan if (unionstack) { 14750397Sobrien int len = 0; 148169689Skan int space = 0; 149169689Skan char *buf = 0; 15050397Sobrien char *ddptr = 0; 151169689Skan char *ddeptr; 152169689Skan int n; 153169689Skan struct dirent **dpv; 154169689Skan 155169689Skan /* 156169689Skan * The strategy here is to read all the directory 157169689Skan * entries into a buffer, sort the buffer, and 158169689Skan * remove duplicate entries by setting the inode 159169689Skan * number to zero. 160169689Skan */ 161169689Skan 162169689Skan do { 16390075Sobrien /* 16490075Sobrien * Always make at least DIRBLKSIZ bytes 16590075Sobrien * available to _getdirentries 166169689Skan */ 16750397Sobrien if (space < DIRBLKSIZ) { 168169689Skan space += incr; 169169689Skan len += incr; 17050397Sobrien buf = reallocf(buf, len); 171169689Skan if (buf == NULL) 172169689Skan goto fail; 173117395Skan ddptr = buf + (len - space); 174169689Skan } 175169689Skan 17650397Sobrien n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 17750397Sobrien if (n > 0) { 17890075Sobrien ddptr += n; 179169689Skan space -= n; 18050397Sobrien } 181169689Skan } while (n > 0); 18290075Sobrien 183169689Skan ddeptr = ddptr; 184169689Skan flags |= __DTF_READALL; 185169689Skan 186169689Skan /* 18750397Sobrien * Re-open the directory. 188169689Skan * This has the effect of rewinding back to the 189169689Skan * top of the union stack and is needed by 19050397Sobrien * programs which plan to fchdir to a descriptor 191169689Skan * which has also been read -- see fts.c. 192169689Skan */ 19350397Sobrien if (flags & DTF_REWIND) { 194169689Skan (void)_close(fd); 195169689Skan if ((fd = _open(name, O_RDONLY)) == -1) { 196169689Skan saved_errno = errno; 197169689Skan free(buf); 198169689Skan free(dirp); 19950397Sobrien errno = saved_errno; 200169689Skan return (NULL); 201169689Skan } 20250397Sobrien } 203169689Skan 204169689Skan /* 205169689Skan * There is now a buffer full of (possibly) duplicate 206169689Skan * names. 207169689Skan */ 208169689Skan dirp->dd_buf = buf; 20950397Sobrien 210169689Skan /* 211169689Skan * Go round this loop twice... 212169689Skan * 213169689Skan * Scan through the buffer, counting entries. 214169689Skan * On the second pass, save pointers to each one. 215169689Skan * Then sort the pointers and remove duplicate names. 216169689Skan */ 217169689Skan for (dpv = 0;;) { 21890075Sobrien n = 0; 219169689Skan ddptr = buf; 220169689Skan while (ddptr < ddeptr) { 221169689Skan struct dirent *dp; 222169689Skan 223169689Skan dp = (struct dirent *) ddptr; 224169689Skan if ((long)dp & 03L) 225169689Skan break; 226169689Skan if ((dp->d_reclen <= 0) || 227169689Skan (dp->d_reclen > (ddeptr + 1 - ddptr))) 228169689Skan break; 229169689Skan ddptr += dp->d_reclen; 230169689Skan if (dp->d_fileno) { 231169689Skan if (dpv) 232169689Skan dpv[n] = dp; 233169689Skan n++; 234169689Skan } 235169689Skan } 236169689Skan 237169689Skan if (dpv) { 238169689Skan struct dirent *xp; 239169689Skan 240169689Skan /* 241169689Skan * This sort must be stable. 242169689Skan */ 243169689Skan mergesort(dpv, n, sizeof(*dpv), alphasort); 244169689Skan 245169689Skan dpv[n] = NULL; 246169689Skan xp = NULL; 247169689Skan 248169689Skan /* 249169689Skan * Scan through the buffer in sort order, 250169689Skan * zapping the inode number of any 251169689Skan * duplicate names. 252169689Skan */ 253169689Skan for (n = 0; dpv[n]; n++) { 254169689Skan struct dirent *dp = dpv[n]; 255169689Skan 256169689Skan if ((xp == NULL) || 257169689Skan strcmp(dp->d_name, xp->d_name)) { 258169689Skan xp = dp; 259169689Skan } else { 260169689Skan dp->d_fileno = 0; 261169689Skan } 262169689Skan if (dp->d_type == DT_WHT && 263169689Skan (flags & DTF_HIDEW)) 264169689Skan dp->d_fileno = 0; 265169689Skan } 266169689Skan 267169689Skan free(dpv); 268169689Skan break; 269169689Skan } else { 270169689Skan dpv = malloc((n+1) * sizeof(struct dirent *)); 271169689Skan if (dpv == NULL) 272169689Skan break; 273169689Skan } 274169689Skan } 275169689Skan 276169689Skan dirp->dd_len = len; 277169689Skan dirp->dd_size = ddptr - dirp->dd_buf; 278169689Skan } else { 279169689Skan dirp->dd_len = incr; 280169689Skan dirp->dd_size = 0; 281169689Skan dirp->dd_buf = malloc(dirp->dd_len); 282169689Skan if (dirp->dd_buf == NULL) 283169689Skan goto fail; 284169689Skan dirp->dd_seek = 0; 285169689Skan flags &= ~DTF_REWIND; 286169689Skan } 287169689Skan 288169689Skan dirp->dd_loc = 0; 289169689Skan dirp->dd_fd = fd; 290169689Skan dirp->dd_flags = flags; 291169689Skan dirp->dd_lock = NULL; 292169689Skan 293169689Skan /* 294169689Skan * Set up seek point for rewinddir. 295169689Skan */ 296169689Skan dirp->dd_rewind = telldir(dirp); 297169689Skan 298169689Skan return (dirp); 299169689Skan 300169689Skanfail: 301169689Skan saved_errno = errno; 302169689Skan free(dirp); 303169689Skan (void)_close(fd); 304169689Skan errno = saved_errno; 305169689Skan return (NULL); 306169689Skan} 307169689Skan