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.
This commit is contained in:
fvdl 1997-10-10 02:18:18 +00:00
parent e03a6d4d3e
commit b57ab4df0f
2 changed files with 108 additions and 76 deletions

View File

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

View File

@ -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 <sys/param.h>
#include <unistd.h>
#include <dirent.h>
#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);