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: releng/10.3/lib/libc/gen/opendir.c 270002 2014-08-14 20:20:21Z jhb $"); 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 52270002Sjhbstatic DIR * __opendir_common(int, int, bool); 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{ 70178256Sdelphij 71232385Sru if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 72232385Sru return (NULL); 73270002Sjhb return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); 74178256Sdelphij} 75178256Sdelphij 76178256SdelphijDIR * 77178253Sdelphij__opendir2(const char *name, int flags) 7823668Speter{ 7923668Speter int fd; 80227852Sjilles DIR *dir; 81227852Sjilles int saved_errno; 8223668Speter 83270002Sjhb if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 84270002Sjhb return (NULL); 85232385Sru if ((fd = _open(name, 86232385Sru O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 8723768Sbde return (NULL); 88178256Sdelphij 89270002Sjhb dir = __opendir_common(fd, flags, false); 90227852Sjilles if (dir == NULL) { 91227852Sjilles saved_errno = errno; 92227852Sjilles _close(fd); 93227852Sjilles errno = saved_errno; 94227852Sjilles } 95227852Sjilles return (dir); 96178256Sdelphij} 97178256Sdelphij 98201604Skibstatic int 99202679Sacheopendir_compar(const void *p1, const void *p2) 100201604Skib{ 101201604Skib 102202572Sache return (strcmp((*(const struct dirent **)p1)->d_name, 103201604Skib (*(const struct dirent **)p2)->d_name)); 104201604Skib} 105201604Skib 106201604Skib/* 107270002Sjhb * For a directory at the top of a unionfs stack, the entire directory's 108270002Sjhb * contents are read and cached locally until the next call to rewinddir(). 109270002Sjhb * For the fdopendir() case, the initial seek position must be preserved. 110270002Sjhb * For rewinddir(), the full directory should always be re-read from the 111270002Sjhb * beginning. 112270002Sjhb * 113270002Sjhb * If an error occurs, the existing buffer and state of 'dirp' is left 114270002Sjhb * unchanged. 115270002Sjhb */ 116270002Sjhbbool 117270002Sjhb_filldir(DIR *dirp, bool use_current_pos) 118270002Sjhb{ 119270002Sjhb struct dirent **dpv; 120270002Sjhb char *buf, *ddptr, *ddeptr; 121270002Sjhb off_t pos; 122270002Sjhb int fd2, incr, len, n, saved_errno, space; 123270002Sjhb 124270002Sjhb len = 0; 125270002Sjhb space = 0; 126270002Sjhb buf = NULL; 127270002Sjhb ddptr = NULL; 128270002Sjhb 129270002Sjhb /* 130270002Sjhb * Use the system page size if that is a multiple of DIRBLKSIZ. 131270002Sjhb * Hopefully this can be a big win someday by allowing page 132270002Sjhb * trades to user space to be done by _getdirentries(). 133270002Sjhb */ 134270002Sjhb incr = getpagesize(); 135270002Sjhb if ((incr % DIRBLKSIZ) != 0) 136270002Sjhb incr = DIRBLKSIZ; 137270002Sjhb 138270002Sjhb /* 139270002Sjhb * The strategy here is to read all the directory 140270002Sjhb * entries into a buffer, sort the buffer, and 141270002Sjhb * remove duplicate entries by setting the inode 142270002Sjhb * number to zero. 143270002Sjhb * 144270002Sjhb * We reopen the directory because _getdirentries() 145270002Sjhb * on a MNT_UNION mount modifies the open directory, 146270002Sjhb * making it refer to the lower directory after the 147270002Sjhb * upper directory's entries are exhausted. 148270002Sjhb * This would otherwise break software that uses 149270002Sjhb * the directory descriptor for fchdir or *at 150270002Sjhb * functions, such as fts.c. 151270002Sjhb */ 152270002Sjhb if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 153270002Sjhb return (false); 154270002Sjhb 155270002Sjhb if (use_current_pos) { 156270002Sjhb pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 157270002Sjhb if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 158270002Sjhb saved_errno = errno; 159270002Sjhb _close(fd2); 160270002Sjhb errno = saved_errno; 161270002Sjhb return (false); 162270002Sjhb } 163270002Sjhb } 164270002Sjhb 165270002Sjhb do { 166270002Sjhb /* 167270002Sjhb * Always make at least DIRBLKSIZ bytes 168270002Sjhb * available to _getdirentries 169270002Sjhb */ 170270002Sjhb if (space < DIRBLKSIZ) { 171270002Sjhb space += incr; 172270002Sjhb len += incr; 173270002Sjhb buf = reallocf(buf, len); 174270002Sjhb if (buf == NULL) { 175270002Sjhb saved_errno = errno; 176270002Sjhb _close(fd2); 177270002Sjhb errno = saved_errno; 178270002Sjhb return (false); 179270002Sjhb } 180270002Sjhb ddptr = buf + (len - space); 181270002Sjhb } 182270002Sjhb 183270002Sjhb n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 184270002Sjhb if (n > 0) { 185270002Sjhb ddptr += n; 186270002Sjhb space -= n; 187270002Sjhb } 188270002Sjhb if (n < 0) { 189270002Sjhb saved_errno = errno; 190270002Sjhb _close(fd2); 191270002Sjhb errno = saved_errno; 192270002Sjhb return (false); 193270002Sjhb } 194270002Sjhb } while (n > 0); 195270002Sjhb _close(fd2); 196270002Sjhb 197270002Sjhb ddeptr = ddptr; 198270002Sjhb 199270002Sjhb /* 200270002Sjhb * There is now a buffer full of (possibly) duplicate 201270002Sjhb * names. 202270002Sjhb */ 203270002Sjhb dirp->dd_buf = buf; 204270002Sjhb 205270002Sjhb /* 206270002Sjhb * Go round this loop twice... 207270002Sjhb * 208270002Sjhb * Scan through the buffer, counting entries. 209270002Sjhb * On the second pass, save pointers to each one. 210270002Sjhb * Then sort the pointers and remove duplicate names. 211270002Sjhb */ 212270002Sjhb for (dpv = 0;;) { 213270002Sjhb n = 0; 214270002Sjhb ddptr = buf; 215270002Sjhb while (ddptr < ddeptr) { 216270002Sjhb struct dirent *dp; 217270002Sjhb 218270002Sjhb dp = (struct dirent *) ddptr; 219270002Sjhb if ((long)dp & 03L) 220270002Sjhb break; 221270002Sjhb if ((dp->d_reclen <= 0) || 222270002Sjhb (dp->d_reclen > (ddeptr + 1 - ddptr))) 223270002Sjhb break; 224270002Sjhb ddptr += dp->d_reclen; 225270002Sjhb if (dp->d_fileno) { 226270002Sjhb if (dpv) 227270002Sjhb dpv[n] = dp; 228270002Sjhb n++; 229270002Sjhb } 230270002Sjhb } 231270002Sjhb 232270002Sjhb if (dpv) { 233270002Sjhb struct dirent *xp; 234270002Sjhb 235270002Sjhb /* 236270002Sjhb * This sort must be stable. 237270002Sjhb */ 238270002Sjhb mergesort(dpv, n, sizeof(*dpv), opendir_compar); 239270002Sjhb 240270002Sjhb dpv[n] = NULL; 241270002Sjhb xp = NULL; 242270002Sjhb 243270002Sjhb /* 244270002Sjhb * Scan through the buffer in sort order, 245270002Sjhb * zapping the inode number of any 246270002Sjhb * duplicate names. 247270002Sjhb */ 248270002Sjhb for (n = 0; dpv[n]; n++) { 249270002Sjhb struct dirent *dp = dpv[n]; 250270002Sjhb 251270002Sjhb if ((xp == NULL) || 252270002Sjhb strcmp(dp->d_name, xp->d_name)) { 253270002Sjhb xp = dp; 254270002Sjhb } else { 255270002Sjhb dp->d_fileno = 0; 256270002Sjhb } 257270002Sjhb if (dp->d_type == DT_WHT && 258270002Sjhb (dirp->dd_flags & DTF_HIDEW)) 259270002Sjhb dp->d_fileno = 0; 260270002Sjhb } 261270002Sjhb 262270002Sjhb free(dpv); 263270002Sjhb break; 264270002Sjhb } else { 265270002Sjhb dpv = malloc((n+1) * sizeof(struct dirent *)); 266270002Sjhb if (dpv == NULL) 267270002Sjhb break; 268270002Sjhb } 269270002Sjhb } 270270002Sjhb 271270002Sjhb dirp->dd_len = len; 272270002Sjhb dirp->dd_size = ddptr - dirp->dd_buf; 273270002Sjhb return (true); 274270002Sjhb} 275270002Sjhb 276270002Sjhb 277270002Sjhb/* 278178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 279178256Sdelphij */ 280178256Sdelphijstatic DIR * 281270002Sjhb__opendir_common(int fd, int flags, bool use_current_pos) 282178256Sdelphij{ 283178256Sdelphij DIR *dirp; 284178256Sdelphij int incr; 285178256Sdelphij int saved_errno; 286178256Sdelphij int unionstack; 287178256Sdelphij 288232385Sru if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 289232385Sru return (NULL); 29023668Speter 291270002Sjhb dirp->dd_buf = NULL; 292270002Sjhb dirp->dd_fd = fd; 293270002Sjhb dirp->dd_flags = flags; 294270002Sjhb dirp->dd_loc = 0; 295270002Sjhb dirp->dd_lock = NULL; 296133723Sstefanf dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 29769841Sdeischen LIST_INIT(&dirp->dd_td->td_locq); 29869841Sdeischen dirp->dd_td->td_loccnt = 0; 29969841Sdeischen 3001573Srgrimes /* 30123768Sbde * Use the system page size if that is a multiple of DIRBLKSIZ. 30223668Speter * Hopefully this can be a big win someday by allowing page 30371579Sdeischen * trades to user space to be done by _getdirentries(). 3041573Srgrimes */ 30523668Speter incr = getpagesize(); 30623668Speter if ((incr % DIRBLKSIZ) != 0) 30723668Speter incr = DIRBLKSIZ; 30823668Speter 30923668Speter /* 31023668Speter * Determine whether this directory is the top of a union stack. 31123668Speter */ 31223668Speter if (flags & DTF_NODUP) { 31323668Speter struct statfs sfb; 31423668Speter 31571579Sdeischen if (_fstatfs(fd, &sfb) < 0) 31623768Sbde goto fail; 317115047Stjr unionstack = !strcmp(sfb.f_fstypename, "unionfs") 31875860Sjoerg || (sfb.f_flags & MNT_UNION); 31923668Speter } else { 32023668Speter unionstack = 0; 32123668Speter } 32223668Speter 32323668Speter if (unionstack) { 324270002Sjhb if (!_filldir(dirp, use_current_pos)) 325270002Sjhb goto fail; 326270002Sjhb dirp->dd_flags |= __DTF_READALL; 32723668Speter } else { 32823668Speter dirp->dd_len = incr; 32923668Speter dirp->dd_buf = malloc(dirp->dd_len); 33023768Sbde if (dirp->dd_buf == NULL) 33123768Sbde goto fail; 332270002Sjhb if (use_current_pos) { 333270002Sjhb /* 334270002Sjhb * Read the first batch of directory entries 335270002Sjhb * to prime dd_seek. This also checks if the 336270002Sjhb * fd passed to fdopendir() is a directory. 337270002Sjhb */ 338270002Sjhb dirp->dd_size = _getdirentries(dirp->dd_fd, 339270002Sjhb dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 340270002Sjhb if (dirp->dd_size < 0) { 341270002Sjhb if (errno == EINVAL) 342270002Sjhb errno = ENOTDIR; 343270002Sjhb goto fail; 344270002Sjhb } 345270002Sjhb dirp->dd_flags |= __DTF_SKIPREAD; 346270002Sjhb } else { 347270002Sjhb dirp->dd_size = 0; 348270002Sjhb dirp->dd_seek = 0; 349270002Sjhb } 35023668Speter } 35123668Speter 35223668Speter return (dirp); 35323768Sbde 35423768Sbdefail: 35523768Sbde saved_errno = errno; 356270002Sjhb free(dirp->dd_buf); 35723768Sbde free(dirp); 35823768Sbde errno = saved_errno; 35923768Sbde return (NULL); 3601573Srgrimes} 361