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$"); 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 49235647Sgleb#include "gen-private.h" 5069841Sdeischen#include "telldir.h" 51178256Sdelphij 52247236Sjillesstatic DIR * __opendir_common(int, int); 53178256Sdelphij 541573Srgrimes/* 5523668Speter * Open a directory. 561573Srgrimes */ 571573SrgrimesDIR * 58178253Sdelphijopendir(const char *name) 591573Srgrimes{ 6073632Sobrien 6123668Speter return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 6223668Speter} 631573Srgrimes 64178256Sdelphij/* 65178256Sdelphij * Open a directory with existing file descriptor. 66178256Sdelphij */ 6723668SpeterDIR * 68178256Sdelphijfdopendir(int fd) 69178256Sdelphij{ 70232385Sru struct stat statb; 71178256Sdelphij 72232385Sru /* Check that fd is associated with a directory. */ 73232385Sru if (_fstat(fd, &statb) != 0) 74232385Sru return (NULL); 75232385Sru if (!S_ISDIR(statb.st_mode)) { 76232385Sru errno = ENOTDIR; 77232385Sru return (NULL); 78232385Sru } 79232385Sru if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 80232385Sru return (NULL); 81247236Sjilles return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP)); 82178256Sdelphij} 83178256Sdelphij 84178256SdelphijDIR * 85178253Sdelphij__opendir2(const char *name, int flags) 8623668Speter{ 8723668Speter int fd; 88227852Sjilles DIR *dir; 89227852Sjilles int saved_errno; 9023668Speter 91232385Sru if ((fd = _open(name, 92232385Sru O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 9323768Sbde return (NULL); 94178256Sdelphij 95247236Sjilles dir = __opendir_common(fd, flags); 96227852Sjilles if (dir == NULL) { 97227852Sjilles saved_errno = errno; 98227852Sjilles _close(fd); 99227852Sjilles errno = saved_errno; 100227852Sjilles } 101227852Sjilles return (dir); 102178256Sdelphij} 103178256Sdelphij 104201604Skibstatic int 105202679Sacheopendir_compar(const void *p1, const void *p2) 106201604Skib{ 107201604Skib 108202572Sache return (strcmp((*(const struct dirent **)p1)->d_name, 109201604Skib (*(const struct dirent **)p2)->d_name)); 110201604Skib} 111201604Skib 112201604Skib/* 113178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 114178256Sdelphij */ 115178256Sdelphijstatic DIR * 116247236Sjilles__opendir_common(int fd, int flags) 117178256Sdelphij{ 118178256Sdelphij DIR *dirp; 119178256Sdelphij int incr; 120178256Sdelphij int saved_errno; 121178256Sdelphij int unionstack; 122227852Sjilles int fd2; 123178256Sdelphij 124247236Sjilles fd2 = -1; 125247236Sjilles 126232385Sru if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 127232385Sru return (NULL); 12823668Speter 129133723Sstefanf dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 13069841Sdeischen LIST_INIT(&dirp->dd_td->td_locq); 13169841Sdeischen dirp->dd_td->td_loccnt = 0; 13269841Sdeischen 1331573Srgrimes /* 13423768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 13523668Speter * Hopefully this can be a big win someday by allowing page 13671579Sdeischen * trades to user space to be done by _getdirentries(). 1371573Srgrimes */ 13823668Speter incr = getpagesize(); 13923668Speter if ((incr % DIRBLKSIZ) != 0) 14023668Speter incr = DIRBLKSIZ; 14123668Speter 14223668Speter /* 14323668Speter * Determine whether this directory is the top of a union stack. 14423668Speter */ 14523668Speter if (flags & DTF_NODUP) { 14623668Speter struct statfs sfb; 14723668Speter 14871579Sdeischen if (_fstatfs(fd, &sfb) < 0) 14923768Sbde goto fail; 150115047Stjr unionstack = !strcmp(sfb.f_fstypename, "unionfs") 15175860Sjoerg || (sfb.f_flags & MNT_UNION); 15223668Speter } else { 15323668Speter unionstack = 0; 15423668Speter } 15523668Speter 15623668Speter if (unionstack) { 15723668Speter int len = 0; 15823668Speter int space = 0; 15923668Speter char *buf = 0; 16023668Speter char *ddptr = 0; 16123668Speter char *ddeptr; 16223668Speter int n; 16323668Speter struct dirent **dpv; 16423668Speter 16523668Speter /* 16623668Speter * The strategy here is to read all the directory 16723668Speter * entries into a buffer, sort the buffer, and 16823668Speter * remove duplicate entries by setting the inode 16923668Speter * number to zero. 170247236Sjilles * 171247236Sjilles * We reopen the directory because _getdirentries() 172247236Sjilles * on a MNT_UNION mount modifies the open directory, 173247236Sjilles * making it refer to the lower directory after the 174247236Sjilles * upper directory's entries are exhausted. 175247236Sjilles * This would otherwise break software that uses 176247236Sjilles * the directory descriptor for fchdir or *at 177247236Sjilles * functions, such as fts.c. 17823668Speter */ 179247236Sjilles if ((fd2 = _openat(fd, ".", O_RDONLY | O_CLOEXEC)) == -1) { 180247236Sjilles saved_errno = errno; 181247236Sjilles free(buf); 182247236Sjilles free(dirp); 183247236Sjilles errno = saved_errno; 184247236Sjilles return (NULL); 185247236Sjilles } 18623668Speter 18723668Speter do { 18823668Speter /* 18923668Speter * Always make at least DIRBLKSIZ bytes 19071579Sdeischen * available to _getdirentries 19123668Speter */ 19223668Speter if (space < DIRBLKSIZ) { 19323668Speter space += incr; 19423668Speter len += incr; 19539327Simp buf = reallocf(buf, len); 19623768Sbde if (buf == NULL) 19723768Sbde goto fail; 19823668Speter ddptr = buf + (len - space); 19923668Speter } 20023668Speter 201247236Sjilles n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 20223668Speter if (n > 0) { 20323668Speter ddptr += n; 20423668Speter space -= n; 20523668Speter } 20623668Speter } while (n > 0); 20723668Speter 20823668Speter ddeptr = ddptr; 20923668Speter flags |= __DTF_READALL; 21023668Speter 211247236Sjilles _close(fd2); 212247236Sjilles fd2 = -1; 21323668Speter 21423668Speter /* 21523668Speter * There is now a buffer full of (possibly) duplicate 21623668Speter * names. 21723668Speter */ 21823668Speter dirp->dd_buf = buf; 21923668Speter 22023668Speter /* 22123668Speter * Go round this loop twice... 22223668Speter * 22323668Speter * Scan through the buffer, counting entries. 22423668Speter * On the second pass, save pointers to each one. 22523668Speter * Then sort the pointers and remove duplicate names. 22623668Speter */ 22723668Speter for (dpv = 0;;) { 22823668Speter n = 0; 22923668Speter ddptr = buf; 23023668Speter while (ddptr < ddeptr) { 23123668Speter struct dirent *dp; 23223668Speter 23323668Speter dp = (struct dirent *) ddptr; 23434357Sjb if ((long)dp & 03L) 23523668Speter break; 23623668Speter if ((dp->d_reclen <= 0) || 23723668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 23823668Speter break; 23923668Speter ddptr += dp->d_reclen; 24023668Speter if (dp->d_fileno) { 24123668Speter if (dpv) 24223668Speter dpv[n] = dp; 24323668Speter n++; 24423668Speter } 24523668Speter } 24623668Speter 24723668Speter if (dpv) { 24823668Speter struct dirent *xp; 24923668Speter 25023668Speter /* 25123668Speter * This sort must be stable. 25223668Speter */ 253201604Skib mergesort(dpv, n, sizeof(*dpv), 254202679Sache opendir_compar); 25523668Speter 25623668Speter dpv[n] = NULL; 25723668Speter xp = NULL; 25823668Speter 25923668Speter /* 26023668Speter * Scan through the buffer in sort order, 26123668Speter * zapping the inode number of any 26223668Speter * duplicate names. 26323668Speter */ 26423668Speter for (n = 0; dpv[n]; n++) { 26523668Speter struct dirent *dp = dpv[n]; 26623668Speter 26723668Speter if ((xp == NULL) || 26823668Speter strcmp(dp->d_name, xp->d_name)) { 26923668Speter xp = dp; 27023668Speter } else { 27123668Speter dp->d_fileno = 0; 27223668Speter } 27323668Speter if (dp->d_type == DT_WHT && 27423668Speter (flags & DTF_HIDEW)) 27523668Speter dp->d_fileno = 0; 27623668Speter } 27723668Speter 27823668Speter free(dpv); 27923668Speter break; 28023668Speter } else { 28123668Speter dpv = malloc((n+1) * sizeof(struct dirent *)); 28223668Speter if (dpv == NULL) 28323668Speter break; 28423668Speter } 28523668Speter } 28623668Speter 28723668Speter dirp->dd_len = len; 28823668Speter dirp->dd_size = ddptr - dirp->dd_buf; 28923668Speter } else { 29023668Speter dirp->dd_len = incr; 291123861Sdfr dirp->dd_size = 0; 29223668Speter dirp->dd_buf = malloc(dirp->dd_len); 29323768Sbde if (dirp->dd_buf == NULL) 29423768Sbde goto fail; 29523668Speter dirp->dd_seek = 0; 29623668Speter } 29723668Speter 29823668Speter dirp->dd_loc = 0; 2991573Srgrimes dirp->dd_fd = fd; 30023668Speter dirp->dd_flags = flags; 30171579Sdeischen dirp->dd_lock = NULL; 30223668Speter 3031573Srgrimes /* 3041573Srgrimes * Set up seek point for rewinddir. 3051573Srgrimes */ 3061573Srgrimes dirp->dd_rewind = telldir(dirp); 3077978Sbde 30823668Speter return (dirp); 30923768Sbde 31023768Sbdefail: 31123768Sbde saved_errno = errno; 312247236Sjilles if (fd2 != -1) 313247236Sjilles _close(fd2); 31423768Sbde free(dirp); 31523768Sbde errno = saved_errno; 31623768Sbde return (NULL); 3171573Srgrimes} 318