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 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{ 69249401Sjilles struct stat statb; 70178256Sdelphij 71249401Sjilles /* Check that fd is associated with a directory. */ 72249401Sjilles if (_fstat(fd, &statb) != 0) 73249401Sjilles return (NULL); 74249401Sjilles if (!S_ISDIR(statb.st_mode)) { 75249401Sjilles errno = ENOTDIR; 76249401Sjilles return (NULL); 77249401Sjilles } 78249401Sjilles if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 79249401Sjilles return (NULL); 80178256Sdelphij return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP)); 81178256Sdelphij} 82178256Sdelphij 83178256SdelphijDIR * 84178253Sdelphij__opendir2(const char *name, int flags) 8523668Speter{ 8623668Speter int fd; 8723668Speter 88249401Sjilles if ((fd = _open(name, 89249401Sjilles O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 9023768Sbde return (NULL); 91178256Sdelphij 92178256Sdelphij return __opendir_common(fd, name, flags); 93178256Sdelphij} 94178256Sdelphij 95201604Skibstatic int 96202679Sacheopendir_compar(const void *p1, const void *p2) 97201604Skib{ 98201604Skib 99202572Sache return (strcmp((*(const struct dirent **)p1)->d_name, 100201604Skib (*(const struct dirent **)p2)->d_name)); 101201604Skib} 102201604Skib 103201604Skib/* 104178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 105178256Sdelphij */ 106178256Sdelphijstatic DIR * 107178256Sdelphij__opendir_common(int fd, const char *name, int flags) 108178256Sdelphij{ 109178256Sdelphij DIR *dirp; 110178256Sdelphij int incr; 111178256Sdelphij int saved_errno; 112178256Sdelphij int unionstack; 113178256Sdelphij 114249401Sjilles if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 115249401Sjilles return (NULL); 11623668Speter 117133723Sstefanf dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 11869841Sdeischen LIST_INIT(&dirp->dd_td->td_locq); 11969841Sdeischen dirp->dd_td->td_loccnt = 0; 12069841Sdeischen 1211573Srgrimes /* 12223768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 12323668Speter * Hopefully this can be a big win someday by allowing page 12471579Sdeischen * trades to user space to be done by _getdirentries(). 1251573Srgrimes */ 12623668Speter incr = getpagesize(); 12723668Speter if ((incr % DIRBLKSIZ) != 0) 12823668Speter incr = DIRBLKSIZ; 12923668Speter 13023668Speter /* 13123668Speter * Determine whether this directory is the top of a union stack. 13223668Speter */ 13323668Speter if (flags & DTF_NODUP) { 13423668Speter struct statfs sfb; 13523668Speter 13671579Sdeischen if (_fstatfs(fd, &sfb) < 0) 13723768Sbde goto fail; 138115047Stjr unionstack = !strcmp(sfb.f_fstypename, "unionfs") 13975860Sjoerg || (sfb.f_flags & MNT_UNION); 14023668Speter } else { 14123668Speter unionstack = 0; 14223668Speter } 14323668Speter 14423668Speter if (unionstack) { 14523668Speter int len = 0; 14623668Speter int space = 0; 14723668Speter char *buf = 0; 14823668Speter char *ddptr = 0; 14923668Speter char *ddeptr; 15023668Speter int n; 15123668Speter struct dirent **dpv; 15223668Speter 15323668Speter /* 15423668Speter * The strategy here is to read all the directory 15523668Speter * entries into a buffer, sort the buffer, and 15623668Speter * remove duplicate entries by setting the inode 15723668Speter * number to zero. 15823668Speter */ 15923668Speter 16023668Speter do { 16123668Speter /* 16223668Speter * Always make at least DIRBLKSIZ bytes 16371579Sdeischen * available to _getdirentries 16423668Speter */ 16523668Speter if (space < DIRBLKSIZ) { 16623668Speter space += incr; 16723668Speter len += incr; 16839327Simp buf = reallocf(buf, len); 16923768Sbde if (buf == NULL) 17023768Sbde goto fail; 17123668Speter ddptr = buf + (len - space); 17223668Speter } 17323668Speter 17471579Sdeischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 17523668Speter if (n > 0) { 17623668Speter ddptr += n; 17723668Speter space -= n; 17823668Speter } 17923668Speter } while (n > 0); 18023668Speter 18123668Speter ddeptr = ddptr; 18223668Speter flags |= __DTF_READALL; 18323668Speter 18423668Speter /* 18523668Speter * Re-open the directory. 18623668Speter * This has the effect of rewinding back to the 18723668Speter * top of the union stack and is needed by 18823668Speter * programs which plan to fchdir to a descriptor 18923668Speter * which has also been read -- see fts.c. 19023668Speter */ 19123668Speter if (flags & DTF_REWIND) { 19256698Sjasone (void)_close(fd); 193261813Sjilles if ((fd = _open(name, O_RDONLY | O_DIRECTORY | 194261813Sjilles O_CLOEXEC)) == -1) { 19523768Sbde saved_errno = errno; 19623668Speter free(buf); 19723668Speter free(dirp); 19823768Sbde errno = saved_errno; 19923668Speter return (NULL); 20023668Speter } 20123668Speter } 20223668Speter 20323668Speter /* 20423668Speter * There is now a buffer full of (possibly) duplicate 20523668Speter * names. 20623668Speter */ 20723668Speter dirp->dd_buf = buf; 20823668Speter 20923668Speter /* 21023668Speter * Go round this loop twice... 21123668Speter * 21223668Speter * Scan through the buffer, counting entries. 21323668Speter * On the second pass, save pointers to each one. 21423668Speter * Then sort the pointers and remove duplicate names. 21523668Speter */ 21623668Speter for (dpv = 0;;) { 21723668Speter n = 0; 21823668Speter ddptr = buf; 21923668Speter while (ddptr < ddeptr) { 22023668Speter struct dirent *dp; 22123668Speter 22223668Speter dp = (struct dirent *) ddptr; 22334357Sjb if ((long)dp & 03L) 22423668Speter break; 22523668Speter if ((dp->d_reclen <= 0) || 22623668Speter (dp->d_reclen > (ddeptr + 1 - ddptr))) 22723668Speter break; 22823668Speter ddptr += dp->d_reclen; 22923668Speter if (dp->d_fileno) { 23023668Speter if (dpv) 23123668Speter dpv[n] = dp; 23223668Speter n++; 23323668Speter } 23423668Speter } 23523668Speter 23623668Speter if (dpv) { 23723668Speter struct dirent *xp; 23823668Speter 23923668Speter /* 24023668Speter * This sort must be stable. 24123668Speter */ 242201604Skib mergesort(dpv, n, sizeof(*dpv), 243202679Sache opendir_compar); 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