1f6386c25SXin LI /*- 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 * 4. Neither the name of the University nor the names of its contributors 1458f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software 1558f0484fSRodney W. Grimes * without specific prior written permission. 1658f0484fSRodney W. Grimes * 1758f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1858f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1958f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2058f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2158f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2258f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2358f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2458f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2558f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2658f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2758f0484fSRodney W. Grimes * SUCH DAMAGE. 2858f0484fSRodney W. Grimes */ 2958f0484fSRodney W. Grimes 3058f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 31adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 3258f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 33ea8d448aSDavid E. O'Brien #include <sys/cdefs.h> 34ea8d448aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 3558f0484fSRodney W. Grimes 36d201fe46SDaniel Eischen #include "namespace.h" 3758f0484fSRodney W. Grimes #include <sys/param.h> 38adf6ad9eSPeter Wemm #include <sys/mount.h> 3903dcee8dSBruce Evans #include <sys/stat.h> 4058f0484fSRodney W. Grimes 4158f0484fSRodney W. Grimes #include <dirent.h> 4255e2b2c6SBruce Evans #include <errno.h> 4358f0484fSRodney W. Grimes #include <fcntl.h> 4458f0484fSRodney W. Grimes #include <stdlib.h> 4581b3ad59STim J. Robbins #include <string.h> 4658f0484fSRodney W. Grimes #include <unistd.h> 47d201fe46SDaniel Eischen #include "un-namespace.h" 4858f0484fSRodney W. Grimes 4910d1cba0SDaniel Eischen #include "telldir.h" 5058f0484fSRodney W. Grimes /* 51adf6ad9eSPeter Wemm * Open a directory. 5258f0484fSRodney W. Grimes */ 5358f0484fSRodney W. Grimes DIR * 54f6386c25SXin LI opendir(const char *name) 5558f0484fSRodney W. Grimes { 568bb47e40SDavid E. O'Brien 57adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 58adf6ad9eSPeter Wemm } 59adf6ad9eSPeter Wemm 60adf6ad9eSPeter Wemm DIR * 61f6386c25SXin LI __opendir2(const char *name, int flags) 62adf6ad9eSPeter Wemm { 63adf6ad9eSPeter Wemm DIR *dirp; 64adf6ad9eSPeter Wemm int fd; 65adf6ad9eSPeter Wemm int incr; 6603dcee8dSBruce Evans int saved_errno; 67adf6ad9eSPeter Wemm int unionstack; 68adf6ad9eSPeter Wemm struct stat statb; 6958f0484fSRodney W. Grimes 700b50c8d6SBruce Evans /* 71d201fe46SDaniel Eischen * stat() before _open() because opening of special files may be 72d201fe46SDaniel Eischen * harmful. _fstat() after open because the file may have changed. 730b50c8d6SBruce Evans */ 74adf6ad9eSPeter Wemm if (stat(name, &statb) != 0) 7503dcee8dSBruce Evans return (NULL); 76adf6ad9eSPeter Wemm if (!S_ISDIR(statb.st_mode)) { 770b50c8d6SBruce Evans errno = ENOTDIR; 7803dcee8dSBruce Evans return (NULL); 790b50c8d6SBruce Evans } 809233c4d9SJason Evans if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 81adf6ad9eSPeter Wemm return (NULL); 8203dcee8dSBruce Evans dirp = NULL; 83d201fe46SDaniel Eischen if (_fstat(fd, &statb) != 0) 8403dcee8dSBruce Evans goto fail; 8503dcee8dSBruce Evans if (!S_ISDIR(statb.st_mode)) { 8655e2b2c6SBruce Evans errno = ENOTDIR; 8703dcee8dSBruce Evans goto fail; 8858f0484fSRodney W. Grimes } 899233c4d9SJason Evans if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 9010d1cba0SDaniel Eischen (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 9103dcee8dSBruce Evans goto fail; 92adf6ad9eSPeter Wemm 934eae39bfSStefan Farfeleder dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 9410d1cba0SDaniel Eischen LIST_INIT(&dirp->dd_td->td_locq); 9510d1cba0SDaniel Eischen dirp->dd_td->td_loccnt = 0; 9610d1cba0SDaniel Eischen 9758f0484fSRodney W. Grimes /* 9803dcee8dSBruce Evans * Use the system page size if that is a multiple of DIRBLKSIZ. 99adf6ad9eSPeter Wemm * Hopefully this can be a big win someday by allowing page 100d201fe46SDaniel Eischen * trades to user space to be done by _getdirentries(). 10158f0484fSRodney W. Grimes */ 102adf6ad9eSPeter Wemm incr = getpagesize(); 103adf6ad9eSPeter Wemm if ((incr % DIRBLKSIZ) != 0) 104adf6ad9eSPeter Wemm incr = DIRBLKSIZ; 105adf6ad9eSPeter Wemm 106adf6ad9eSPeter Wemm /* 107adf6ad9eSPeter Wemm * Determine whether this directory is the top of a union stack. 108adf6ad9eSPeter Wemm */ 109adf6ad9eSPeter Wemm if (flags & DTF_NODUP) { 110adf6ad9eSPeter Wemm struct statfs sfb; 111adf6ad9eSPeter Wemm 112d201fe46SDaniel Eischen if (_fstatfs(fd, &sfb) < 0) 11303dcee8dSBruce Evans goto fail; 1146e7988b9STim J. Robbins unionstack = !strcmp(sfb.f_fstypename, "unionfs") 115b2bcd87eSJoerg Wunsch || (sfb.f_flags & MNT_UNION); 116adf6ad9eSPeter Wemm } else { 117adf6ad9eSPeter Wemm unionstack = 0; 118adf6ad9eSPeter Wemm } 119adf6ad9eSPeter Wemm 120adf6ad9eSPeter Wemm if (unionstack) { 121adf6ad9eSPeter Wemm int len = 0; 122adf6ad9eSPeter Wemm int space = 0; 123adf6ad9eSPeter Wemm char *buf = 0; 124adf6ad9eSPeter Wemm char *ddptr = 0; 125adf6ad9eSPeter Wemm char *ddeptr; 126adf6ad9eSPeter Wemm int n; 127adf6ad9eSPeter Wemm struct dirent **dpv; 128adf6ad9eSPeter Wemm 129adf6ad9eSPeter Wemm /* 130adf6ad9eSPeter Wemm * The strategy here is to read all the directory 131adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 132adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 133adf6ad9eSPeter Wemm * number to zero. 134adf6ad9eSPeter Wemm */ 135adf6ad9eSPeter Wemm 136adf6ad9eSPeter Wemm do { 137adf6ad9eSPeter Wemm /* 138adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 139d201fe46SDaniel Eischen * available to _getdirentries 140adf6ad9eSPeter Wemm */ 141adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 142adf6ad9eSPeter Wemm space += incr; 143adf6ad9eSPeter Wemm len += incr; 144e8420087SWarner Losh buf = reallocf(buf, len); 14503dcee8dSBruce Evans if (buf == NULL) 14603dcee8dSBruce Evans goto fail; 147adf6ad9eSPeter Wemm ddptr = buf + (len - space); 148adf6ad9eSPeter Wemm } 149adf6ad9eSPeter Wemm 150d201fe46SDaniel Eischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 151adf6ad9eSPeter Wemm if (n > 0) { 152adf6ad9eSPeter Wemm ddptr += n; 153adf6ad9eSPeter Wemm space -= n; 154adf6ad9eSPeter Wemm } 155adf6ad9eSPeter Wemm } while (n > 0); 156adf6ad9eSPeter Wemm 157adf6ad9eSPeter Wemm ddeptr = ddptr; 158adf6ad9eSPeter Wemm flags |= __DTF_READALL; 159adf6ad9eSPeter Wemm 160adf6ad9eSPeter Wemm /* 161adf6ad9eSPeter Wemm * Re-open the directory. 162adf6ad9eSPeter Wemm * This has the effect of rewinding back to the 163adf6ad9eSPeter Wemm * top of the union stack and is needed by 164adf6ad9eSPeter Wemm * programs which plan to fchdir to a descriptor 165adf6ad9eSPeter Wemm * which has also been read -- see fts.c. 166adf6ad9eSPeter Wemm */ 167adf6ad9eSPeter Wemm if (flags & DTF_REWIND) { 1689233c4d9SJason Evans (void)_close(fd); 1699233c4d9SJason Evans if ((fd = _open(name, O_RDONLY)) == -1) { 17003dcee8dSBruce Evans saved_errno = errno; 171adf6ad9eSPeter Wemm free(buf); 172adf6ad9eSPeter Wemm free(dirp); 17303dcee8dSBruce Evans errno = saved_errno; 174adf6ad9eSPeter Wemm return (NULL); 175adf6ad9eSPeter Wemm } 176adf6ad9eSPeter Wemm } 177adf6ad9eSPeter Wemm 178adf6ad9eSPeter Wemm /* 179adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 180adf6ad9eSPeter Wemm * names. 181adf6ad9eSPeter Wemm */ 182adf6ad9eSPeter Wemm dirp->dd_buf = buf; 183adf6ad9eSPeter Wemm 184adf6ad9eSPeter Wemm /* 185adf6ad9eSPeter Wemm * Go round this loop twice... 186adf6ad9eSPeter Wemm * 187adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 188adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 189adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 190adf6ad9eSPeter Wemm */ 191adf6ad9eSPeter Wemm for (dpv = 0;;) { 192adf6ad9eSPeter Wemm n = 0; 193adf6ad9eSPeter Wemm ddptr = buf; 194adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 195adf6ad9eSPeter Wemm struct dirent *dp; 196adf6ad9eSPeter Wemm 197adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 19884d65005SJohn Birrell if ((long)dp & 03L) 199adf6ad9eSPeter Wemm break; 200adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 201adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 202adf6ad9eSPeter Wemm break; 203adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 204adf6ad9eSPeter Wemm if (dp->d_fileno) { 205adf6ad9eSPeter Wemm if (dpv) 206adf6ad9eSPeter Wemm dpv[n] = dp; 207adf6ad9eSPeter Wemm n++; 208adf6ad9eSPeter Wemm } 209adf6ad9eSPeter Wemm } 210adf6ad9eSPeter Wemm 211adf6ad9eSPeter Wemm if (dpv) { 212adf6ad9eSPeter Wemm struct dirent *xp; 213adf6ad9eSPeter Wemm 214adf6ad9eSPeter Wemm /* 215adf6ad9eSPeter Wemm * This sort must be stable. 216adf6ad9eSPeter Wemm */ 217adf6ad9eSPeter Wemm mergesort(dpv, n, sizeof(*dpv), alphasort); 218adf6ad9eSPeter Wemm 219adf6ad9eSPeter Wemm dpv[n] = NULL; 220adf6ad9eSPeter Wemm xp = NULL; 221adf6ad9eSPeter Wemm 222adf6ad9eSPeter Wemm /* 223adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 224adf6ad9eSPeter Wemm * zapping the inode number of any 225adf6ad9eSPeter Wemm * duplicate names. 226adf6ad9eSPeter Wemm */ 227adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 228adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 229adf6ad9eSPeter Wemm 230adf6ad9eSPeter Wemm if ((xp == NULL) || 231adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 232adf6ad9eSPeter Wemm xp = dp; 233adf6ad9eSPeter Wemm } else { 234adf6ad9eSPeter Wemm dp->d_fileno = 0; 235adf6ad9eSPeter Wemm } 236adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 237adf6ad9eSPeter Wemm (flags & DTF_HIDEW)) 238adf6ad9eSPeter Wemm dp->d_fileno = 0; 239adf6ad9eSPeter Wemm } 240adf6ad9eSPeter Wemm 241adf6ad9eSPeter Wemm free(dpv); 242adf6ad9eSPeter Wemm break; 243adf6ad9eSPeter Wemm } else { 244adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 245adf6ad9eSPeter Wemm if (dpv == NULL) 246adf6ad9eSPeter Wemm break; 247adf6ad9eSPeter Wemm } 248adf6ad9eSPeter Wemm } 249adf6ad9eSPeter Wemm 250adf6ad9eSPeter Wemm dirp->dd_len = len; 251adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 252adf6ad9eSPeter Wemm } else { 253adf6ad9eSPeter Wemm dirp->dd_len = incr; 2544da7d0f5SDoug Rabson dirp->dd_size = 0; 255d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 25603dcee8dSBruce Evans if (dirp->dd_buf == NULL) 25703dcee8dSBruce Evans goto fail; 25858f0484fSRodney W. Grimes dirp->dd_seek = 0; 259adf6ad9eSPeter Wemm flags &= ~DTF_REWIND; 260adf6ad9eSPeter Wemm } 261adf6ad9eSPeter Wemm 262adf6ad9eSPeter Wemm dirp->dd_loc = 0; 263adf6ad9eSPeter Wemm dirp->dd_fd = fd; 264adf6ad9eSPeter Wemm dirp->dd_flags = flags; 265d201fe46SDaniel Eischen dirp->dd_lock = NULL; 266adf6ad9eSPeter Wemm 26758f0484fSRodney W. Grimes /* 26858f0484fSRodney W. Grimes * Set up seek point for rewinddir. 26958f0484fSRodney W. Grimes */ 27058f0484fSRodney W. Grimes dirp->dd_rewind = telldir(dirp); 27155e2b2c6SBruce Evans 272adf6ad9eSPeter Wemm return (dirp); 27303dcee8dSBruce Evans 27403dcee8dSBruce Evans fail: 27503dcee8dSBruce Evans saved_errno = errno; 27603dcee8dSBruce Evans free(dirp); 2779233c4d9SJason Evans (void)_close(fd); 27803dcee8dSBruce Evans errno = saved_errno; 27903dcee8dSBruce Evans return (NULL); 28058f0484fSRodney W. Grimes } 281