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. 3292927338SJason Evans * 3392927338SJason Evans * $FreeBSD$ 3458f0484fSRodney W. Grimes */ 3558f0484fSRodney W. Grimes 3658f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 37adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 3858f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 3958f0484fSRodney W. Grimes 4058f0484fSRodney W. Grimes #include <sys/param.h> 41adf6ad9eSPeter Wemm #include <sys/mount.h> 4203dcee8dSBruce Evans #include <sys/stat.h> 4358f0484fSRodney W. Grimes 4458f0484fSRodney W. Grimes #include <dirent.h> 4555e2b2c6SBruce Evans #include <errno.h> 4658f0484fSRodney W. Grimes #include <fcntl.h> 4758f0484fSRodney W. Grimes #include <stdlib.h> 4858f0484fSRodney W. Grimes #include <unistd.h> 4958f0484fSRodney W. Grimes 5010d1cba0SDaniel Eischen #include "telldir.h" 5110d1cba0SDaniel Eischen 5258f0484fSRodney W. Grimes /* 53adf6ad9eSPeter Wemm * Open a directory. 5458f0484fSRodney W. Grimes */ 5558f0484fSRodney W. Grimes DIR * 5658f0484fSRodney W. Grimes opendir(name) 5758f0484fSRodney W. Grimes const char *name; 5858f0484fSRodney W. Grimes { 5903dcee8dSBruce Evans 60adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 61adf6ad9eSPeter Wemm } 62adf6ad9eSPeter Wemm 63adf6ad9eSPeter Wemm DIR * 64adf6ad9eSPeter Wemm __opendir2(name, flags) 65adf6ad9eSPeter Wemm const char *name; 66adf6ad9eSPeter Wemm int flags; 67adf6ad9eSPeter Wemm { 68adf6ad9eSPeter Wemm DIR *dirp; 69adf6ad9eSPeter Wemm int fd; 70adf6ad9eSPeter Wemm int incr; 7103dcee8dSBruce Evans int saved_errno; 72adf6ad9eSPeter Wemm int unionstack; 73adf6ad9eSPeter Wemm struct stat statb; 7458f0484fSRodney W. Grimes 750b50c8d6SBruce Evans /* 760b50c8d6SBruce Evans * stat() before open() because opening of special files may be 770b50c8d6SBruce Evans * harmful. fstat() after open because the file may have changed. 780b50c8d6SBruce Evans */ 79adf6ad9eSPeter Wemm if (stat(name, &statb) != 0) 8003dcee8dSBruce Evans return (NULL); 81adf6ad9eSPeter Wemm if (!S_ISDIR(statb.st_mode)) { 820b50c8d6SBruce Evans errno = ENOTDIR; 8303dcee8dSBruce Evans return (NULL); 840b50c8d6SBruce Evans } 859233c4d9SJason Evans if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 86adf6ad9eSPeter Wemm return (NULL); 8703dcee8dSBruce Evans dirp = NULL; 8803dcee8dSBruce Evans if (fstat(fd, &statb) != 0) 8903dcee8dSBruce Evans goto fail; 9003dcee8dSBruce Evans if (!S_ISDIR(statb.st_mode)) { 9155e2b2c6SBruce Evans errno = ENOTDIR; 9203dcee8dSBruce Evans goto fail; 9358f0484fSRodney W. Grimes } 949233c4d9SJason Evans if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 9510d1cba0SDaniel Eischen (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 9603dcee8dSBruce Evans goto fail; 97adf6ad9eSPeter Wemm 9810d1cba0SDaniel Eischen dirp->dd_td = (void *)dirp + sizeof(DIR); 9910d1cba0SDaniel Eischen LIST_INIT(&dirp->dd_td->td_locq); 10010d1cba0SDaniel Eischen dirp->dd_td->td_loccnt = 0; 10110d1cba0SDaniel Eischen 10258f0484fSRodney W. Grimes /* 10303dcee8dSBruce Evans * Use the system page size if that is a multiple of DIRBLKSIZ. 104adf6ad9eSPeter Wemm * Hopefully this can be a big win someday by allowing page 105adf6ad9eSPeter Wemm * trades to user space to be done by getdirentries(). 10658f0484fSRodney W. Grimes */ 107adf6ad9eSPeter Wemm incr = getpagesize(); 108adf6ad9eSPeter Wemm if ((incr % DIRBLKSIZ) != 0) 109adf6ad9eSPeter Wemm incr = DIRBLKSIZ; 110adf6ad9eSPeter Wemm 111adf6ad9eSPeter Wemm /* 112adf6ad9eSPeter Wemm * Determine whether this directory is the top of a union stack. 113adf6ad9eSPeter Wemm */ 114adf6ad9eSPeter Wemm if (flags & DTF_NODUP) { 115adf6ad9eSPeter Wemm struct statfs sfb; 116adf6ad9eSPeter Wemm 11703dcee8dSBruce Evans if (fstatfs(fd, &sfb) < 0) 11803dcee8dSBruce Evans goto fail; 119adf6ad9eSPeter Wemm unionstack = !strcmp(sfb.f_fstypename, "union"); 120adf6ad9eSPeter Wemm } else { 121adf6ad9eSPeter Wemm unionstack = 0; 122adf6ad9eSPeter Wemm } 123adf6ad9eSPeter Wemm 124adf6ad9eSPeter Wemm if (unionstack) { 125adf6ad9eSPeter Wemm int len = 0; 126adf6ad9eSPeter Wemm int space = 0; 127adf6ad9eSPeter Wemm char *buf = 0; 128adf6ad9eSPeter Wemm char *ddptr = 0; 129adf6ad9eSPeter Wemm char *ddeptr; 130adf6ad9eSPeter Wemm int n; 131adf6ad9eSPeter Wemm struct dirent **dpv; 132adf6ad9eSPeter Wemm 133adf6ad9eSPeter Wemm /* 134adf6ad9eSPeter Wemm * The strategy here is to read all the directory 135adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 136adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 137adf6ad9eSPeter Wemm * number to zero. 138adf6ad9eSPeter Wemm */ 139adf6ad9eSPeter Wemm 140adf6ad9eSPeter Wemm do { 141adf6ad9eSPeter Wemm /* 142adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 143adf6ad9eSPeter Wemm * available to getdirentries 144adf6ad9eSPeter Wemm */ 145adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 146adf6ad9eSPeter Wemm space += incr; 147adf6ad9eSPeter Wemm len += incr; 148e8420087SWarner Losh buf = reallocf(buf, len); 14903dcee8dSBruce Evans if (buf == NULL) 15003dcee8dSBruce Evans goto fail; 151adf6ad9eSPeter Wemm ddptr = buf + (len - space); 152adf6ad9eSPeter Wemm } 153adf6ad9eSPeter Wemm 154adf6ad9eSPeter Wemm n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 155adf6ad9eSPeter Wemm if (n > 0) { 156adf6ad9eSPeter Wemm ddptr += n; 157adf6ad9eSPeter Wemm space -= n; 158adf6ad9eSPeter Wemm } 159adf6ad9eSPeter Wemm } while (n > 0); 160adf6ad9eSPeter Wemm 161adf6ad9eSPeter Wemm ddeptr = ddptr; 162adf6ad9eSPeter Wemm flags |= __DTF_READALL; 163adf6ad9eSPeter Wemm 164adf6ad9eSPeter Wemm /* 165adf6ad9eSPeter Wemm * Re-open the directory. 166adf6ad9eSPeter Wemm * This has the effect of rewinding back to the 167adf6ad9eSPeter Wemm * top of the union stack and is needed by 168adf6ad9eSPeter Wemm * programs which plan to fchdir to a descriptor 169adf6ad9eSPeter Wemm * which has also been read -- see fts.c. 170adf6ad9eSPeter Wemm */ 171adf6ad9eSPeter Wemm if (flags & DTF_REWIND) { 1729233c4d9SJason Evans (void)_close(fd); 1739233c4d9SJason Evans if ((fd = _open(name, O_RDONLY)) == -1) { 17403dcee8dSBruce Evans saved_errno = errno; 175adf6ad9eSPeter Wemm free(buf); 176adf6ad9eSPeter Wemm free(dirp); 17703dcee8dSBruce Evans errno = saved_errno; 178adf6ad9eSPeter Wemm return (NULL); 179adf6ad9eSPeter Wemm } 180adf6ad9eSPeter Wemm } 181adf6ad9eSPeter Wemm 182adf6ad9eSPeter Wemm /* 183adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 184adf6ad9eSPeter Wemm * names. 185adf6ad9eSPeter Wemm */ 186adf6ad9eSPeter Wemm dirp->dd_buf = buf; 187adf6ad9eSPeter Wemm 188adf6ad9eSPeter Wemm /* 189adf6ad9eSPeter Wemm * Go round this loop twice... 190adf6ad9eSPeter Wemm * 191adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 192adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 193adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 194adf6ad9eSPeter Wemm */ 195adf6ad9eSPeter Wemm for (dpv = 0;;) { 196adf6ad9eSPeter Wemm n = 0; 197adf6ad9eSPeter Wemm ddptr = buf; 198adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 199adf6ad9eSPeter Wemm struct dirent *dp; 200adf6ad9eSPeter Wemm 201adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 20284d65005SJohn Birrell if ((long)dp & 03L) 203adf6ad9eSPeter Wemm break; 204adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 205adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 206adf6ad9eSPeter Wemm break; 207adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 208adf6ad9eSPeter Wemm if (dp->d_fileno) { 209adf6ad9eSPeter Wemm if (dpv) 210adf6ad9eSPeter Wemm dpv[n] = dp; 211adf6ad9eSPeter Wemm n++; 212adf6ad9eSPeter Wemm } 213adf6ad9eSPeter Wemm } 214adf6ad9eSPeter Wemm 215adf6ad9eSPeter Wemm if (dpv) { 216adf6ad9eSPeter Wemm struct dirent *xp; 217adf6ad9eSPeter Wemm 218adf6ad9eSPeter Wemm /* 219adf6ad9eSPeter Wemm * This sort must be stable. 220adf6ad9eSPeter Wemm */ 221adf6ad9eSPeter Wemm mergesort(dpv, n, sizeof(*dpv), alphasort); 222adf6ad9eSPeter Wemm 223adf6ad9eSPeter Wemm dpv[n] = NULL; 224adf6ad9eSPeter Wemm xp = NULL; 225adf6ad9eSPeter Wemm 226adf6ad9eSPeter Wemm /* 227adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 228adf6ad9eSPeter Wemm * zapping the inode number of any 229adf6ad9eSPeter Wemm * duplicate names. 230adf6ad9eSPeter Wemm */ 231adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 232adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 233adf6ad9eSPeter Wemm 234adf6ad9eSPeter Wemm if ((xp == NULL) || 235adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 236adf6ad9eSPeter Wemm xp = dp; 237adf6ad9eSPeter Wemm } else { 238adf6ad9eSPeter Wemm dp->d_fileno = 0; 239adf6ad9eSPeter Wemm } 240adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 241adf6ad9eSPeter Wemm (flags & DTF_HIDEW)) 242adf6ad9eSPeter Wemm dp->d_fileno = 0; 243adf6ad9eSPeter Wemm } 244adf6ad9eSPeter Wemm 245adf6ad9eSPeter Wemm free(dpv); 246adf6ad9eSPeter Wemm break; 247adf6ad9eSPeter Wemm } else { 248adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 249adf6ad9eSPeter Wemm if (dpv == NULL) 250adf6ad9eSPeter Wemm break; 251adf6ad9eSPeter Wemm } 252adf6ad9eSPeter Wemm } 253adf6ad9eSPeter Wemm 254adf6ad9eSPeter Wemm dirp->dd_len = len; 255adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 256adf6ad9eSPeter Wemm } else { 257adf6ad9eSPeter Wemm dirp->dd_len = incr; 258d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 25903dcee8dSBruce Evans if (dirp->dd_buf == NULL) 26003dcee8dSBruce Evans goto fail; 26158f0484fSRodney W. Grimes dirp->dd_seek = 0; 262adf6ad9eSPeter Wemm flags &= ~DTF_REWIND; 263adf6ad9eSPeter Wemm } 264adf6ad9eSPeter Wemm 265adf6ad9eSPeter Wemm dirp->dd_loc = 0; 266adf6ad9eSPeter Wemm dirp->dd_fd = fd; 267adf6ad9eSPeter Wemm dirp->dd_flags = flags; 268adf6ad9eSPeter Wemm 26958f0484fSRodney W. Grimes /* 27058f0484fSRodney W. Grimes * Set up seek point for rewinddir. 27158f0484fSRodney W. Grimes */ 27258f0484fSRodney W. Grimes dirp->dd_rewind = telldir(dirp); 27355e2b2c6SBruce Evans 274adf6ad9eSPeter Wemm return (dirp); 27503dcee8dSBruce Evans 27603dcee8dSBruce Evans fail: 27703dcee8dSBruce Evans saved_errno = errno; 27803dcee8dSBruce Evans free(dirp); 2799233c4d9SJason Evans (void)_close(fd); 28003dcee8dSBruce Evans errno = saved_errno; 28103dcee8dSBruce Evans return (NULL); 28258f0484fSRodney W. Grimes } 283