opendir.c revision 34357
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> 3923768Sbde#include <sys/mount.h> 407978Sbde#include <sys/stat.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{ 5523768Sbde 5623668Speter return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 5723668Speter} 581573Srgrimes 5923668SpeterDIR * 6023668Speter__opendir2(name, flags) 6123668Speter const char *name; 6223668Speter int flags; 6323668Speter{ 6423668Speter DIR *dirp; 6523668Speter int fd; 6623668Speter int incr; 6723768Sbde int saved_errno; 6823668Speter int unionstack; 6923668Speter struct stat statb; 7023668Speter 7114910Sbde /* 7214910Sbde * stat() before open() because opening of special files may be 7314910Sbde * harmful. fstat() after open because the file may have changed. 7414910Sbde */ 7523668Speter if (stat(name, &statb) != 0) 7623768Sbde return (NULL); 7723668Speter if (!S_ISDIR(statb.st_mode)) { 7814910Sbde errno = ENOTDIR; 7923768Sbde return (NULL); 8014910Sbde } 817978Sbde if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1) 8223668Speter return (NULL); 8323768Sbde dirp = NULL; 8423768Sbde if (fstat(fd, &statb) != 0) 8523768Sbde goto fail; 8623768Sbde if (!S_ISDIR(statb.st_mode)) { 877978Sbde errno = ENOTDIR; 8823768Sbde goto fail; 897978Sbde } 901573Srgrimes if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 9123768Sbde (dirp = malloc(sizeof(DIR))) == NULL) 9223768Sbde goto fail; 9323668Speter 941573Srgrimes /* 9523768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 9623668Speter * Hopefully this can be a big win someday by allowing page 9723668Speter * trades to user space to be done by getdirentries(). 981573Srgrimes */ 9923668Speter incr = getpagesize(); 10023668Speter if ((incr % DIRBLKSIZ) != 0) 10123668Speter incr = DIRBLKSIZ; 10223668Speter 10323668Speter /* 10423668Speter * Determine whether this directory is the top of a union stack. 10523668Speter */ 10623668Speter if (flags & DTF_NODUP) { 10723668Speter struct statfs sfb; 10823668Speter 10923768Sbde if (fstatfs(fd, &sfb) < 0) 11023768Sbde goto fail; 11123668Speter unionstack = !strcmp(sfb.f_fstypename, "union"); 11223668Speter } else { 11323668Speter unionstack = 0; 11423668Speter } 11523668Speter 11623668Speter if (unionstack) { 11723668Speter int len = 0; 11823668Speter int space = 0; 11923668Speter char *buf = 0; 12023668Speter char *ddptr = 0; 12123668Speter char *ddeptr; 12223668Speter int n; 12323668Speter struct dirent **dpv; 12423668Speter 12523668Speter /* 12623668Speter * The strategy here is to read all the directory 12723668Speter * entries into a buffer, sort the buffer, and 12823668Speter * remove duplicate entries by setting the inode 12923668Speter * number to zero. 13023668Speter */ 13123668Speter 13223668Speter do { 13323668Speter /* 13423668Speter * Always make at least DIRBLKSIZ bytes 13523668Speter * available to getdirentries 13623668Speter */ 13723668Speter if (space < DIRBLKSIZ) { 13823668Speter space += incr; 13923668Speter len += incr; 14023668Speter buf = realloc(buf, len); 14123768Sbde if (buf == NULL) 14223768Sbde goto fail; 14323668Speter ddptr = buf + (len - space); 14423668Speter } 14523668Speter 14623668Speter n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 14723668Speter if (n > 0) { 14823668Speter ddptr += n; 14923668Speter space -= n; 15023668Speter } 15123668Speter } while (n > 0); 15223668Speter 15323668Speter ddeptr = ddptr; 15423668Speter flags |= __DTF_READALL; 15523668Speter 15623668Speter /* 15723668Speter * Re-open the directory. 15823668Speter * This has the effect of rewinding back to the 15923668Speter * top of the union stack and is needed by 16023668Speter * programs which plan to fchdir to a descriptor 16123668Speter * which has also been read -- see fts.c. 16223668Speter */ 16323668Speter if (flags & DTF_REWIND) { 16423668Speter (void) close(fd); 16523668Speter if ((fd = open(name, O_RDONLY)) == -1) { 16623768Sbde saved_errno = errno; 16723668Speter free(buf); 16823668Speter free(dirp); 16923768Sbde errno = saved_errno; 17023668Speter return (NULL); 17123668Speter } 17223668Speter } 17323668Speter 17423668Speter /* 17523668Speter * There is now a buffer full of (possibly) duplicate 17623668Speter * names. 17723668Speter */ 17823668Speter dirp->dd_buf = buf; 17923668Speter 18023668Speter /* 18123668Speter * Go round this loop twice... 18223668Speter * 18323668Speter * Scan through the buffer, counting entries. 18423668Speter * On the second pass, save pointers to each one. 18523668Speter * Then sort the pointers and remove duplicate names. 18623668Speter */ 18723668Speter for (dpv = 0;;) { 18823668Speter n = 0; 18923668Speter ddptr = buf; 19023668Speter while (ddptr < ddeptr) { 19123668Speter struct dirent *dp; 19223668Speter 19323668Speter dp = (struct dirent *) ddptr; 19434357Sjb if ((long)dp & 03L) 19523668Speter break; 19623668Speter if ((dp->d_reclen <= 0) || 19723668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 19823668Speter break; 19923668Speter ddptr += dp->d_reclen; 20023668Speter if (dp->d_fileno) { 20123668Speter if (dpv) 20223668Speter dpv[n] = dp; 20323668Speter n++; 20423668Speter } 20523668Speter } 20623668Speter 20723668Speter if (dpv) { 20823668Speter struct dirent *xp; 20923668Speter 21023668Speter /* 21123668Speter * This sort must be stable. 21223668Speter */ 21323668Speter mergesort(dpv, n, sizeof(*dpv), alphasort); 21423668Speter 21523668Speter dpv[n] = NULL; 21623668Speter xp = NULL; 21723668Speter 21823668Speter /* 21923668Speter * Scan through the buffer in sort order, 22023668Speter * zapping the inode number of any 22123668Speter * duplicate names. 22223668Speter */ 22323668Speter for (n = 0; dpv[n]; n++) { 22423668Speter struct dirent *dp = dpv[n]; 22523668Speter 22623668Speter if ((xp == NULL) || 22723668Speter strcmp(dp->d_name, xp->d_name)) { 22823668Speter xp = dp; 22923668Speter } else { 23023668Speter dp->d_fileno = 0; 23123668Speter } 23223668Speter if (dp->d_type == DT_WHT && 23323668Speter (flags & DTF_HIDEW)) 23423668Speter dp->d_fileno = 0; 23523668Speter } 23623668Speter 23723668Speter free(dpv); 23823668Speter break; 23923668Speter } else { 24023668Speter dpv = malloc((n+1) * sizeof(struct dirent *)); 24123668Speter if (dpv == NULL) 24223668Speter break; 24323668Speter } 24423668Speter } 24523668Speter 24623668Speter dirp->dd_len = len; 24723668Speter dirp->dd_size = ddptr - dirp->dd_buf; 24823668Speter } else { 24923668Speter dirp->dd_len = incr; 25023668Speter dirp->dd_buf = malloc(dirp->dd_len); 25123768Sbde if (dirp->dd_buf == NULL) 25223768Sbde goto fail; 25323668Speter dirp->dd_seek = 0; 25423668Speter flags &= ~DTF_REWIND; 25523668Speter } 25623668Speter 25723668Speter dirp->dd_loc = 0; 2581573Srgrimes dirp->dd_fd = fd; 25923668Speter dirp->dd_flags = flags; 26023668Speter 2611573Srgrimes /* 2621573Srgrimes * Set up seek point for rewinddir. 2631573Srgrimes */ 2641573Srgrimes dirp->dd_rewind = telldir(dirp); 2657978Sbde 26623668Speter return (dirp); 26723768Sbde 26823768Sbdefail: 26923768Sbde saved_errno = errno; 27023768Sbde free(dirp); 27123768Sbde (void) close(fd); 27223768Sbde errno = saved_errno; 27323768Sbde return (NULL); 2741573Srgrimes} 275