opendir.c revision 23668
11573Srgrimes/* 21573Srgrimes * Copyright (c) 1983, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 3. All advertising materials mentioning features or use of this software 141573Srgrimes * must display the following acknowledgement: 151573Srgrimes * This product includes software developed by the University of 161573Srgrimes * California, Berkeley and its contributors. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 331573Srgrimes 341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3523668Speterstatic char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 371573Srgrimes 381573Srgrimes#include <sys/param.h> 397978Sbde#include <sys/stat.h> 4023668Speter#include <sys/mount.h> 411573Srgrimes 421573Srgrimes#include <dirent.h> 437978Sbde#include <errno.h> 441573Srgrimes#include <fcntl.h> 451573Srgrimes#include <stdlib.h> 461573Srgrimes#include <unistd.h> 471573Srgrimes 481573Srgrimes/* 4923668Speter * Open a directory. 501573Srgrimes */ 511573SrgrimesDIR * 521573Srgrimesopendir(name) 531573Srgrimes const char *name; 541573Srgrimes{ 5523668Speter return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 5623668Speter} 571573Srgrimes 5823668SpeterDIR * 5923668Speter__opendir2(name, flags) 6023668Speter const char *name; 6123668Speter int flags; 6223668Speter{ 6323668Speter DIR *dirp; 6423668Speter int fd; 6523668Speter int incr; 6623668Speter int unionstack; 6723668Speter struct stat statb; 6823668Speter 6914910Sbde /* 7014910Sbde * stat() before open() because opening of special files may be 7114910Sbde * harmful. fstat() after open because the file may have changed. 7214910Sbde */ 7323668Speter if (stat(name, &statb) != 0) 7414910Sbde return NULL; 7523668Speter if (!S_ISDIR(statb.st_mode)) { 7614910Sbde errno = ENOTDIR; 7714910Sbde return NULL; 7814910Sbde } 797978Sbde if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1) 8023668Speter return (NULL); 8123668Speter if (fstat(fd, &statb) || !S_ISDIR(statb.st_mode)) { 827978Sbde errno = ENOTDIR; 8323668Speter close(fd); 8423668Speter return (NULL); 857978Sbde } 861573Srgrimes if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 8723668Speter (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 8823668Speter close(fd); 8923668Speter return (NULL); 9023668Speter } 9123668Speter 921573Srgrimes /* 9315528Sphk * Use the system page size if that is a multiple of DIRBLKSIZ 9423668Speter * this could speed things up in some cases. 9523668Speter * Hopefully this can be a big win someday by allowing page 9623668Speter * trades to user space to be done by getdirentries(). 971573Srgrimes */ 9823668Speter incr = getpagesize(); 9923668Speter if ((incr % DIRBLKSIZ) != 0) 10023668Speter incr = DIRBLKSIZ; 10123668Speter 10223668Speter /* 10323668Speter * Determine whether this directory is the top of a union stack. 10423668Speter */ 10523668Speter if (flags & DTF_NODUP) { 10623668Speter struct statfs sfb; 10723668Speter 10823668Speter if (fstatfs(fd, &sfb) < 0) { 10923668Speter free(dirp); 11023668Speter close(fd); 11123668Speter return (NULL); 11223668Speter } 11323668Speter unionstack = !strcmp(sfb.f_fstypename, "union"); 11423668Speter } else { 11523668Speter unionstack = 0; 11623668Speter } 11723668Speter 11823668Speter if (unionstack) { 11923668Speter int len = 0; 12023668Speter int space = 0; 12123668Speter char *buf = 0; 12223668Speter char *ddptr = 0; 12323668Speter char *ddeptr; 12423668Speter int n; 12523668Speter struct dirent **dpv; 12623668Speter 12723668Speter /* 12823668Speter * The strategy here is to read all the directory 12923668Speter * entries into a buffer, sort the buffer, and 13023668Speter * remove duplicate entries by setting the inode 13123668Speter * number to zero. 13223668Speter */ 13323668Speter 13423668Speter do { 13523668Speter /* 13623668Speter * Always make at least DIRBLKSIZ bytes 13723668Speter * available to getdirentries 13823668Speter */ 13923668Speter if (space < DIRBLKSIZ) { 14023668Speter space += incr; 14123668Speter len += incr; 14223668Speter buf = realloc(buf, len); 14323668Speter if (buf == NULL) { 14423668Speter free(dirp); 14523668Speter close(fd); 14623668Speter return (NULL); 14723668Speter } 14823668Speter ddptr = buf + (len - space); 14923668Speter } 15023668Speter 15123668Speter n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 15223668Speter if (n > 0) { 15323668Speter ddptr += n; 15423668Speter space -= n; 15523668Speter } 15623668Speter } while (n > 0); 15723668Speter 15823668Speter ddeptr = ddptr; 15923668Speter flags |= __DTF_READALL; 16023668Speter 16123668Speter /* 16223668Speter * Re-open the directory. 16323668Speter * This has the effect of rewinding back to the 16423668Speter * top of the union stack and is needed by 16523668Speter * programs which plan to fchdir to a descriptor 16623668Speter * which has also been read -- see fts.c. 16723668Speter */ 16823668Speter if (flags & DTF_REWIND) { 16923668Speter (void) close(fd); 17023668Speter if ((fd = open(name, O_RDONLY)) == -1) { 17123668Speter free(buf); 17223668Speter free(dirp); 17323668Speter return (NULL); 17423668Speter } 17523668Speter } 17623668Speter 17723668Speter /* 17823668Speter * There is now a buffer full of (possibly) duplicate 17923668Speter * names. 18023668Speter */ 18123668Speter dirp->dd_buf = buf; 18223668Speter 18323668Speter /* 18423668Speter * Go round this loop twice... 18523668Speter * 18623668Speter * Scan through the buffer, counting entries. 18723668Speter * On the second pass, save pointers to each one. 18823668Speter * Then sort the pointers and remove duplicate names. 18923668Speter */ 19023668Speter for (dpv = 0;;) { 19123668Speter n = 0; 19223668Speter ddptr = buf; 19323668Speter while (ddptr < ddeptr) { 19423668Speter struct dirent *dp; 19523668Speter 19623668Speter dp = (struct dirent *) ddptr; 19723668Speter if ((int)dp & 03) 19823668Speter break; 19923668Speter if ((dp->d_reclen <= 0) || 20023668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 20123668Speter break; 20223668Speter ddptr += dp->d_reclen; 20323668Speter if (dp->d_fileno) { 20423668Speter if (dpv) 20523668Speter dpv[n] = dp; 20623668Speter n++; 20723668Speter } 20823668Speter } 20923668Speter 21023668Speter if (dpv) { 21123668Speter struct dirent *xp; 21223668Speter 21323668Speter /* 21423668Speter * This sort must be stable. 21523668Speter */ 21623668Speter mergesort(dpv, n, sizeof(*dpv), alphasort); 21723668Speter 21823668Speter dpv[n] = NULL; 21923668Speter xp = NULL; 22023668Speter 22123668Speter /* 22223668Speter * Scan through the buffer in sort order, 22323668Speter * zapping the inode number of any 22423668Speter * duplicate names. 22523668Speter */ 22623668Speter for (n = 0; dpv[n]; n++) { 22723668Speter struct dirent *dp = dpv[n]; 22823668Speter 22923668Speter if ((xp == NULL) || 23023668Speter strcmp(dp->d_name, xp->d_name)) { 23123668Speter xp = dp; 23223668Speter } else { 23323668Speter dp->d_fileno = 0; 23423668Speter } 23523668Speter if (dp->d_type == DT_WHT && 23623668Speter (flags & DTF_HIDEW)) 23723668Speter dp->d_fileno = 0; 23823668Speter } 23923668Speter 24023668Speter free(dpv); 24123668Speter break; 24223668Speter } else { 24323668Speter dpv = malloc((n+1) * sizeof(struct dirent *)); 24423668Speter if (dpv == NULL) 24523668Speter break; 24623668Speter } 24723668Speter } 24823668Speter 24923668Speter dirp->dd_len = len; 25023668Speter dirp->dd_size = ddptr - dirp->dd_buf; 25123668Speter } else { 25223668Speter dirp->dd_len = incr; 25323668Speter dirp->dd_buf = malloc(dirp->dd_len); 25423668Speter if (dirp->dd_buf == NULL) { 25523668Speter free(dirp); 25623668Speter close (fd); 25723668Speter return (NULL); 25823668Speter } 25923668Speter dirp->dd_seek = 0; 26023668Speter flags &= ~DTF_REWIND; 26123668Speter } 26223668Speter 26323668Speter dirp->dd_loc = 0; 2641573Srgrimes dirp->dd_fd = fd; 26523668Speter dirp->dd_flags = flags; 26623668Speter 2671573Srgrimes /* 2681573Srgrimes * Set up seek point for rewinddir. 2691573Srgrimes */ 2701573Srgrimes dirp->dd_rewind = telldir(dirp); 2717978Sbde 27223668Speter return (dirp); 2731573Srgrimes} 274