Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src fix rewinddir on nfs. fix PR/42879 (and probably PR/40229.)



details:   https://anonhg.NetBSD.org/src/rev/bbcdabd7db19
branches:  trunk
changeset: 757838:bbcdabd7db19
user:      yamt <yamt%NetBSD.org@localhost>
date:      Sun Sep 26 02:26:59 2010 +0000

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

diffstat:

 include/dirent.h              |    4 +-
 lib/libc/gen/Makefile.inc     |    4 +-
 lib/libc/gen/closedir.c       |   15 +-
 lib/libc/gen/dirent_private.h |    4 +-
 lib/libc/gen/initdir.c        |  274 ++++++++++++++++++++++++++++++++++++++++++
 lib/libc/gen/opendir.c        |  262 ++++++----------------------------------
 lib/libc/gen/rewinddir.c      |   23 +-
 7 files changed, 338 insertions(+), 248 deletions(-)

diffs (truncated from 739 to 300 lines):

diff -r b1c8750a7a2f -r bbcdabd7db19 include/dirent.h
--- a/include/dirent.h  Sat Sep 25 22:14:07 2010 +0000
+++ b/include/dirent.h  Sun Sep 26 02:26:59 2010 +0000
@@ -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>
 
diff -r b1c8750a7a2f -r bbcdabd7db19 lib/libc/gen/Makefile.inc
--- a/lib/libc/gen/Makefile.inc Sat Sep 25 22:14:07 2010 +0000
+++ b/lib/libc/gen/Makefile.inc Sun Sep 26 02:26:59 2010 +0000
@@ -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 \
diff -r b1c8750a7a2f -r bbcdabd7db19 lib/libc/gen/closedir.c
--- a/lib/libc/gen/closedir.c   Sat Sep 25 22:14:07 2010 +0000
+++ b/lib/libc/gen/closedir.c   Sun Sep 26 02:26:59 2010 +0000
@@ -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) {
diff -r b1c8750a7a2f -r bbcdabd7db19 lib/libc/gen/dirent_private.h
--- a/lib/libc/gen/dirent_private.h     Sat Sep 25 22:14:07 2010 +0000
+++ b/lib/libc/gen/dirent_private.h     Sun Sep 26 02:26:59 2010 +0000
@@ -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)
diff -r b1c8750a7a2f -r bbcdabd7db19 lib/libc/gen/initdir.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/libc/gen/initdir.c    Sun Sep 26 02:26:59 2010 +0000
@@ -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;



Home | Main Index | Thread Index | Old Index