Module Name:    src
Committed By:   yamt
Date:           Sun Sep 26 02:27:00 UTC 2010

Modified Files:
        src/include: dirent.h
        src/lib/libc/gen: Makefile.inc closedir.c dirent_private.h opendir.c
            rewinddir.c
Added Files:
        src/lib/libc/gen: initdir.c

Log Message:
fix rewinddir on nfs.  fix PR/42879 (and probably PR/40229.)


To generate a diff of this commit:
cvs rdiff -u -r1.33 -r1.34 src/include/dirent.h
cvs rdiff -u -r1.171 -r1.172 src/lib/libc/gen/Makefile.inc
cvs rdiff -u -r1.15 -r1.16 src/lib/libc/gen/closedir.c
cvs rdiff -u -r1.3 -r1.4 src/lib/libc/gen/dirent_private.h
cvs rdiff -u -r0 -r1.1 src/lib/libc/gen/initdir.c
cvs rdiff -u -r1.36 -r1.37 src/lib/libc/gen/opendir.c
cvs rdiff -u -r1.12 -r1.13 src/lib/libc/gen/rewinddir.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/include/dirent.h
diff -u src/include/dirent.h:1.33 src/include/dirent.h:1.34
--- src/include/dirent.h:1.33	Tue Feb 24 18:41:40 2009
+++ src/include/dirent.h	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: dirent.h,v 1.33 2009/02/24 18:41:40 christos Exp $	*/
+/*	$NetBSD: dirent.h,v 1.34 2010/09/26 02:26:59 yamt Exp $	*/
 
 /*-
  * Copyright (c) 1989, 1993
@@ -74,6 +74,8 @@
 #define DTF_NODUP	0x0002	/* don't return duplicate names */
 #define DTF_REWIND	0x0004	/* rewind after reading union stack */
 #define __DTF_READALL	0x0008	/* everything has been read */
+#define __DTF_RETRY_ON_BADCOOKIE 0x0001	/* retry on EINVAL
+					(only valid with __DTF_READALL) */
 
 #include <sys/null.h>
 

Index: src/lib/libc/gen/Makefile.inc
diff -u src/lib/libc/gen/Makefile.inc:1.171 src/lib/libc/gen/Makefile.inc:1.172
--- src/lib/libc/gen/Makefile.inc:1.171	Fri Aug 27 08:38:41 2010
+++ src/lib/libc/gen/Makefile.inc	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile.inc,v 1.171 2010/08/27 08:38:41 christos Exp $
+#	$NetBSD: Makefile.inc,v 1.172 2010/09/26 02:26:59 yamt Exp $
 #	from: @(#)Makefile.inc	8.6 (Berkeley) 5/4/95
 
 # gen sources
@@ -15,7 +15,7 @@
 	getloadavg.c getlogin.c getmntinfo.c \
 	getnetgrent.c getpagesize.c \
 	getpass.c getprogname.c getpwent.c getttyent.c \
-	getusershell.c glob.c humanize_number.c initgroups.c \
+	getusershell.c glob.c humanize_number.c initdir.c initgroups.c \
 	isascii.c isatty.c isctype.c lockf.c nftw.c \
 	nice.c nlist.c nlist_aout.c \
 	nlist_coff.c nlist_ecoff.c nlist_elf32.c nlist_elf64.c opendir.c \

