opendir.c revision 201604
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 201604 2010-01-05 20:20:31Z kib $"); 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/* 96201604Skib * POSIX 2008 and XSI 7 require alphasort() to call strcoll() for 97201604Skib * directory entries ordering. Use local copy that uses strcmp(). 98201604Skib */ 99201604Skibstatic int 100201604Skibopendir_alphasort(const void *p1, const void *p2) 101201604Skib{ 102201604Skib 103201604Skib return (strcmp((*(const struct dirent **)p1)->d_name, 104201604Skib (*(const struct dirent **)p2)->d_name)); 105201604Skib} 106201604Skib 107201604Skib/* 108178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 109178256Sdelphij */ 110178256Sdelphijstatic DIR * 111178256Sdelphij__opendir_common(int fd, const char *name, int flags) 112178256Sdelphij{ 113178256Sdelphij DIR *dirp; 114178256Sdelphij int incr; 115178256Sdelphij int saved_errno; 116178256Sdelphij int unionstack; 117178256Sdelphij struct stat statb; 118178256Sdelphij 11923768Sbde dirp = NULL; 120178256Sdelphij /* _fstat() the open handler because the file may have changed. */ 12171579Sdeischen if (_fstat(fd, &statb) != 0) 12223768Sbde goto fail; 12323768Sbde if (!S_ISDIR(statb.st_mode)) { 1247978Sbde errno = ENOTDIR; 12523768Sbde goto fail; 1267978Sbde } 12756698Sjasone if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 12869841Sdeischen (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 12923768Sbde goto fail; 13023668Speter 131133723Sstefanf dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 13269841Sdeischen LIST_INIT(&dirp->dd_td->td_locq); 13369841Sdeischen dirp->dd_td->td_loccnt = 0; 13469841Sdeischen 1351573Srgrimes /* 13623768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 13723668Speter * Hopefully this can be a big win someday by allowing page 13871579Sdeischen * trades to user space to be done by _getdirentries(). 1391573Srgrimes */ 14023668Speter incr = getpagesize(); 14123668Speter if ((incr % DIRBLKSIZ) != 0) 14223668Speter incr = DIRBLKSIZ; 14323668Speter 14423668Speter /* 14523668Speter * Determine whether this directory is the top of a union stack. 14623668Speter */ 14723668Speter if (flags & DTF_NODUP) { 14823668Speter struct statfs sfb; 14923668Speter 15071579Sdeischen if (_fstatfs(fd, &sfb) < 0) 15123768Sbde goto fail; 152115047Stjr unionstack = !strcmp(sfb.f_fstypename, "unionfs") 15375860Sjoerg || (sfb.f_flags & MNT_UNION); 15423668Speter } else { 15523668Speter unionstack = 0; 15623668Speter } 15723668Speter 15823668Speter if (unionstack) { 15923668Speter int len = 0; 16023668Speter int space = 0; 16123668Speter char *buf = 0; 16223668Speter char *ddptr = 0; 16323668Speter char *ddeptr; 16423668Speter int n; 16523668Speter struct dirent **dpv; 16623668Speter 16723668Speter /* 16823668Speter * The strategy here is to read all the directory 16923668Speter * entries into a buffer, sort the buffer, and 17023668Speter * remove duplicate entries by setting the inode 17123668Speter * number to zero. 17223668Speter */ 17323668Speter 17423668Speter do { 17523668Speter /* 17623668Speter * Always make at least DIRBLKSIZ bytes 17771579Sdeischen * available to _getdirentries 17823668Speter */ 17923668Speter if (space < DIRBLKSIZ) { 18023668Speter space += incr; 18123668Speter len += incr; 18239327Simp buf = reallocf(buf, len); 18323768Sbde if (buf == NULL) 18423768Sbde goto fail; 18523668Speter ddptr = buf + (len - space); 18623668Speter } 18723668Speter 18871579Sdeischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 18923668Speter if (n > 0) { 19023668Speter ddptr += n; 19123668Speter space -= n; 19223668Speter } 19323668Speter } while (n > 0); 19423668Speter 19523668Speter ddeptr = ddptr; 19623668Speter flags |= __DTF_READALL; 19723668Speter 19823668Speter /* 19923668Speter * Re-open the directory. 20023668Speter * This has the effect of rewinding back to the 20123668Speter * top of the union stack and is needed by 20223668Speter * programs which plan to fchdir to a descriptor 20323668Speter * which has also been read -- see fts.c. 20423668Speter */ 20523668Speter if (flags & DTF_REWIND) { 20656698Sjasone (void)_close(fd); 20756698Sjasone if ((fd = _open(name, O_RDONLY)) == -1) { 20823768Sbde saved_errno = errno; 20923668Speter free(buf); 21023668Speter free(dirp); 21123768Sbde errno = saved_errno; 21223668Speter return (NULL); 21323668Speter } 21423668Speter } 21523668Speter 21623668Speter /* 21723668Speter * There is now a buffer full of (possibly) duplicate 21823668Speter * names. 21923668Speter */ 22023668Speter dirp->dd_buf = buf; 22123668Speter 22223668Speter /* 22323668Speter * Go round this loop twice... 22423668Speter * 22523668Speter * Scan through the buffer, counting entries. 22623668Speter * On the second pass, save pointers to each one. 22723668Speter * Then sort the pointers and remove duplicate names. 22823668Speter */ 22923668Speter for (dpv = 0;;) { 23023668Speter n = 0; 23123668Speter ddptr = buf; 23223668Speter while (ddptr < ddeptr) { 23323668Speter struct dirent *dp; 23423668Speter 23523668Speter dp = (struct dirent *) ddptr; 23634357Sjb if ((long)dp & 03L) 23723668Speter break; 23823668Speter if ((dp->d_reclen <= 0) || 23923668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 24023668Speter break; 24123668Speter ddptr += dp->d_reclen; 24223668Speter if (dp->d_fileno) { 24323668Speter if (dpv) 24423668Speter dpv[n] = dp; 24523668Speter n++; 24623668Speter } 24723668Speter } 24823668Speter 24923668Speter if (dpv) { 25023668Speter struct dirent *xp; 25123668Speter 25223668Speter /* 25323668Speter * This sort must be stable. 25423668Speter */ 255201604Skib mergesort(dpv, n, sizeof(*dpv), 256201604Skib opendir_alphasort); 25723668Speter 25823668Speter dpv[n] = NULL; 25923668Speter xp = NULL; 26023668Speter 26123668Speter /* 26223668Speter * Scan through the buffer in sort order, 26323668Speter * zapping the inode number of any 26423668Speter * duplicate names. 26523668Speter */ 26623668Speter for (n = 0; dpv[n]; n++) { 26723668Speter struct dirent *dp = dpv[n]; 26823668Speter 26923668Speter if ((xp == NULL) || 27023668Speter strcmp(dp->d_name, xp->d_name)) { 27123668Speter xp = dp; 27223668Speter } else { 27323668Speter dp->d_fileno = 0; 27423668Speter } 27523668Speter if (dp->d_type == DT_WHT && 27623668Speter (flags & DTF_HIDEW)) 27723668Speter dp->d_fileno = 0; 27823668Speter } 27923668Speter 28023668Speter free(dpv); 28123668Speter break; 28223668Speter } else { 28323668Speter dpv = malloc((n+1) * sizeof(struct dirent *)); 28423668Speter if (dpv == NULL) 28523668Speter break; 28623668Speter } 28723668Speter } 28823668Speter 28923668Speter dirp->dd_len = len; 29023668Speter dirp->dd_size = ddptr - dirp->dd_buf; 29123668Speter } else { 29223668Speter dirp->dd_len = incr; 293123861Sdfr dirp->dd_size = 0; 29423668Speter dirp->dd_buf = malloc(dirp->dd_len); 29523768Sbde if (dirp->dd_buf == NULL) 29623768Sbde goto fail; 29723668Speter dirp->dd_seek = 0; 29823668Speter flags &= ~DTF_REWIND; 29923668Speter } 30023668Speter 30123668Speter dirp->dd_loc = 0; 3021573Srgrimes dirp->dd_fd = fd; 30323668Speter dirp->dd_flags = flags; 30471579Sdeischen dirp->dd_lock = NULL; 30523668Speter 3061573Srgrimes /* 3071573Srgrimes * Set up seek point for rewinddir. 3081573Srgrimes */ 3091573Srgrimes dirp->dd_rewind = telldir(dirp); 3107978Sbde 31123668Speter return (dirp); 31223768Sbde 31323768Sbdefail: 31423768Sbde saved_errno = errno; 31523768Sbde free(dirp); 31656698Sjasone (void)_close(fd); 31723768Sbde errno = saved_errno; 31823768Sbde return (NULL); 3191573Srgrimes} 320