158f0484fSRodney W. Grimes /* 258f0484fSRodney W. Grimes * Copyright (c) 1983, 1993 358f0484fSRodney W. Grimes * The Regents of the University of California. All rights reserved. 458f0484fSRodney W. Grimes * 558f0484fSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 658f0484fSRodney W. Grimes * modification, are permitted provided that the following conditions 758f0484fSRodney W. Grimes * are met: 858f0484fSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 958f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 1058f0484fSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 1158f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 1258f0484fSRodney W. Grimes * documentation and/or other materials provided with the distribution. 1358f0484fSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 1458f0484fSRodney W. Grimes * must display the following acknowledgement: 1558f0484fSRodney W. Grimes * This product includes software developed by the University of 1658f0484fSRodney W. Grimes * California, Berkeley and its contributors. 1758f0484fSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 1858f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software 1958f0484fSRodney W. Grimes * without specific prior written permission. 2058f0484fSRodney W. Grimes * 2158f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2258f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2358f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2458f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2558f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2658f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2758f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2858f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2958f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3058f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3158f0484fSRodney W. Grimes * SUCH DAMAGE. 3258f0484fSRodney W. Grimes */ 3358f0484fSRodney W. Grimes 3458f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 35adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 3658f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 37ea8d448aSDavid E. O'Brien #include <sys/cdefs.h> 38ea8d448aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 3958f0484fSRodney W. Grimes 40d201fe46SDaniel Eischen #include "namespace.h" 4158f0484fSRodney W. Grimes #include <sys/param.h> 42adf6ad9eSPeter Wemm #include <sys/mount.h> 4303dcee8dSBruce Evans #include <sys/stat.h> 4458f0484fSRodney W. Grimes 4558f0484fSRodney W. Grimes #include <dirent.h> 4655e2b2c6SBruce Evans #include <errno.h> 4758f0484fSRodney W. Grimes #include <fcntl.h> 4858f0484fSRodney W. Grimes #include <stdlib.h> 4981b3ad59STim J. Robbins #include <string.h> 5058f0484fSRodney W. Grimes #include <unistd.h> 51d201fe46SDaniel Eischen #include "un-namespace.h" 5258f0484fSRodney W. Grimes 5310d1cba0SDaniel Eischen #include "telldir.h" 5458f0484fSRodney W. Grimes /* 55adf6ad9eSPeter Wemm * Open a directory. 5658f0484fSRodney W. Grimes */ 5758f0484fSRodney W. Grimes DIR * 5858f0484fSRodney W. Grimes opendir(name) 5958f0484fSRodney W. Grimes const char *name; 6058f0484fSRodney W. Grimes { 618bb47e40SDavid E. O'Brien 62adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 63adf6ad9eSPeter Wemm } 64adf6ad9eSPeter Wemm 65adf6ad9eSPeter Wemm DIR * 66adf6ad9eSPeter Wemm __opendir2(name, flags) 67adf6ad9eSPeter Wemm const char *name; 68adf6ad9eSPeter Wemm int flags; 69adf6ad9eSPeter Wemm { 70adf6ad9eSPeter Wemm DIR *dirp; 71adf6ad9eSPeter Wemm int fd; 72adf6ad9eSPeter Wemm int incr; 7303dcee8dSBruce Evans int saved_errno; 74adf6ad9eSPeter Wemm int unionstack; 75adf6ad9eSPeter Wemm struct stat statb; 7658f0484fSRodney W. Grimes 770b50c8d6SBruce Evans /* 78d201fe46SDaniel Eischen * stat() before _open() because opening of special files may be 79d201fe46SDaniel Eischen * harmful. _fstat() after open because the file may have changed. 800b50c8d6SBruce Evans */ 81adf6ad9eSPeter Wemm if (stat(name, &statb) != 0) 8203dcee8dSBruce Evans return (NULL); 83adf6ad9eSPeter Wemm if (!S_ISDIR(statb.st_mode)) { 840b50c8d6SBruce Evans errno = ENOTDIR; 8503dcee8dSBruce Evans return (NULL); 860b50c8d6SBruce Evans } 879233c4d9SJason Evans if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 88adf6ad9eSPeter Wemm return (NULL); 8903dcee8dSBruce Evans dirp = NULL; 90d201fe46SDaniel Eischen if (_fstat(fd, &statb) != 0) 9103dcee8dSBruce Evans goto fail; 9203dcee8dSBruce Evans if (!S_ISDIR(statb.st_mode)) { 9355e2b2c6SBruce Evans errno = ENOTDIR; 9403dcee8dSBruce Evans goto fail; 9558f0484fSRodney W. Grimes } 969233c4d9SJason Evans if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 9710d1cba0SDaniel Eischen (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 9803dcee8dSBruce Evans goto fail; 99adf6ad9eSPeter Wemm 10010d1cba0SDaniel Eischen dirp->dd_td = (void *)dirp + sizeof(DIR); 10110d1cba0SDaniel Eischen LIST_INIT(&dirp->dd_td->td_locq); 10210d1cba0SDaniel Eischen dirp->dd_td->td_loccnt = 0; 10310d1cba0SDaniel Eischen 10458f0484fSRodney W. Grimes /* 10503dcee8dSBruce Evans * Use the system page size if that is a multiple of DIRBLKSIZ. 106adf6ad9eSPeter Wemm * Hopefully this can be a big win someday by allowing page 107d201fe46SDaniel Eischen * trades to user space to be done by _getdirentries(). 10858f0484fSRodney W. Grimes */ 109adf6ad9eSPeter Wemm incr = getpagesize(); 110adf6ad9eSPeter Wemm if ((incr % DIRBLKSIZ) != 0) 111adf6ad9eSPeter Wemm incr = DIRBLKSIZ; 112adf6ad9eSPeter Wemm 113adf6ad9eSPeter Wemm /* 114adf6ad9eSPeter Wemm * Determine whether this directory is the top of a union stack. 115adf6ad9eSPeter Wemm */ 116adf6ad9eSPeter Wemm if (flags & DTF_NODUP) { 117adf6ad9eSPeter Wemm struct statfs sfb; 118adf6ad9eSPeter Wemm 119d201fe46SDaniel Eischen if (_fstatfs(fd, &sfb) < 0) 12003dcee8dSBruce Evans goto fail; 121b2bcd87eSJoerg Wunsch unionstack = !strcmp(sfb.f_fstypename, "union") 122b2bcd87eSJoerg Wunsch || (sfb.f_flags & MNT_UNION); 123adf6ad9eSPeter Wemm } else { 124adf6ad9eSPeter Wemm unionstack = 0; 125adf6ad9eSPeter Wemm } 126adf6ad9eSPeter Wemm 127adf6ad9eSPeter Wemm if (unionstack) { 128adf6ad9eSPeter Wemm int len = 0; 129adf6ad9eSPeter Wemm int space = 0; 130adf6ad9eSPeter Wemm char *buf = 0; 131adf6ad9eSPeter Wemm char *ddptr = 0; 132adf6ad9eSPeter Wemm char *ddeptr; 133adf6ad9eSPeter Wemm int n; 134adf6ad9eSPeter Wemm struct dirent **dpv; 135adf6ad9eSPeter Wemm 136adf6ad9eSPeter Wemm /* 137adf6ad9eSPeter Wemm * The strategy here is to read all the directory 138adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 139adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 140adf6ad9eSPeter Wemm * number to zero. 141adf6ad9eSPeter Wemm */ 142adf6ad9eSPeter Wemm 143adf6ad9eSPeter Wemm do { 144adf6ad9eSPeter Wemm /* 145adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 146d201fe46SDaniel Eischen * available to _getdirentries 147adf6ad9eSPeter Wemm */ 148adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 149adf6ad9eSPeter Wemm space += incr; 150adf6ad9eSPeter Wemm len += incr; 151e8420087SWarner Losh buf = reallocf(buf, len); 15203dcee8dSBruce Evans if (buf == NULL) 15303dcee8dSBruce Evans goto fail; 154adf6ad9eSPeter Wemm ddptr = buf + (len - space); 155adf6ad9eSPeter Wemm } 156adf6ad9eSPeter Wemm 157d201fe46SDaniel Eischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 158adf6ad9eSPeter Wemm if (n > 0) { 159adf6ad9eSPeter Wemm ddptr += n; 160adf6ad9eSPeter Wemm space -= n; 161adf6ad9eSPeter Wemm } 162adf6ad9eSPeter Wemm } while (n > 0); 163adf6ad9eSPeter Wemm 164adf6ad9eSPeter Wemm ddeptr = ddptr; 165adf6ad9eSPeter Wemm flags |= __DTF_READALL; 166adf6ad9eSPeter Wemm 167adf6ad9eSPeter Wemm /* 168adf6ad9eSPeter Wemm * Re-open the directory. 169adf6ad9eSPeter Wemm * This has the effect of rewinding back to the 170adf6ad9eSPeter Wemm * top of the union stack and is needed by 171adf6ad9eSPeter Wemm * programs which plan to fchdir to a descriptor 172adf6ad9eSPeter Wemm * which has also been read -- see fts.c. 173adf6ad9eSPeter Wemm */ 174adf6ad9eSPeter Wemm if (flags & DTF_REWIND) { 1759233c4d9SJason Evans (void)_close(fd); 1769233c4d9SJason Evans if ((fd = _open(name, O_RDONLY)) == -1) { 17703dcee8dSBruce Evans saved_errno = errno; 178adf6ad9eSPeter Wemm free(buf); 179adf6ad9eSPeter Wemm free(dirp); 18003dcee8dSBruce Evans errno = saved_errno; 181adf6ad9eSPeter Wemm return (NULL); 182adf6ad9eSPeter Wemm } 183adf6ad9eSPeter Wemm } 184adf6ad9eSPeter Wemm 185adf6ad9eSPeter Wemm /* 186adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 187adf6ad9eSPeter Wemm * names. 188adf6ad9eSPeter Wemm */ 189adf6ad9eSPeter Wemm dirp->dd_buf = buf; 190adf6ad9eSPeter Wemm 191adf6ad9eSPeter Wemm /* 192adf6ad9eSPeter Wemm * Go round this loop twice... 193adf6ad9eSPeter Wemm * 194adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 195adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 196adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 197adf6ad9eSPeter Wemm */ 198adf6ad9eSPeter Wemm for (dpv = 0;;) { 199adf6ad9eSPeter Wemm n = 0; 200adf6ad9eSPeter Wemm ddptr = buf; 201adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 202adf6ad9eSPeter Wemm struct dirent *dp; 203adf6ad9eSPeter Wemm 204adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 20584d65005SJohn Birrell if ((long)dp & 03L) 206adf6ad9eSPeter Wemm break; 207adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 208adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 209adf6ad9eSPeter Wemm break; 210adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 211adf6ad9eSPeter Wemm if (dp->d_fileno) { 212adf6ad9eSPeter Wemm if (dpv) 213adf6ad9eSPeter Wemm dpv[n] = dp; 214adf6ad9eSPeter Wemm n++; 215adf6ad9eSPeter Wemm } 216adf6ad9eSPeter Wemm } 217adf6ad9eSPeter Wemm 218adf6ad9eSPeter Wemm if (dpv) { 219adf6ad9eSPeter Wemm struct dirent *xp; 220adf6ad9eSPeter Wemm 221adf6ad9eSPeter Wemm /* 222adf6ad9eSPeter Wemm * This sort must be stable. 223adf6ad9eSPeter Wemm */ 224adf6ad9eSPeter Wemm mergesort(dpv, n, sizeof(*dpv), alphasort); 225adf6ad9eSPeter Wemm 226adf6ad9eSPeter Wemm dpv[n] = NULL; 227adf6ad9eSPeter Wemm xp = NULL; 228adf6ad9eSPeter Wemm 229adf6ad9eSPeter Wemm /* 230adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 231adf6ad9eSPeter Wemm * zapping the inode number of any 232adf6ad9eSPeter Wemm * duplicate names. 233adf6ad9eSPeter Wemm */ 234adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 235adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 236adf6ad9eSPeter Wemm 237adf6ad9eSPeter Wemm if ((xp == NULL) || 238adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 239adf6ad9eSPeter Wemm xp = dp; 240adf6ad9eSPeter Wemm } else { 241adf6ad9eSPeter Wemm dp->d_fileno = 0; 242adf6ad9eSPeter Wemm } 243adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 244adf6ad9eSPeter Wemm (flags & DTF_HIDEW)) 245adf6ad9eSPeter Wemm dp->d_fileno = 0; 246adf6ad9eSPeter Wemm } 247adf6ad9eSPeter Wemm 248adf6ad9eSPeter Wemm free(dpv); 249adf6ad9eSPeter Wemm break; 250adf6ad9eSPeter Wemm } else { 251adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 252adf6ad9eSPeter Wemm if (dpv == NULL) 253adf6ad9eSPeter Wemm break; 254adf6ad9eSPeter Wemm } 255adf6ad9eSPeter Wemm } 256adf6ad9eSPeter Wemm 257adf6ad9eSPeter Wemm dirp->dd_len = len; 258adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 259adf6ad9eSPeter Wemm } else { 260adf6ad9eSPeter Wemm dirp->dd_len = incr; 261d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 26203dcee8dSBruce Evans if (dirp->dd_buf == NULL) 26303dcee8dSBruce Evans goto fail; 26458f0484fSRodney W. Grimes dirp->dd_seek = 0; 265adf6ad9eSPeter Wemm flags &= ~DTF_REWIND; 266adf6ad9eSPeter Wemm } 267adf6ad9eSPeter Wemm 268adf6ad9eSPeter Wemm dirp->dd_loc = 0; 269adf6ad9eSPeter Wemm dirp->dd_fd = fd; 270adf6ad9eSPeter Wemm dirp->dd_flags = flags; 271d201fe46SDaniel Eischen dirp->dd_lock = NULL; 272adf6ad9eSPeter Wemm 27358f0484fSRodney W. Grimes /* 27458f0484fSRodney W. Grimes * Set up seek point for rewinddir. 27558f0484fSRodney W. Grimes */ 27658f0484fSRodney W. Grimes dirp->dd_rewind = telldir(dirp); 27755e2b2c6SBruce Evans 278adf6ad9eSPeter Wemm return (dirp); 27903dcee8dSBruce Evans 28003dcee8dSBruce Evans fail: 28103dcee8dSBruce Evans saved_errno = errno; 28203dcee8dSBruce Evans free(dirp); 2839233c4d9SJason Evans (void)_close(fd); 28403dcee8dSBruce Evans errno = saved_errno; 28503dcee8dSBruce Evans return (NULL); 28658f0484fSRodney W. Grimes } 287