Index: src/lib/libc/gen/closedir.c
diff -u src/lib/libc/gen/closedir.c:1.15 src/lib/libc/gen/closedir.c:1.16
--- src/lib/libc/gen/closedir.c:1.15	Wed May 17 20:36:50 2006
+++ src/lib/libc/gen/closedir.c	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: closedir.c,v 1.15 2006/05/17 20:36:50 christos Exp $	*/
+/*	$NetBSD: closedir.c,v 1.16 2010/09/26 02:26:59 yamt Exp $	*/
 
 /*
  * Copyright (c) 1983, 1993
@@ -34,7 +34,7 @@
 #if 0
 static char sccsid[] = "@(#)closedir.c	8.1 (Berkeley) 6/10/93";
 #else
-__RCSID("$NetBSD: closedir.c,v 1.15 2006/05/17 20:36:50 christos Exp $");
+__RCSID("$NetBSD: closedir.c,v 1.16 2010/09/26 02:26:59 yamt Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -62,7 +62,6 @@
 closedir(DIR *dirp)
 {
 	int fd;
-	struct dirpos *poslist;
 
 	_DIAGASSERT(dirp != NULL);
 
@@ -72,15 +71,7 @@
 #endif
 	fd = dirp->dd_fd;
 	dirp->dd_fd = -1;
-	dirp->dd_loc = 0;
-	free(dirp->dd_buf);
-
-	/* free seekdir/telldir storage */
-	for (poslist = dirp->dd_internal; poslist; ) {
-		struct dirpos *nextpos = poslist->dp_next;
-		free(poslist);
-		poslist = nextpos;
-	}
+	_finidir(dirp);
 
 #ifdef _REENTRANT
 	if (__isthreaded) {

Index: src/lib/libc/gen/dirent_private.h
diff -u src/lib/libc/gen/dirent_private.h:1.3 src/lib/libc/gen/dirent_private.h:1.4
--- src/lib/libc/gen/dirent_private.h:1.3	Thu Sep 16 02:38:50 2010
+++ src/lib/libc/gen/dirent_private.h	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: dirent_private.h,v 1.3 2010/09/16 02:38:50 yamt Exp $	*/
+/*	$NetBSD: dirent_private.h,v 1.4 2010/09/26 02:26:59 yamt Exp $	*/
 
 /*
  * One struct _dirpos is malloced to describe the current directory
@@ -15,6 +15,8 @@
 struct _dirdesc;
 void _seekdir_unlocked(struct _dirdesc *, long);
 long _telldir_unlocked(struct _dirdesc *);
+int _initdir(DIR *, int, const char *);
+void _finidir(DIR *);
 #ifndef __LIBC12_SOURCE__
 struct dirent;
 struct dirent *_readdir_unlocked(struct _dirdesc *, int)

Index: src/lib/libc/gen/opendir.c
diff -u src/lib/libc/gen/opendir.c:1.36 src/lib/libc/gen/opendir.c:1.37
--- src/lib/libc/gen/opendir.c:1.36	Thu Sep 16 02:38:50 2010
+++ src/lib/libc/gen/opendir.c	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: opendir.c,v 1.36 2010/09/16 02:38:50 yamt Exp $	*/
+/*	$NetBSD: opendir.c,v 1.37 2010/09/26 02:26:59 yamt Exp $	*/
 
 /*
  * Copyright (c) 1983, 1993
@@ -34,7 +34,7 @@
 #if 0
 static char sccsid[] = "@(#)opendir.c	8.7 (Berkeley) 12/10/94";
 #else
-__RCSID("$NetBSD: opendir.c,v 1.36 2010/09/16 02:38:50 yamt Exp $");
+__RCSID("$NetBSD: opendir.c,v 1.37 2010/09/26 02:26:59 yamt Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -56,8 +56,6 @@
 
 #include "dirent_private.h"
 
-#define	MAXITERATIONS	100
-
 static DIR	*__opendir_common(int, const char *, int);
 
 __weak_alias(fdopendir,_fdopendir)
@@ -99,10 +97,8 @@
 	DIR *dirp = NULL;
 	int serrno;
 	struct stat sb;
-	int pagesz;
-	int incr;
-	int unionstack, nfsdir;
 	struct statvfs sfb;
+	int error;
 
 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 		goto error;
@@ -113,232 +109,54 @@
 	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL)
 		goto error;
 	dirp->dd_buf = NULL;
+	dirp->dd_internal = NULL;
+#ifdef _REENTRANT
+	if (__isthreaded) {
+		if ((dirp->dd_lock = malloc(sizeof(mutex_t))) == NULL)
+			goto error;
+		mutex_init((mutex_t *)dirp->dd_lock, NULL);
+	}
+#endif
 
 	/*
-	 * If the machine's page size is an exact multiple of DIRBLKSIZ,
-	 * use a buffer that is cluster boundary aligned.
-	 * Hopefully this can be a big win someday by allowing page trades
-	 * to user space to be done by getdents()
-	 */
-	if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0)
-		incr = pagesz;
-	else
-		incr = DIRBLKSIZ;
-
-	/*
-	 * Determine whether this directory is the top of a union stack.
+	 * Tweak flags for the underlying filesystem.
 	 */
 
 	if (fstatvfs1(fd, &sfb, ST_NOWAIT) < 0)
 		goto error;
