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;
+}