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 */ 3758f0484fSRodney W. Grimes 3858f0484fSRodney W. Grimes #include <sys/param.h> 3955e2b2c6SBruce Evans #include <sys/stat.h> 40adf6ad9eSPeter Wemm #include <sys/mount.h> 4158f0484fSRodney W. Grimes 4258f0484fSRodney W. Grimes #include <dirent.h> 4355e2b2c6SBruce Evans #include <errno.h> 4458f0484fSRodney W. Grimes #include <fcntl.h> 4558f0484fSRodney W. Grimes #include <stdlib.h> 4658f0484fSRodney W. Grimes #include <unistd.h> 4758f0484fSRodney W. Grimes 4858f0484fSRodney W. Grimes /* 49adf6ad9eSPeter Wemm * Open a directory. 5058f0484fSRodney W. Grimes */ 5158f0484fSRodney W. Grimes DIR * 5258f0484fSRodney W. Grimes opendir(name) 5358f0484fSRodney W. Grimes const char *name; 5458f0484fSRodney W. Grimes { 55adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 56adf6ad9eSPeter Wemm } 57adf6ad9eSPeter Wemm 58adf6ad9eSPeter Wemm DIR * 59adf6ad9eSPeter Wemm __opendir2(name, flags) 60adf6ad9eSPeter Wemm const char *name; 61adf6ad9eSPeter Wemm int flags; 62adf6ad9eSPeter Wemm { 63adf6ad9eSPeter Wemm DIR *dirp; 64adf6ad9eSPeter Wemm int fd; 65adf6ad9eSPeter Wemm int incr; 66adf6ad9eSPeter Wemm int unionstack; 67adf6ad9eSPeter Wemm struct stat statb; 6858f0484fSRodney W. Grimes 690b50c8d6SBruce Evans /* 700b50c8d6SBruce Evans * stat() before open() because opening of special files may be 710b50c8d6SBruce Evans * harmful. fstat() after open because the file may have changed. 720b50c8d6SBruce Evans */ 73adf6ad9eSPeter Wemm if (stat(name, &statb) != 0) 740b50c8d6SBruce Evans return NULL; 75adf6ad9eSPeter Wemm if (!S_ISDIR(statb.st_mode)) { 760b50c8d6SBruce Evans errno = ENOTDIR; 770b50c8d6SBruce Evans return NULL; 780b50c8d6SBruce Evans } 7955e2b2c6SBruce Evans if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1) 80adf6ad9eSPeter Wemm return (NULL); 81adf6ad9eSPeter Wemm if (fstat(fd, &statb) || !S_ISDIR(statb.st_mode)) { 8255e2b2c6SBruce Evans errno = ENOTDIR; 83adf6ad9eSPeter Wemm close(fd); 84adf6ad9eSPeter Wemm return (NULL); 8558f0484fSRodney W. Grimes } 8655e2b2c6SBruce Evans if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 87adf6ad9eSPeter Wemm (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 88adf6ad9eSPeter Wemm close(fd); 89adf6ad9eSPeter Wemm return (NULL); 90adf6ad9eSPeter Wemm } 91adf6ad9eSPeter Wemm 9258f0484fSRodney W. Grimes /* 93d71458eeSPoul-Henning Kamp * Use the system page size if that is a multiple of DIRBLKSIZ 94adf6ad9eSPeter Wemm * this could speed things up in some cases. 95adf6ad9eSPeter Wemm * Hopefully this can be a big win someday by allowing page 96adf6ad9eSPeter Wemm * trades to user space to be done by getdirentries(). 9758f0484fSRodney W. Grimes */ 98adf6ad9eSPeter Wemm incr = getpagesize(); 99adf6ad9eSPeter Wemm if ((incr % DIRBLKSIZ) != 0) 100adf6ad9eSPeter Wemm incr = DIRBLKSIZ; 101adf6ad9eSPeter Wemm 102adf6ad9eSPeter Wemm /* 103adf6ad9eSPeter Wemm * Determine whether this directory is the top of a union stack. 104adf6ad9eSPeter Wemm */ 105adf6ad9eSPeter Wemm if (flags & DTF_NODUP) { 106adf6ad9eSPeter Wemm struct statfs sfb; 107adf6ad9eSPeter Wemm 108adf6ad9eSPeter Wemm if (fstatfs(fd, &sfb) < 0) { 109adf6ad9eSPeter Wemm free(dirp); 110adf6ad9eSPeter Wemm close(fd); 111adf6ad9eSPeter Wemm return (NULL); 112adf6ad9eSPeter Wemm } 113adf6ad9eSPeter Wemm unionstack = !strcmp(sfb.f_fstypename, "union"); 114adf6ad9eSPeter Wemm } else { 115adf6ad9eSPeter Wemm unionstack = 0; 116adf6ad9eSPeter Wemm } 117adf6ad9eSPeter Wemm 118adf6ad9eSPeter Wemm if (unionstack) { 119adf6ad9eSPeter Wemm int len = 0; 120adf6ad9eSPeter Wemm int space = 0; 121adf6ad9eSPeter Wemm char *buf = 0; 122adf6ad9eSPeter Wemm char *ddptr = 0; 123adf6ad9eSPeter Wemm char *ddeptr; 124adf6ad9eSPeter Wemm int n; 125adf6ad9eSPeter Wemm struct dirent **dpv; 126adf6ad9eSPeter Wemm 127adf6ad9eSPeter Wemm /* 128adf6ad9eSPeter Wemm * The strategy here is to read all the directory 129adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 130adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 131adf6ad9eSPeter Wemm * number to zero. 132adf6ad9eSPeter Wemm */ 133adf6ad9eSPeter Wemm 134adf6ad9eSPeter Wemm do { 135adf6ad9eSPeter Wemm /* 136adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 137adf6ad9eSPeter Wemm * available to getdirentries 138adf6ad9eSPeter Wemm */ 139adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 140adf6ad9eSPeter Wemm space += incr; 141adf6ad9eSPeter Wemm len += incr; 142adf6ad9eSPeter Wemm buf = realloc(buf, len); 143adf6ad9eSPeter Wemm if (buf == NULL) { 144adf6ad9eSPeter Wemm free(dirp); 145adf6ad9eSPeter Wemm close(fd); 146adf6ad9eSPeter Wemm return (NULL); 147adf6ad9eSPeter Wemm } 148adf6ad9eSPeter Wemm ddptr = buf + (len - space); 149adf6ad9eSPeter Wemm } 150adf6ad9eSPeter Wemm 151adf6ad9eSPeter Wemm n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 152adf6ad9eSPeter Wemm if (n > 0) { 153adf6ad9eSPeter Wemm ddptr += n; 154adf6ad9eSPeter Wemm space -= n; 155adf6ad9eSPeter Wemm } 156adf6ad9eSPeter Wemm } while (n > 0); 157adf6ad9eSPeter Wemm 158adf6ad9eSPeter Wemm ddeptr = ddptr; 159adf6ad9eSPeter Wemm flags |= __DTF_READALL; 160adf6ad9eSPeter Wemm 161adf6ad9eSPeter Wemm /* 162adf6ad9eSPeter Wemm * Re-open the directory. 163adf6ad9eSPeter Wemm * This has the effect of rewinding back to the 164adf6ad9eSPeter Wemm * top of the union stack and is needed by 165adf6ad9eSPeter Wemm * programs which plan to fchdir to a descriptor 166adf6ad9eSPeter Wemm * which has also been read -- see fts.c. 167adf6ad9eSPeter Wemm */ 168adf6ad9eSPeter Wemm if (flags & DTF_REWIND) { 169adf6ad9eSPeter Wemm (void) close(fd); 170adf6ad9eSPeter Wemm if ((fd = open(name, O_RDONLY)) == -1) { 171adf6ad9eSPeter Wemm free(buf); 172adf6ad9eSPeter Wemm free(dirp); 173adf6ad9eSPeter Wemm return (NULL); 174adf6ad9eSPeter Wemm } 175adf6ad9eSPeter Wemm } 176adf6ad9eSPeter Wemm 177adf6ad9eSPeter Wemm /* 178adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 179adf6ad9eSPeter Wemm * names. 180adf6ad9eSPeter Wemm */ 181adf6ad9eSPeter Wemm dirp->dd_buf = buf; 182adf6ad9eSPeter Wemm 183adf6ad9eSPeter Wemm /* 184adf6ad9eSPeter Wemm * Go round this loop twice... 185adf6ad9eSPeter Wemm * 186adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 187adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 188adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 189adf6ad9eSPeter Wemm */ 190adf6ad9eSPeter Wemm for (dpv = 0;;) { 191adf6ad9eSPeter Wemm n = 0; 192adf6ad9eSPeter Wemm ddptr = buf; 193adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 194adf6ad9eSPeter Wemm struct dirent *dp; 195adf6ad9eSPeter Wemm 196adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 197adf6ad9eSPeter Wemm if ((int)dp & 03) 198adf6ad9eSPeter Wemm break; 199adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 200adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 201adf6ad9eSPeter Wemm break; 202adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 203adf6ad9eSPeter Wemm if (dp->d_fileno) { 204adf6ad9eSPeter Wemm if (dpv) 205adf6ad9eSPeter Wemm dpv[n] = dp; 206adf6ad9eSPeter Wemm n++; 207adf6ad9eSPeter Wemm } 208adf6ad9eSPeter Wemm } 209adf6ad9eSPeter Wemm 210adf6ad9eSPeter Wemm if (dpv) { 211adf6ad9eSPeter Wemm struct dirent *xp; 212adf6ad9eSPeter Wemm 213adf6ad9eSPeter Wemm /* 214adf6ad9eSPeter Wemm * This sort must be stable. 215adf6ad9eSPeter Wemm */ 216adf6ad9eSPeter Wemm mergesort(dpv, n, sizeof(*dpv), alphasort); 217adf6ad9eSPeter Wemm 218adf6ad9eSPeter Wemm dpv[n] = NULL; 219adf6ad9eSPeter Wemm xp = NULL; 220adf6ad9eSPeter Wemm 221adf6ad9eSPeter Wemm /* 222adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 223adf6ad9eSPeter Wemm * zapping the inode number of any 224adf6ad9eSPeter Wemm * duplicate names. 225adf6ad9eSPeter Wemm */ 226adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 227adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 228adf6ad9eSPeter Wemm 229adf6ad9eSPeter Wemm if ((xp == NULL) || 230adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 231adf6ad9eSPeter Wemm xp = dp; 232adf6ad9eSPeter Wemm } else { 233adf6ad9eSPeter Wemm dp->d_fileno = 0; 234adf6ad9eSPeter Wemm } 235adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 236adf6ad9eSPeter Wemm (flags & DTF_HIDEW)) 237adf6ad9eSPeter Wemm dp->d_fileno = 0; 238adf6ad9eSPeter Wemm } 239adf6ad9eSPeter Wemm 240adf6ad9eSPeter Wemm free(dpv); 241adf6ad9eSPeter Wemm break; 242adf6ad9eSPeter Wemm } else { 243adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 244adf6ad9eSPeter Wemm if (dpv == NULL) 245adf6ad9eSPeter Wemm break; 246adf6ad9eSPeter Wemm } 247adf6ad9eSPeter Wemm } 248adf6ad9eSPeter Wemm 249adf6ad9eSPeter Wemm dirp->dd_len = len; 250adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 251adf6ad9eSPeter Wemm } else { 252adf6ad9eSPeter Wemm dirp->dd_len = incr; 253d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 254adf6ad9eSPeter Wemm if (dirp->dd_buf == NULL) { 255adf6ad9eSPeter Wemm free(dirp); 256adf6ad9eSPeter Wemm close (fd); 257adf6ad9eSPeter Wemm return (NULL); 258adf6ad9eSPeter Wemm } 25958f0484fSRodney W. Grimes dirp->dd_seek = 0; 260adf6ad9eSPeter Wemm flags &= ~DTF_REWIND; 261adf6ad9eSPeter Wemm } 262adf6ad9eSPeter Wemm 263adf6ad9eSPeter Wemm dirp->dd_loc = 0; 264adf6ad9eSPeter Wemm dirp->dd_fd = fd; 265adf6ad9eSPeter Wemm dirp->dd_flags = flags; 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); 27358f0484fSRodney W. Grimes } 274