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