178977Sroam/* $NetBSD: initdir.c,v 1.1 2010/09/26 02:26:59 yamt Exp $ */ 278977Sroam 378977Sroam/* 478977Sroam * Copyright (c) 1983, 1993 578977Sroam * The Regents of the University of California. All rights reserved. 678977Sroam * 778977Sroam * Redistribution and use in source and binary forms, with or without 878977Sroam * modification, are permitted provided that the following conditions 978977Sroam * are met: 1078977Sroam * 1. Redistributions of source code must retain the above copyright 1178977Sroam * notice, this list of conditions and the following disclaimer. 1278977Sroam * 2. Redistributions in binary form must reproduce the above copyright 1378977Sroam * notice, this list of conditions and the following disclaimer in the 1478977Sroam * documentation and/or other materials provided with the distribution. 1578977Sroam * 3. Neither the name of the University nor the names of its contributors 1678977Sroam * may be used to endorse or promote products derived from this software 1778977Sroam * without specific prior written permission. 1878977Sroam * 1978977Sroam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2078977Sroam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2178977Sroam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2278977Sroam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2378977Sroam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2478977Sroam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2578977Sroam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2678977Sroam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27114589Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28114589Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2978977Sroam * SUCH DAMAGE. 3078977Sroam */ 3178977Sroam 3278977Sroam#include <sys/cdefs.h> 3378977Sroam#if defined(LIBC_SCCS) && !defined(lint) 3478977Sroam__RCSID("$NetBSD: initdir.c,v 1.1 2010/09/26 02:26:59 yamt Exp $"); 3578977Sroam#endif /* LIBC_SCCS and not lint */ 3678977Sroam 3778977Sroam#include "namespace.h" 3878977Sroam#include "reentrant.h" 3978977Sroam#include "extern.h" 4078977Sroam 4178977Sroam#include <sys/param.h> 4278977Sroam 4378977Sroam#include <assert.h> 4478977Sroam#include <dirent.h> 4578977Sroam#include <errno.h> 4678977Sroam#include <fcntl.h> 4778977Sroam#include <stdlib.h> 4878977Sroam#include <string.h> 4978977Sroam#include <unistd.h> 5078977Sroam 5178977Sroam#include "dirent_private.h" 5278977Sroam 5378977Sroam#define MAXITERATIONS 100 5478977Sroam 5578977Sroamint 5678977Sroam_initdir(DIR *dirp, int fd, const char *name) 5778977Sroam{ 5878977Sroam int flags = dirp->dd_flags; 5978977Sroam int pagesz; 6078977Sroam int incr; 6178977Sroam 6278977Sroam /* 6378977Sroam * If the machine's page size is an exact multiple of DIRBLKSIZ, 6478977Sroam * use a buffer that is cluster boundary aligned. 6578977Sroam * Hopefully this can be a big win someday by allowing page trades 6678977Sroam * to user space to be done by getdents() 6778977Sroam */ 6878977Sroam if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) 6978977Sroam incr = pagesz; 7078977Sroam else 7178977Sroam incr = DIRBLKSIZ; 7278977Sroam 7378977Sroam if ((flags & DTF_REWIND) && name == NULL) { 7478977Sroam return EINVAL; 7578977Sroam } 7678977Sroam if ((flags & __DTF_READALL) != 0) { 7778977Sroam size_t len; 7878977Sroam size_t space; 7978977Sroam char *buf, *nbuf; 8078977Sroam char *ddptr; 8178977Sroam char *ddeptr; 8278977Sroam int n; 8378977Sroam struct dirent **dpv; 8478977Sroam int i; 8578977Sroam 8678977Sroam /* 8778977Sroam * The strategy here for directories on top of a union stack 8878977Sroam * is to read all the directory entries into a buffer, sort 8978977Sroam * the buffer, and remove duplicate entries by setting the 9078977Sroam * inode number to zero. 9178977Sroam * 9278977Sroam * For directories on an NFS mounted filesystem, we try 9378977Sroam * to get a consistent snapshot by trying until we have 9478977Sroam * successfully read all of the directory without errors 9578977Sroam * (i.e. 'bad cookie' errors from the server because 9678977Sroam * the directory was modified). These errors should not 9778977Sroam * happen often, but need to be dealt with. 9878977Sroam */ 9978977Sroam i = 0; 10078977Sroamretry: 10178977Sroam len = 0; 10278977Sroam space = 0; 10378977Sroam buf = 0; 10478977Sroam ddptr = 0; 10578977Sroam 10678977Sroam do { 10778977Sroam /* 10878977Sroam * Always make at least DIRBLKSIZ bytes 10978977Sroam * available to getdents 11078977Sroam */ 11178977Sroam if (space < DIRBLKSIZ) { 11278977Sroam space += incr; 11378977Sroam len += incr; 11478977Sroam nbuf = realloc(buf, len); 11578977Sroam if (nbuf == NULL) { 116126643Smarkm dirp->dd_buf = buf; 11778977Sroam return errno; 11878977Sroam } 11978977Sroam buf = nbuf; 12079002Sroam ddptr = buf + (len - space); 12179002Sroam } 12278977Sroam 123126643Smarkm dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR); 12478977Sroam n = getdents(fd, ddptr, space); 12578977Sroam /* 12678977Sroam * For NFS: EINVAL means a bad cookie error 12778977Sroam * from the server. Keep trying to get a 12878977Sroam * consistent view, in this case this means 12978977Sroam * starting all over again. 13078977Sroam */ 13178977Sroam if (n == -1 && errno == EINVAL && 13278977Sroam (flags & __DTF_RETRY_ON_BADCOOKIE) != 0) { 13378977Sroam free(buf); 13478977Sroam lseek(fd, (off_t)0, SEEK_SET); 13578977Sroam if (++i > MAXITERATIONS) 13678977Sroam return EINVAL; 13778977Sroam goto retry; 13878977Sroam } 13978977Sroam if (n > 0) { 14078977Sroam ddptr += n; 14178977Sroam space -= n; 14278977Sroam } 143113936Sjohan } while (n > 0); 14478977Sroam 14578977Sroam ddeptr = ddptr; 14678977Sroam 14778977Sroam /* 14878977Sroam * Re-open the directory. 14978977Sroam * This has the effect of rewinding back to the 15078977Sroam * top of the union stack and is needed by 15178977Sroam * programs which plan to fchdir to a descriptor 15278977Sroam * which has also been read -- see fts.c. 15378977Sroam */ 15478977Sroam if (flags & DTF_REWIND) { 15578977Sroam (void) close(fd); 15678977Sroam if ((fd = open(name, O_RDONLY | O_CLOEXEC)) == -1) { 15778977Sroam dirp->dd_buf = buf; 15878977Sroam return errno; 15978977Sroam } 16078977Sroam } 16178977Sroam 16278977Sroam /* 16378977Sroam * There is now a buffer full of (possibly) duplicate 16478977Sroam * names. 16578977Sroam */ 16678977Sroam dirp->dd_buf = buf; 16778977Sroam 16878977Sroam /* 16978977Sroam * Go round this loop twice... 17078977Sroam * 17178977Sroam * Scan through the buffer, counting entries. 17278977Sroam * On the second pass, save pointers to each one. 17378977Sroam * Then sort the pointers and remove duplicate names. 17478977Sroam */ 17578977Sroam if ((flags & DTF_NODUP) != 0) { 17678977Sroam for (dpv = 0;;) { 17778977Sroam for (n = 0, ddptr = buf; ddptr < ddeptr;) { 17878977Sroam struct dirent *dp; 17978977Sroam 18078977Sroam dp = (struct dirent *)(void *)ddptr; 18178977Sroam if ((long)dp & _DIRENT_ALIGN(dp)) 18278977Sroam break; 18378977Sroam /* 18478977Sroam * d_reclen is unsigned, 18578977Sroam * so no need to compare <= 0 18678977Sroam */ 18778977Sroam if (dp->d_reclen > (ddeptr + 1 - ddptr)) 18878977Sroam break; 18978977Sroam ddptr += dp->d_reclen; 19078977Sroam if (dp->d_fileno) { 19178977Sroam if (dpv) 19278977Sroam dpv[n] = dp; 19378977Sroam n++; 19478977Sroam } 19578977Sroam } 19678977Sroam 19778977Sroam if (dpv) { 19878977Sroam struct dirent *xp; 19978977Sroam 20078977Sroam /* 20178977Sroam * This sort must be stable. 20278977Sroam */ 20378977Sroam mergesort(dpv, (size_t)n, sizeof(*dpv), 20478977Sroam alphasort); 20578977Sroam 20678977Sroam dpv[n] = NULL; 20778977Sroam xp = NULL; 20878977Sroam 20978977Sroam /* 21078977Sroam * Scan through the buffer in sort 21178977Sroam * order, zapping the inode number 21278977Sroam * of any duplicate names. 21378977Sroam */ 21478977Sroam for (n = 0; dpv[n]; n++) { 21578977Sroam struct dirent *dp = dpv[n]; 21678977Sroam 21778977Sroam if ((xp == NULL) || 21878977Sroam strcmp(dp->d_name, 21978977Sroam xp->d_name)) 22078977Sroam xp = dp; 22178977Sroam else 22278977Sroam dp->d_fileno = 0; 22378977Sroam if (dp->d_type == DT_WHT && 22478977Sroam (flags & DTF_HIDEW)) 22578977Sroam dp->d_fileno = 0; 22678977Sroam } 22778977Sroam 22878977Sroam free(dpv); 22978977Sroam break; 23078977Sroam } else { 23178977Sroam dpv = malloc((n + 1) * 23278977Sroam sizeof(struct dirent *)); 23378977Sroam if (dpv == NULL) 23478977Sroam break; 23578977Sroam } 23678977Sroam } 23778977Sroam } 23878977Sroam 23978977Sroam dirp->dd_len = len; 24078977Sroam dirp->dd_size = ddptr - dirp->dd_buf; 24178977Sroam } else { 24278977Sroam dirp->dd_len = incr; 24378977Sroam dirp->dd_buf = malloc((size_t)dirp->dd_len); 24478977Sroam if (dirp->dd_buf == NULL) 24578977Sroam return errno; 24678977Sroam dirp->dd_seek = 0; 24778977Sroam flags &= ~DTF_REWIND; 24878977Sroam } 24978977Sroam dirp->dd_loc = 0; 25078977Sroam dirp->dd_fd = fd; 25178977Sroam dirp->dd_flags = flags; 25278977Sroam /* 25378977Sroam * Set up seek point for rewinddir. 25478977Sroam */ 25578977Sroam (void)_telldir_unlocked(dirp); 25678977Sroam return 0; 25778977Sroam} 25878977Sroam 25978977Sroamvoid 26078977Sroam_finidir(DIR *dirp) 26178977Sroam{ 26278977Sroam struct dirpos *poslist; 26378977Sroam 26478977Sroam free(dirp->dd_buf); 26578977Sroam 26678977Sroam /* free seekdir/telldir storage */ 26778977Sroam for (poslist = dirp->dd_internal; poslist; ) { 26878977Sroam struct dirpos *nextpos = poslist->dp_next; 26978977Sroam free(poslist); 27078977Sroam poslist = nextpos; 27178977Sroam } 27278977Sroam dirp->dd_internal = NULL; 273152169Sru} 27478977Sroam