opendir.c revision 178256
1178253Sdelphij/*- 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 * 4. Neither the name of the University nor the names of its contributors 141573Srgrimes * may be used to endorse or promote products derived from this software 151573Srgrimes * without specific prior written permission. 161573Srgrimes * 171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271573Srgrimes * SUCH DAMAGE. 281573Srgrimes */ 291573Srgrimes 301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3123668Speterstatic char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 321573Srgrimes#endif /* LIBC_SCCS and not lint */ 3390039Sobrien#include <sys/cdefs.h> 3490039Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/opendir.c 178256 2008-04-16 18:59:36Z delphij $"); 351573Srgrimes 3671579Sdeischen#include "namespace.h" 371573Srgrimes#include <sys/param.h> 3823768Sbde#include <sys/mount.h> 397978Sbde#include <sys/stat.h> 401573Srgrimes 411573Srgrimes#include <dirent.h> 427978Sbde#include <errno.h> 431573Srgrimes#include <fcntl.h> 441573Srgrimes#include <stdlib.h> 45108631Stjr#include <string.h> 461573Srgrimes#include <unistd.h> 4771579Sdeischen#include "un-namespace.h" 481573Srgrimes 4969841Sdeischen#include "telldir.h" 50178256Sdelphij 51178256Sdelphijstatic DIR * __opendir_common(int, const char *, int); 52178256Sdelphij 531573Srgrimes/* 5423668Speter * Open a directory. 551573Srgrimes */ 561573SrgrimesDIR * 57178253Sdelphijopendir(const char *name) 581573Srgrimes{ 5973632Sobrien 6023668Speter return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 6123668Speter} 621573Srgrimes 63178256Sdelphij/* 64178256Sdelphij * Open a directory with existing file descriptor. 65178256Sdelphij */ 6623668SpeterDIR * 67178256Sdelphijfdopendir(int fd) 68178256Sdelphij{ 69178256Sdelphij 70178256Sdelphij return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP)); 71178256Sdelphij} 72178256Sdelphij 73178256SdelphijDIR * 74178253Sdelphij__opendir2(const char *name, int flags) 7523668Speter{ 7623668Speter int fd; 7723668Speter struct stat statb; 7823668Speter 7914910Sbde /* 8071579Sdeischen * stat() before _open() because opening of special files may be 81178256Sdelphij * harmful. 8214910Sbde */ 8323668Speter if (stat(name, &statb) != 0) 8423768Sbde return (NULL); 8523668Speter if (!S_ISDIR(statb.st_mode)) { 8614910Sbde errno = ENOTDIR; 8723768Sbde return (NULL); 8814910Sbde } 8956698Sjasone if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 9023668Speter return (NULL); 91178256Sdelphij 92178256Sdelphij return __opendir_common(fd, name, flags); 93178256Sdelphij} 94178256Sdelphij 95178256Sdelphij/* 96178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 97178256Sdelphij */ 98178256Sdelphijstatic DIR * 99178256Sdelphij__opendir_common(int fd, const char *name, int flags) 100178256Sdelphij{ 101178256Sdelphij DIR *dirp; 102178256Sdelphij int incr; 103178256Sdelphij int saved_errno; 104178256Sdelphij int unionstack; 105178256Sdelphij struct stat statb; 106178256Sdelphij 10723768Sbde dirp = NULL; 108178256Sdelphij /* _fstat() the open handler because the file may have changed. */ 10971579Sdeischen if (_fstat(fd, &statb) != 0) 11023768Sbde goto fail; 11123768Sbde if (!S_ISDIR(statb.st_mode)) { 1127978Sbde errno = ENOTDIR; 11323768Sbde goto fail; 1147978Sbde } 11556698Sjasone if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 11669841Sdeischen (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 11723768Sbde goto fail; 11823668Speter 119133723Sstefanf dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 12069841Sdeischen LIST_INIT(&dirp->dd_td->td_locq); 12169841Sdeischen dirp->dd_td->td_loccnt = 0; 12269841Sdeischen 1231573Srgrimes /* 12423768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 12523668Speter * Hopefully this can be a big win someday by allowing page 12671579Sdeischen * trades to user space to be done by _getdirentries(). 1271573Srgrimes */ 12823668Speter incr = getpagesize(); 12923668Speter if ((incr % DIRBLKSIZ) != 0) 13023668Speter incr = DIRBLKSIZ; 13123668Speter 13223668Speter /* 13323668Speter * Determine whether this directory is the top of a union stack. 13423668Speter */ 13523668Speter if (flags & DTF_NODUP) { 13623668Speter struct statfs sfb; 13723668Speter 13871579Sdeischen if (_fstatfs(fd, &sfb) < 0) 13923768Sbde goto fail; 140115047Stjr unionstack = !strcmp(sfb.f_fstypename, "unionfs") 14175860Sjoerg || (sfb.f_flags & MNT_UNION); 14223668Speter } else { 14323668Speter unionstack = 0; 14423668Speter } 14523668Speter 14623668Speter if (unionstack) { 14723668Speter int len = 0; 14823668Speter int space = 0; 14923668Speter char *buf = 0; 15023668Speter char *ddptr = 0; 15123668Speter char *ddeptr; 15223668Speter int n; 15323668Speter struct dirent **dpv; 15423668Speter 15523668Speter /* 15623668Speter * The strategy here is to read all the directory 15723668Speter * entries into a buffer, sort the buffer, and 15823668Speter * remove duplicate entries by setting the inode 15923668Speter * number to zero. 16023668Speter */ 16123668Speter 16223668Speter do { 16323668Speter /* 16423668Speter * Always make at least DIRBLKSIZ bytes 16571579Sdeischen * available to _getdirentries 16623668Speter */ 16723668Speter if (space < DIRBLKSIZ) { 16823668Speter space += incr; 16923668Speter len += incr; 17039327Simp buf = reallocf(buf, len); 17123768Sbde if (buf == NULL) 17223768Sbde goto fail; 17323668Speter ddptr = buf + (len - space); 17423668Speter } 17523668Speter 17671579Sdeischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 17723668Speter if (n > 0) { 17823668Speter ddptr += n; 17923668Speter space -= n; 18023668Speter } 18123668Speter } while (n > 0); 18223668Speter 18323668Speter ddeptr = ddptr; 18423668Speter flags |= __DTF_READALL; 18523668Speter 18623668Speter /* 18723668Speter * Re-open the directory. 18823668Speter * This has the effect of rewinding back to the 18923668Speter * top of the union stack and is needed by 19023668Speter * programs which plan to fchdir to a descriptor 19123668Speter * which has also been read -- see fts.c. 19223668Speter */ 19323668Speter if (flags & DTF_REWIND) { 19456698Sjasone (void)_close(fd); 19556698Sjasone if ((fd = _open(name, O_RDONLY)) == -1) { 19623768Sbde saved_errno = errno; 19723668Speter free(buf); 19823668Speter free(dirp); 19923768Sbde errno = saved_errno; 20023668Speter return (NULL); 20123668Speter } 20223668Speter } 20323668Speter 20423668Speter /* 20523668Speter * There is now a buffer full of (possibly) duplicate 20623668Speter * names. 20723668Speter */ 20823668Speter dirp->dd_buf = buf; 20923668Speter 21023668Speter /* 21123668Speter * Go round this loop twice... 21223668Speter * 21323668Speter * Scan through the buffer, counting entries. 21423668Speter * On the second pass, save pointers to each one. 21523668Speter * Then sort the pointers and remove duplicate names. 21623668Speter */ 21723668Speter for (dpv = 0;;) { 21823668Speter n = 0; 21923668Speter ddptr = buf; 22023668Speter while (ddptr < ddeptr) { 22123668Speter struct dirent *dp; 22223668Speter 22323668Speter dp = (struct dirent *) ddptr; 22434357Sjb if ((long)dp & 03L) 22523668Speter break; 22623668Speter if ((dp->d_reclen <= 0) || 22723668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 22823668Speter break; 22923668Speter ddptr += dp->d_reclen; 23023668Speter if (dp->d_fileno) { 23123668Speter if (dpv) 23223668Speter dpv[n] = dp; 23323668Speter n++; 23423668Speter } 23523668Speter } 23623668Speter 23723668Speter if (dpv) { 23823668Speter struct dirent *xp; 23923668Speter 24023668Speter /* 24123668Speter * This sort must be stable. 24223668Speter */ 24323668Speter mergesort(dpv, n, sizeof(*dpv), alphasort); 24423668Speter 24523668Speter dpv[n] = NULL; 24623668Speter xp = NULL; 24723668Speter 24823668Speter /* 24923668Speter * Scan through the buffer in sort order, 25023668Speter * zapping the inode number of any 25123668Speter * duplicate names. 25223668Speter */ 25323668Speter for (n = 0; dpv[n]; n++) { 25423668Speter struct dirent *dp = dpv[n]; 25523668Speter 25623668Speter if ((xp == NULL) || 25723668Speter strcmp(dp->d_name, xp->d_name)) { 25823668Speter xp = dp; 25923668Speter } else { 26023668Speter dp->d_fileno = 0; 26123668Speter } 26223668Speter if (dp->d_type == DT_WHT && 26323668Speter (flags & DTF_HIDEW)) 26423668Speter dp->d_fileno = 0; 26523668Speter } 26623668Speter 26723668Speter free(dpv); 26823668Speter break; 26923668Speter } else { 27023668Speter dpv = malloc((n+1) * sizeof(struct dirent *)); 27123668Speter if (dpv == NULL) 27223668Speter break; 27323668Speter } 27423668Speter } 27523668Speter 27623668Speter dirp->dd_len = len; 27723668Speter dirp->dd_size = ddptr - dirp->dd_buf; 27823668Speter } else { 27923668Speter dirp->dd_len = incr; 280123861Sdfr dirp->dd_size = 0; 28123668Speter dirp->dd_buf = malloc(dirp->dd_len); 28223768Sbde if (dirp->dd_buf == NULL) 28323768Sbde goto fail; 28423668Speter dirp->dd_seek = 0; 28523668Speter flags &= ~DTF_REWIND; 28623668Speter } 28723668Speter 28823668Speter dirp->dd_loc = 0; 2891573Srgrimes dirp->dd_fd = fd; 29023668Speter dirp->dd_flags = flags; 29171579Sdeischen dirp->dd_lock = NULL; 29223668Speter 2931573Srgrimes /* 2941573Srgrimes * Set up seek point for rewinddir. 2951573Srgrimes */ 2961573Srgrimes dirp->dd_rewind = telldir(dirp); 2977978Sbde 29823668Speter return (dirp); 29923768Sbde 30023768Sbdefail: 30123768Sbde saved_errno = errno; 30223768Sbde free(dirp); 30356698Sjasone (void)_close(fd); 30423768Sbde errno = saved_errno; 30523768Sbde return (NULL); 3061573Srgrimes} 307