-
-	if (flags & DTF_NODUP)
-		unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION,
-		    sizeof(sfb.f_fstypename))) || (sfb.f_flag & MNT_UNION);
-	else
-		unionstack = 0;
-
-	nfsdir = !(strncmp(sfb.f_fstypename, MOUNT_NFS,
-	    sizeof(sfb.f_fstypename)));
-
-	if (unionstack || nfsdir) {
-		size_t len;
-		size_t space;
-		char *buf, *nbuf;
-		char *ddptr;
-		char *ddeptr;
-		int n;
-		struct dirent **dpv;
-		int i;
-
-		/*
-		 * The strategy here for directories on top of a union stack
-		 * is to read all the directory entries into a buffer, sort
-		 * the buffer, and remove duplicate entries by setting the
-		 * inode number to zero.
-		 *
-		 * For directories on an NFS mounted filesystem, we try
-	 	 * to get a consistent snapshot by trying until we have
-		 * successfully read all of the directory without errors
-		 * (i.e. 'bad cookie' errors from the server because
-		 * the directory was modified). These errors should not
-		 * happen often, but need to be dealt with.
-		 */
-		i = 0;
-retry:
-		len = 0;
-		space = 0;
-		buf = 0;
-		ddptr = 0;
-
-		do {
-			/*
-			 * Always make at least DIRBLKSIZ bytes
-			 * available to getdents
-			 */
-			if (space < DIRBLKSIZ) {
-				space += incr;
-				len += incr;
-				nbuf = realloc(buf, len);
-				if (nbuf == NULL) {
-					dirp->dd_buf = buf;
-					goto error;
-				}
-				buf = nbuf;
-				ddptr = buf + (len - space);
-			}
-
-			dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR);
-			n = getdents(fd, ddptr, space);
-			/*
-			 * For NFS: EINVAL means a bad cookie error
-			 * from the server. Keep trying to get a
-			 * consistent view, in this case this means
-			 * starting all over again.
-			 */
-			if (n == -1 && errno == EINVAL && nfsdir) {
-				free(buf);
-				lseek(fd, (off_t)0, SEEK_SET);
-				if (++i > MAXITERATIONS)
-					goto error;
-				goto retry;
-			}
-			if (n > 0) {
-				ddptr += n;
-				space -= n;
-			}
-		} while (n > 0);
-
-		ddeptr = ddptr;
-		flags |= __DTF_READALL;
-
-		/*
-		 * Re-open the directory.
-		 * This has the effect of rewinding back to the
-		 * top of the union stack and is needed by
-		 * programs which plan to fchdir to a descriptor
-		 * which has also been read -- see fts.c.
-		 */
-		if (flags & DTF_REWIND) {
-			(void) close(fd);
-			if ((fd = open(name, O_RDONLY)) == -1 ||
-			    fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
-				dirp->dd_buf = buf;
-				goto error;
-			}
+	if ((flags & DTF_NODUP) != 0) {
+		if (!strncmp(sfb.f_fstypename, MOUNT_UNION,
+		    sizeof(sfb.f_fstypename)) ||
+		    (sfb.f_flag & MNT_UNION) != 0) {
+			flags |= __DTF_READALL;
+		} else {
+			flags &= ~DTF_NODUP;
 		}
-
-		/*
-		 * There is now a buffer full of (possibly) duplicate
-		 * names.
-		 */
-		dirp->dd_buf = buf;
-
-		/*
-		 * Go round this loop twice...
-		 *
-		 * Scan through the buffer, counting entries.
-		 * On the second pass, save pointers to each one.
-		 * Then sort the pointers and remove duplicate names.
-		 */
-		if (!nfsdir) {
-			for (dpv = 0;;) {
-				for (n = 0, ddptr = buf; ddptr < ddeptr;) {
-					struct dirent *dp;
-
-					dp = (struct dirent *)(void *)ddptr;
-					if ((long)dp & _DIRENT_ALIGN(dp))
-						break;
-					/*
-					 * d_reclen is unsigned,
-					 * so no need to compare <= 0
-					 */
-					if (dp->d_reclen > (ddeptr + 1 - ddptr))
-						break;
-					ddptr += dp->d_reclen;
-					if (dp->d_fileno) {
-						if (dpv)
-							dpv[n] = dp;
-						n++;
-					}
-				}
-
-				if (dpv) {
-					struct dirent *xp;
-
-					/*
-					 * This sort must be stable.
-					 */
-					mergesort(dpv, (size_t)n, sizeof(*dpv),
-					    alphasort);
-
-					dpv[n] = NULL;
-					xp = NULL;
-
-					/*
-					 * Scan through the buffer in sort
-					 * order, zapping the inode number
-					 * of any duplicate names.
-					 */
-					for (n = 0; dpv[n]; n++) {
-						struct dirent *dp = dpv[n];
-
-						if ((xp == NULL) ||
-						    strcmp(dp->d_name,
-						      xp->d_name))
-							xp = dp;
-						else
-							dp->d_fileno = 0;
-						if (dp->d_type == DT_WHT &&
-						    (flags & DTF_HIDEW))
-							dp->d_fileno = 0;
-					}
-
-					free(dpv);
-					break;
-				} else {
-					dpv = malloc((n + 1) *
-					    sizeof(struct dirent *));
-					if (dpv == NULL)
-						break;
-				}
-			}
-		}
-
-		dirp->dd_len = len;
-		dirp->dd_size = ddptr - dirp->dd_buf;
-	} else {
-		dirp->dd_len = incr;
-		dirp->dd_buf = malloc((size_t)dirp->dd_len);
-		if (dirp->dd_buf == NULL)
-			goto error;
-		dirp->dd_seek = 0;
-		flags &= ~DTF_REWIND;
+	}
+	if (!strncmp(sfb.f_fstypename, MOUNT_NFS, sizeof(sfb.f_fstypename))) {
+		flags |= __DTF_READALL | __DTF_RETRY_ON_BADCOOKIE;
 	}
 
-	dirp->dd_loc = 0;
-	dirp->dd_fd = fd;
 	dirp->dd_flags = flags;
-
-	/*
-	 * Set up seek point for rewinddir.
-	 */
-#ifdef _REENTRANT
-	if (__isthreaded) {
-		if ((dirp->dd_lock = malloc(sizeof(mutex_t))) == NULL)
-			goto error;
-		mutex_init((mutex_t *)dirp->dd_lock, NULL);
+	error = _initdir(dirp, fd, name);
+	if (error) {
+		errno = error;
+		goto error;
 	}
-#endif
-	dirp->dd_internal = NULL;
-	(void)_telldir_unlocked(dirp);
+
 	return (dirp);
 error:
 	serrno = errno;
-	if (dirp && dirp->dd_buf)
+	if (dirp != NULL) {
+#ifdef _REENTRANT
+		if (__isthreaded) {
+			mutex_destroy((mutex_t *)dirp->dd_lock);
+			free(dirp->dd_lock);
+		}
+#endif
 		free(dirp->dd_buf);
-	if (dirp)
-		free(dirp);
+	}
+	free(dirp);
 	if (fd != -1)
 		(void)close(fd);
 	errno = serrno;

Index: src/lib/libc/gen/rewinddir.c
diff -u src/lib/libc/gen/rewinddir.c:1.12 src/lib/libc/gen/rewinddir.c:1.13
--- src/lib/libc/gen/rewinddir.c:1.12	Wed May 17 20:36:50 2006
+++ src/lib/libc/gen/rewinddir.c	Sun Sep 26 02:26:59 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: rewinddir.c,v 1.12 2006/05/17 20:36:50 christos Exp $	*/
+/*	$NetBSD: rewinddir.c,v 1.13 2010/09/26 02:26:59 yamt Exp $	*/
 
 /*-
  * Copyright (c) 1990, 1993
@@ -34,7 +34,7 @@
 #if 0
 static char sccsid[] = "@(#)rewinddir.c	8.1 (Berkeley) 6/8/93";
 #else
-__RCSID("$NetBSD: rewinddir.c,v 1.12 2006/05/17 20:36:50 christos Exp $");
+__RCSID("$NetBSD: rewinddir.c,v 1.13 2010/09/26 02:26:59 yamt Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -43,6 +43,7 @@
 #include "extern.h"
 #include <sys/types.h>
 
+#include <unistd.h>
 #include <dirent.h>
 
 #include "dirent_private.h"
@@ -54,18 +55,20 @@
 void
 rewinddir(DIR *dirp)
 {
-	struct dirpos *dp = dirp->dd_internal;
-
-	while (dp->dp_next)
-		dp = dp->dp_next;
+	int fd;
 
 #ifdef _REENTRANT
 	if (__isthreaded) {
 		mutex_lock((mutex_t *)dirp->dd_lock);
-		_seekdir_unlocked(dirp, (long)(intptr_t)dp);
+	}
+#endif
+	fd = dirp->dd_fd;
+	_finidir(dirp);
+	dirp->dd_seek = lseek(fd, (off_t)0, SEEK_SET);
+	_initdir(dirp, fd, NULL);
+#ifdef _REENTRANT
+	if (__isthreaded) {
 		mutex_unlock((mutex_t *)dirp->dd_lock);
-		return;
 	}
 #endif
-	_seekdir_unlocked(dirp, (long)(intptr_t)dp);
 }

Added files:

Index: src/lib/libc/gen/initdir.c
diff -u /dev/null src/lib/libc/gen/initdir.c:1.1
--- /dev/null	Sun Sep 26 02:27:00 2010
+++ src/lib/libc/gen/initdir.c	Sun Sep 26 02:26:59 2010
@@ -0,0 +1,274 @@
+/*	$NetBSD: initdir.c,v 1.1 2010/09/26 02:26:59 yamt Exp $	*/
+
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: initdir.c,v 1.1 2010/09/26 02:26:59 yamt Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+#include "reentrant.h"
+#include "extern.h"
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dirent_private.h"
+
+#define	MAXITERATIONS	100
+
+int
+_initdir(DIR *dirp, int fd, const char *name)
+{
+	int flags = dirp->dd_flags;
+	int pagesz;
+	int incr;
+
+	/*
+	 * If the machine's page size is an exact multiple of DIRBLKSIZ,
+	 * use a buffer that is cluster boundary aligned.
+	 * Hopefully this can be a big win someday by allowing page trades
+	 * to user space to be done by getdents()
+	 */
+	if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0)
+		incr = pagesz;
+	else
+		incr = DIRBLKSIZ;
+
+	if ((flags & DTF_REWIND) && name == NULL) {
+		return EINVAL;
+	}
+	if ((flags & __DTF_READALL) != 0) {
+		size_t len;
+		size_t space;
+		char *buf, *nbuf;
+		char *ddptr;
+		char *ddeptr;
+		int n;
+		struct dirent **dpv;
+		int i;
+
+		/*
+		 * The strategy here for directories on top of a union stack
+		 * is to read all the directory entries into a buffer, sort
+		 * the buffer, and remove duplicate entries by setting the
+		 * inode number to zero.
+		 *
+		 * For directories on an NFS mounted filesystem, we try
+	 	 * to get a consistent snapshot by trying until we have
+		 * successfully read all of the directory without errors
+		 * (i.e. 'bad cookie' errors from the server because
+		 * the directory was modified). These errors should not
+		 * happen often, but need to be dealt with.
+		 */
+		i = 0;
+retry:
+		len = 0;
+		space = 0;
+		buf = 0;
+		ddptr = 0;
+
+		do {
+			/*
+			 * Always make at least DIRBLKSIZ bytes
+			 * available to getdents
+			 */
+			if (space < DIRBLKSIZ) {
+				space += incr;
+				len += incr;
+				nbuf = realloc(buf, len);
+				if (nbuf == NULL) {
+					dirp->dd_buf = buf;
+					return errno;
+				}
+				buf = nbuf;
+				ddptr = buf + (len - space);
+			}
+
+			dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR);
+			n = getdents(fd, ddptr, space);
+			/*
+			 * For NFS: EINVAL means a bad cookie error
+			 * from the server. Keep trying to get a
+			 * consistent view, in this case this means
+			 * starting all over again.
+			 */
+			if (n == -1 && errno == EINVAL &&
+			    (flags & __DTF_RETRY_ON_BADCOOKIE) != 0) {
+				free(buf);
+				lseek(fd, (off_t)0, SEEK_SET);
+				if (++i > MAXITERATIONS)
+					return EINVAL;
+				goto retry;
+			}
+			if (n > 0) {
+				ddptr += n;
+				space -= n;
+			}
+		} while (n > 0);
+
+		ddeptr = ddptr;
+
+		/*
+		 * Re-open the directory.
+		 * This has the effect of rewinding back to the
+		 * top of the union stack and is needed by
+		 * programs which plan to fchdir to a descriptor
+		 * which has also been read -- see fts.c.
+		 */
+		if (flags & DTF_REWIND) {
+			(void) close(fd);
+			if ((fd = open(name, O_RDONLY)) == -1 ||
+			    fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+				dirp->dd_buf = buf;
+				return errno;
+			}
+		}
+
+		/*
+		 * There is now a buffer full of (possibly) duplicate
+		 * names.
+		 */
+		dirp->dd_buf = buf;
+
+		/*
+		 * Go round this loop twice...
+		 *
+		 * Scan through the buffer, counting entries.
+		 * On the second pass, save pointers to each one.
+		 * Then sort the pointers and remove duplicate names.
+		 */
+		if ((flags & DTF_NODUP) != 0) {
+			for (dpv = 0;;) {
+				for (n = 0, ddptr = buf; ddptr < ddeptr;) {
+					struct dirent *dp;
+
+					dp = (struct dirent *)(void *)ddptr;
+					if ((long)dp & _DIRENT_ALIGN(dp))
+						break;
+					/*
+					 * d_reclen is unsigned,
+					 * so no need to compare <= 0
+					 */
+					if (dp->d_reclen > (ddeptr + 1 - ddptr))
+						break;
+					ddptr += dp->d_reclen;
+					if (dp->d_fileno) {
+						if (dpv)
+							dpv[n] = dp;
+						n++;
+					}
+				}
+
+				if (dpv) {
+					struct dirent *xp;
+
+					/*
+					 * This sort must be stable.
+					 */
+					mergesort(dpv, (size_t)n, sizeof(*dpv),
+					    alphasort);
+
+					dpv[n] = NULL;
+					xp = NULL;
+
+					/*
+					 * Scan through the buffer in sort
+					 * order, zapping the inode number
+					 * of any duplicate names.
+					 */
+					for (n = 0; dpv[n]; n++) {
+						struct dirent *dp = dpv[n];
+
+						if ((xp == NULL) ||
+						    strcmp(dp->d_name,
+						      xp->d_name))
+							xp = dp;
+						else
+							dp->d_fileno = 0;
+						if (dp->d_type == DT_WHT &&
+						    (flags & DTF_HIDEW))
+							dp->d_fileno = 0;
+					}
+
+					free(dpv);
+					break;
+				} else {
+					dpv = malloc((n + 1) *
+					    sizeof(struct dirent *));
+					if (dpv == NULL)
+						break;
+				}
+			}
+		}
+
+		dirp->dd_len = len;
+		dirp->dd_size = ddptr - dirp->dd_buf;
+	} else {
+		dirp->dd_len = incr;
+		dirp->dd_buf = malloc((size_t)dirp->dd_len);
+		if (dirp->dd_buf == NULL)
+			return errno;
+		dirp->dd_seek = 0;
+		flags &= ~DTF_REWIND;
+	}
+	dirp->dd_loc = 0;
+	dirp->dd_fd = fd;
+	dirp->dd_flags = flags;
+	/*
+	 * Set up seek point for rewinddir.
+	 */
+	(void)_telldir_unlocked(dirp);
+	return 0;
+}
+
+void
+_finidir(DIR *dirp)
+{
+	struct dirpos *poslist;
+
+	free(dirp->dd_buf);
+
+	/* free seekdir/telldir storage */
+	for (poslist = dirp->dd_internal; poslist; ) {
+		struct dirpos *nextpos = poslist->dp_next;
+		free(poslist);
+		poslist = nextpos;
+	}
+	dirp->dd_internal = NULL;
+}

Reply via email to