opendir.c revision 69656
1212793Sdim/* 2212793Sdim * Copyright (c) 1983, 1993 3212793Sdim * The Regents of the University of California. All rights reserved. 4212793Sdim * 5212793Sdim * Redistribution and use in source and binary forms, with or without 6212793Sdim * modification, are permitted provided that the following conditions 7212793Sdim * are met: 8212793Sdim * 1. Redistributions of source code must retain the above copyright 9212793Sdim * notice, this list of conditions and the following disclaimer. 10212793Sdim * 2. Redistributions in binary form must reproduce the above copyright 11212793Sdim * notice, this list of conditions and the following disclaimer in the 12212793Sdim * documentation and/or other materials provided with the distribution. 13212793Sdim * 3. All advertising materials mentioning features or use of this software 14212793Sdim * must display the following acknowledgement: 15212793Sdim * This product includes software developed by the University of 16212793Sdim * California, Berkeley and its contributors. 17212793Sdim * 4. Neither the name of the University nor the names of its contributors 18212793Sdim * may be used to endorse or promote products derived from this software 19212793Sdim * without specific prior written permission. 20212793Sdim * 21212793Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22212793Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23212793Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24212793Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25212793Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26212793Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27212793Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28212793Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29212793Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30212793Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31212793Sdim * SUCH DAMAGE. 32212793Sdim * 33221345Sdim * $FreeBSD: head/lib/libc/gen/opendir.c 69656 2000-12-06 03:15:49Z deischen $ 34239462Sdim */ 35239462Sdim 36239462Sdim#if defined(LIBC_SCCS) && !defined(lint) 37239462Sdimstatic char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 38239462Sdim#endif /* LIBC_SCCS and not lint */ 39239462Sdim 40239462Sdim#include <sys/param.h> 41239462Sdim#include <sys/mount.h> 42239462Sdim#include <sys/stat.h> 43249423Sdim 44249423Sdim#include <dirent.h> 45249423Sdim#include <errno.h> 46249423Sdim#include <fcntl.h> 47249423Sdim#include <stdlib.h> 48261991Sdim#include <unistd.h> 49280031Sdim 50261991Sdim/* 51261991Sdim * Open a directory. 52261991Sdim */ 53261991SdimDIR * 54261991Sdimopendir(name) 55261991Sdim const char *name; 56261991Sdim{ 57261991Sdim 58261991Sdim return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 59261991Sdim} 60261991Sdim 61261991SdimDIR * 62261991Sdim__opendir2(name, flags) 63261991Sdim const char *name; 64261991Sdim int flags; 65261991Sdim{ 66261991Sdim DIR *dirp; 67212793Sdim int fd; 68212793Sdim int incr; 69212793Sdim int saved_errno; 70249423Sdim int unionstack; 71249423Sdim struct stat statb; 72249423Sdim 73249423Sdim /* 74212793Sdim * stat() before open() because opening of special files may be 75212793Sdim * harmful. fstat() after open because the file may have changed. 76212793Sdim */ 77249423Sdim if (stat(name, &statb) != 0) 78249423Sdim return (NULL); 79212793Sdim if (!S_ISDIR(statb.st_mode)) { 80212793Sdim errno = ENOTDIR; 81280031Sdim return (NULL); 82280031Sdim } 83212793Sdim if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) 84212793Sdim return (NULL); 85276479Sdim dirp = NULL; 86276479Sdim if (fstat(fd, &statb) != 0) 87212793Sdim goto fail; 88212793Sdim if (!S_ISDIR(statb.st_mode)) { 89212793Sdim errno = ENOTDIR; 90212793Sdim goto fail; 91212793Sdim } 92218893Sdim if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 93218893Sdim (dirp = malloc(sizeof(DIR))) == NULL) 94218893Sdim goto fail; 95218893Sdim 96276479Sdim /* 97280031Sdim * Use the system page size if that is a multiple of DIRBLKSIZ. 98276479Sdim * Hopefully this can be a big win someday by allowing page 99276479Sdim * trades to user space to be done by getdirentries(). 100212793Sdim */ 101221345Sdim incr = getpagesize(); 102234353Sdim if ((incr % DIRBLKSIZ) != 0) 103239462Sdim incr = DIRBLKSIZ; 104239462Sdim 105280031Sdim /* 106280031Sdim * Determine whether this directory is the top of a union stack. 107212793Sdim */ 108212793Sdim if (flags & DTF_NODUP) { 109212793Sdim struct statfs sfb; 110212793Sdim 111280031Sdim if (fstatfs(fd, &sfb) < 0) 112212793Sdim goto fail; 113212793Sdim unionstack = !strcmp(sfb.f_fstypename, "union"); 114212793Sdim } else { 115212793Sdim unionstack = 0; 116212793Sdim } 117218893Sdim 118218893Sdim if (unionstack) { 119218893Sdim int len = 0; 120212793Sdim int space = 0; 121276479Sdim char *buf = 0; 122212793Sdim char *ddptr = 0; 123276479Sdim char *ddeptr; 124212793Sdim int n; 125212793Sdim struct dirent **dpv; 126212793Sdim 127212793Sdim /* 128212793Sdim * The strategy here is to read all the directory 129212793Sdim * entries into a buffer, sort the buffer, and 130212793Sdim * remove duplicate entries by setting the inode 131212793Sdim * number to zero. 132212793Sdim */ 133239462Sdim 134239462Sdim do { 135280031Sdim /* 136280031Sdim * Always make at least DIRBLKSIZ bytes 137280031Sdim * available to getdirentries 138280031Sdim */ 139261991Sdim if (space < DIRBLKSIZ) { 140280031Sdim space += incr; 141280031Sdim len += incr; 142280031Sdim buf = reallocf(buf, len); 143280031Sdim if (buf == NULL) 144218893Sdim goto fail; 145218893Sdim ddptr = buf + (len - space); 146218893Sdim } 147239462Sdim 148218893Sdim n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 149218893Sdim if (n > 0) { 150276479Sdim ddptr += n; 151276479Sdim space -= n; 152280031Sdim } 153280031Sdim } while (n > 0); 154280031Sdim 155280031Sdim ddeptr = ddptr; 156280031Sdim flags |= __DTF_READALL; 157280031Sdim 158280031Sdim /* 159280031Sdim * Re-open the directory. 160280031Sdim * This has the effect of rewinding back to the 161280031Sdim * top of the union stack and is needed by 162280031Sdim * programs which plan to fchdir to a descriptor 163280031Sdim * which has also been read -- see fts.c. 164280031Sdim */ 165280031Sdim if (flags & DTF_REWIND) { 166280031Sdim (void)_close(fd); 167280031Sdim if ((fd = _open(name, O_RDONLY)) == -1) { 168280031Sdim saved_errno = errno; 169280031Sdim free(buf); 170280031Sdim free(dirp); 171212793Sdim errno = saved_errno; 172276479Sdim return (NULL); 173276479Sdim } 174276479Sdim } 175276479Sdim 176276479Sdim /* 177276479Sdim * There is now a buffer full of (possibly) duplicate 178276479Sdim * names. 179276479Sdim */ 180276479Sdim dirp->dd_buf = buf; 181276479Sdim 182276479Sdim /* 183276479Sdim * Go round this loop twice... 184276479Sdim * 185276479Sdim * Scan through the buffer, counting entries. 186276479Sdim * On the second pass, save pointers to each one. 187276479Sdim * Then sort the pointers and remove duplicate names. 188276479Sdim */ 189276479Sdim for (dpv = 0;;) { 190276479Sdim n = 0; 191276479Sdim ddptr = buf; 192276479Sdim while (ddptr < ddeptr) { 193276479Sdim struct dirent *dp; 194276479Sdim 195276479Sdim dp = (struct dirent *) ddptr; 196276479Sdim if ((long)dp & 03L) 197276479Sdim break; 198276479Sdim if ((dp->d_reclen <= 0) || 199276479Sdim (dp->d_reclen > (ddeptr + 1 - ddptr))) 200276479Sdim break; 201276479Sdim ddptr += dp->d_reclen; 202276479Sdim if (dp->d_fileno) { 203276479Sdim if (dpv) 204276479Sdim dpv[n] = dp; 205276479Sdim n++; 206276479Sdim } 207280031Sdim } 208280031Sdim 209280031Sdim if (dpv) { 210276479Sdim struct dirent *xp; 211280031Sdim 212276479Sdim /* 213276479Sdim * This sort must be stable. 214276479Sdim */ 215280031Sdim mergesort(dpv, n, sizeof(*dpv), alphasort); 216276479Sdim 217280031Sdim dpv[n] = NULL; 218276479Sdim xp = NULL; 219280031Sdim 220276479Sdim /* 221276479Sdim * Scan through the buffer in sort order, 222280031Sdim * zapping the inode number of any 223276479Sdim * duplicate names. 224276479Sdim */ 225280031Sdim for (n = 0; dpv[n]; n++) { 226276479Sdim struct dirent *dp = dpv[n]; 227276479Sdim 228280031Sdim if ((xp == NULL) || 229276479Sdim strcmp(dp->d_name, xp->d_name)) { 230276479Sdim xp = dp; 231280031Sdim } else { 232276479Sdim dp->d_fileno = 0; 233276479Sdim } 234280031Sdim if (dp->d_type == DT_WHT && 235276479Sdim (flags & DTF_HIDEW)) 236280031Sdim dp->d_fileno = 0; 237280031Sdim } 238276479Sdim 239276479Sdim free(dpv); 240276479Sdim break; 241280031Sdim } else { 242280031Sdim dpv = malloc((n+1) * sizeof(struct dirent *)); 243280031Sdim if (dpv == NULL) 244280031Sdim break; 245280031Sdim } 246280031Sdim } 247280031Sdim 248280031Sdim dirp->dd_len = len; 249280031Sdim dirp->dd_size = ddptr - dirp->dd_buf; 250280031Sdim } else { 251280031Sdim dirp->dd_len = incr; 252280031Sdim dirp->dd_buf = malloc(dirp->dd_len); 253280031Sdim if (dirp->dd_buf == NULL) 254280031Sdim goto fail; 255280031Sdim dirp->dd_seek = 0; 256280031Sdim flags &= ~DTF_REWIND; 257280031Sdim } 258280031Sdim 259280031Sdim dirp->dd_loc = 0; 260280031Sdim dirp->dd_fd = fd; 261280031Sdim dirp->dd_flags = flags; 262280031Sdim dirp->dd_loccnt = 0; 263280031Sdim LIST_INIT(&dirp->dd_locq); 264280031Sdim 265276479Sdim /* 266280031Sdim * Set up seek point for rewinddir. 267276479Sdim */ 268280031Sdim dirp->dd_rewind = telldir(dirp); 269276479Sdim 270280031Sdim return (dirp); 271280031Sdim 272280031Sdimfail: 273276479Sdim saved_errno = errno; 274276479Sdim free(dirp); 275276479Sdim (void)_close(fd); 276276479Sdim errno = saved_errno; 277276479Sdim return (NULL); 278280031Sdim} 279276479Sdim