From b57ab4df0f3d89567ff5d1b0abe37bde26092eef Mon Sep 17 00:00:00 2001 From: fvdl Date: Fri, 10 Oct 1997 02:18:18 +0000 Subject: [PATCH] Use getdents(). Read NFS directories in one go (just as union dirs, but without removing double entries). This makes sure that we get a consistent snapshot of the directory, and protects against any 'bad cookie' errors. --- lib/libc/gen/opendir.c | 176 ++++++++++++++++++++++++----------------- lib/libc/gen/readdir.c | 8 +- 2 files changed, 108 insertions(+), 76 deletions(-) diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 77e6ad32c3c3..fe794bb93437 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -1,4 +1,4 @@ -/* $NetBSD: opendir.c,v 1.14 1997/07/21 14:07:22 jtc Exp $ */ +/* $NetBSD: opendir.c,v 1.15 1997/10/10 02:18:18 fvdl Exp $ */ /* * Copyright (c) 1983, 1993 @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)opendir.c 8.7 (Berkeley) 12/10/94"; #else -__RCSID("$NetBSD: opendir.c,v 1.14 1997/07/21 14:07:22 jtc Exp $"); +__RCSID("$NetBSD: opendir.c,v 1.15 1997/10/10 02:18:18 fvdl Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -79,7 +79,8 @@ __opendir2(name, flags) struct stat sb; int pagesz; int incr; - int unionstack; + int unionstack, nfsdir; + struct statfs sfb; if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1) return (NULL); @@ -108,35 +109,48 @@ __opendir2(name, flags) /* * Determine whether this directory is the top of a union stack. */ - if (flags & DTF_NODUP) { - struct statfs sfb; - if (fstatfs(fd, &sfb) < 0) { - free(dirp); - close(fd); - return (NULL); - } - unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, - MFSNAMELEN)) || (sfb.f_flags & MNT_UNION); - } else { - unionstack = 0; + if (fstatfs(fd, &sfb) < 0) { + free(dirp); + close(fd); + return (NULL); } - if (unionstack) { - int len = 0; - int space = 0; - char *buf = 0; - char *ddptr = 0; + if (flags & DTF_NODUP) + unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, + MFSNAMELEN)) || (sfb.f_flags & MNT_UNION); + else + unionstack = 0; + + nfsdir = !(strncmp(sfb.f_fstypename, MOUNT_NFS, MFSNAMELEN)); + + if (unionstack || nfsdir) { + int len; + int space; + char *buf; + char *ddptr; char *ddeptr; int n; struct dirent **dpv; /* - * The strategy here is to read all the directory - * entries into a buffer, sort the buffer, and - * remove duplicate entries by setting the inode - * number to zero. + * 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. */ +retry: + len = 0; + space = 0; + buf = 0; + ddptr = 0; do { /* @@ -155,7 +169,19 @@ __opendir2(name, flags) ddptr = buf + (len - space); } - n = getdirentries(fd, ddptr, space, &dirp->dd_seek); + dirp->dd_seek = lseek(fd, 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, 0, SEEK_SET); + goto retry; + } if (n > 0) { ddptr += n; space -= n; @@ -194,59 +220,63 @@ __opendir2(name, flags) * On the second pass, save pointers to each one. * Then sort the pointers and remove duplicate names. */ - for (dpv = 0;;) { - for (n = 0, ddptr = buf; ddptr < ddeptr;) { - struct dirent *dp; + if (!nfsdir) { + for (dpv = 0;;) { + for (n = 0, ddptr = buf; ddptr < ddeptr;) { + struct dirent *dp; - dp = (struct dirent *) ddptr; - if ((long)dp & 03) - break; - if ((dp->d_reclen <= 0) || - (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, 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; + dp = (struct dirent *) ddptr; + if ((long)dp & 03) + break; + if ((dp->d_reclen <= 0) || + (dp->d_reclen > (ddeptr + 1 - ddptr))) + break; + ddptr += dp->d_reclen; + if (dp->d_fileno) { + if (dpv) + dpv[n] = dp; + n++; + } } - free(dpv); - break; - } else { - dpv = malloc((n+1) * sizeof(struct dirent *)); - if (dpv == NULL) + if (dpv) { + struct dirent *xp; + + /* + * This sort must be stable. + */ + mergesort(dpv, 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; + } } } diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c index 11e659518176..9a52a0831462 100644 --- a/lib/libc/gen/readdir.c +++ b/lib/libc/gen/readdir.c @@ -1,4 +1,4 @@ -/* $NetBSD: readdir.c,v 1.7 1997/07/21 14:07:26 jtc Exp $ */ +/* $NetBSD: readdir.c,v 1.8 1997/10/10 02:18:22 fvdl Exp $ */ /* * Copyright (c) 1983, 1993 @@ -38,12 +38,13 @@ #if 0 static char sccsid[] = "@(#)readdir.c 8.3 (Berkeley) 9/29/94"; #else -__RCSID("$NetBSD: readdir.c,v 1.7 1997/07/21 14:07:26 jtc Exp $"); +__RCSID("$NetBSD: readdir.c,v 1.8 1997/10/10 02:18:22 fvdl Exp $"); #endif #endif /* LIBC_SCCS and not lint */ #include "namespace.h" #include +#include #include #ifdef __weak_alias @@ -66,7 +67,8 @@ readdir(dirp) dirp->dd_loc = 0; } if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) { - dirp->dd_size = getdirentries(dirp->dd_fd, + dirp->dd_seek = lseek(dirp->dd_fd, 0, SEEK_CUR); + dirp->dd_size = getdents(dirp->dd_fd, dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); if (dirp->dd_size <= 0) return (NULL);