change the way to handle directory "offsets" so that

they won't be changed when removing entries in the directory.
some applications like cvs rely on this.
This commit is contained in:
yamt 2005-09-15 12:34:35 +00:00
parent bdc7967496
commit 8e6209cffb
3 changed files with 111 additions and 90 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: tmpfs.h,v 1.3 2005/09/13 14:29:18 yamt Exp $ */
/* $NetBSD: tmpfs.h,v 1.4 2005/09/15 12:34:35 yamt Exp $ */
/*
* Copyright (c) 2005 The NetBSD Foundation, Inc.
@ -68,6 +68,11 @@ struct tmpfs_dirent {
};
TAILQ_HEAD(tmpfs_dir, tmpfs_dirent);
#define TMPFS_DIRCOOKIE(dirent) ((off_t)(uintptr_t)(dirent))
#define TMPFS_DIRCOOKIE_DOT 0
#define TMPFS_DIRCOOKIE_DOTDOT 1
#define TMPFS_DIRCOOKIE_EOF 2
/* --------------------------------------------------------------------- */
/*
@ -116,7 +121,7 @@ struct tmpfs_node {
struct tmpfs_dir tn_dir;
/* Used by tmpfs_readdir to speed up lookups. */
long tn_readdir_lastn;
off_t tn_readdir_lastn;
struct tmpfs_dirent * tn_readdir_lastp;
};
@ -193,7 +198,8 @@ struct tmpfs_dirent * tmpfs_dir_lookup(struct tmpfs_node *node,
struct componentname *cnp);
int tmpfs_dir_getdotdent(struct tmpfs_node *, struct uio *);
int tmpfs_dir_getdotdotdent(struct tmpfs_node *, struct uio *);
int tmpfs_dir_getdents(struct tmpfs_node *, struct uio *);
struct tmpfs_dirent * tmpfs_dir_lookupbycookie(struct tmpfs_node *, off_t);
int tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, off_t *);
int tmpfs_reg_resize(struct vnode *, off_t);
size_t tmpfs_mem_info(boolean_t);
int tmpfs_chflags(struct vnode *, int, struct ucred *, struct proc *);
@ -230,7 +236,9 @@ int tmpfs_chtimes(struct vnode *, struct timespec *, struct timespec *,
*/
#define TMPFS_VALIDATE_DIR(node) \
KASSERT((node)->tn_type == VDIR); \
KASSERT((node)->tn_size % sizeof(struct tmpfs_dirent) == 0);
KASSERT((node)->tn_size % sizeof(struct tmpfs_dirent) == 0); \
KASSERT((node)->tn_readdir_lastp == NULL || \
TMPFS_DIRCOOKIE((node)->tn_readdir_lastp) == (node)->tn_readdir_lastn);
/* --------------------------------------------------------------------- */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tmpfs_subr.c,v 1.4 2005/09/13 14:29:18 yamt Exp $ */
/* $NetBSD: tmpfs_subr.c,v 1.5 2005/09/15 12:34:35 yamt Exp $ */
/*
* Copyright (c) 2005 The NetBSD Foundation, Inc.
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.4 2005/09/13 14:29:18 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.5 2005/09/15 12:34:35 yamt Exp $");
#include <sys/param.h>
#include <sys/dirent.h>
@ -483,8 +483,15 @@ tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
{
struct tmpfs_node *dnode;
KASSERT(VOP_ISLOCKED(vp));
dnode = VP_TO_TMPFS_DIR(vp);
if (dnode->tn_readdir_lastp == de) {
dnode->tn_readdir_lastn = 0;
dnode->tn_readdir_lastp = NULL;
}
TAILQ_REMOVE(&dnode->tn_dir, de, td_entries);
dnode->tn_size -= sizeof(struct tmpfs_dirent);
dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
@ -534,7 +541,7 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
struct dirent dent;
TMPFS_VALIDATE_DIR(node);
KASSERT(uio->uio_offset == 0);
KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
dent.d_fileno = node->tn_id;
dent.d_type = DT_DIR;
@ -548,8 +555,7 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
else {
error = uiomove(&dent, dent.d_reclen, uio);
if (error == 0)
uio->uio_offset += sizeof(struct tmpfs_dirent) - \
dent.d_reclen;
uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
}
node->tn_status |= TMPFS_NODE_ACCESSED;
@ -571,7 +577,7 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
struct dirent dent;
TMPFS_VALIDATE_DIR(node);
KASSERT(uio->uio_offset == sizeof(struct tmpfs_dirent));
KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
dent.d_fileno = node->tn_id;
dent.d_type = DT_DIR;
@ -585,9 +591,15 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
error = -1;
else {
error = uiomove(&dent, dent.d_reclen, uio);
if (error == 0)
uio->uio_offset += sizeof(struct tmpfs_dirent) - \
dent.d_reclen;
if (error == 0) {
struct tmpfs_dirent *de;
de = TAILQ_FIRST(&node->tn_dir);
if (de == NULL)
uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
else
uio->uio_offset = TMPFS_DIRCOOKIE(de);
}
}
node->tn_status |= TMPFS_NODE_ACCESSED;
@ -597,49 +609,56 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
/* --------------------------------------------------------------------- */
/* lookup a directory entry by cookie */
struct tmpfs_dirent *
tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
{
struct tmpfs_dirent *de;
if (cookie == node->tn_readdir_lastn &&
node->tn_readdir_lastp != NULL) {
return node->tn_readdir_lastp;
}
TAILQ_FOREACH(de, &node->tn_dir, td_entries) {
if (TMPFS_DIRCOOKIE(de) == cookie) {
break;
}
}
return de;
}
/* --------------------------------------------------------------------- */
/* Helper function for tmpfs_readdir. Returns as much directory entries
* as can fit in the uio space. The read starts at uio->uio_offset.
* The function returns 0 on success, -1 if there was not enough space
* in the uio structure to hold the directory entry or an appropriate
* error code if another error happens. */
int
tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio)
tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
{
int error;
long cnt, startcnt;
off_t startcookie;
struct tmpfs_dirent *de;
TMPFS_VALIDATE_DIR(node);
KASSERT(uio->uio_offset % sizeof(struct tmpfs_dirent) == 0);
KASSERT(uio->uio_offset >= sizeof(struct tmpfs_dirent) * 2);
KASSERT(uio->uio_offset < node->tn_size +
sizeof(struct tmpfs_dirent) * 2);
/* Locate the first directory entry we have to return. We have cached
* the last readdir in the node, so use those values if appropriate.
* Otherwise do a linear scan to find the requested entry. */
de = NULL;
startcnt = uio->uio_offset / sizeof(struct tmpfs_dirent) - 2;
if (startcnt == node->tn_readdir_lastn && \
node->tn_readdir_lastp != NULL) {
cnt = node->tn_readdir_lastn;
de = node->tn_readdir_lastp;
startcookie = uio->uio_offset;
KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);
KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
if (startcookie == TMPFS_DIRCOOKIE_EOF) {
return 0;
} else {
cnt = 0;
de = TAILQ_FIRST(&node->tn_dir);
while (cnt < startcnt) {
cnt++;
de = TAILQ_NEXT(de, td_entries);
/* Ensure that if we have not found the desired item,
* there are more entries in the directory to continue
* the search. */
KASSERT(IMPLIES(de == TAILQ_LAST(&node->tn_dir,
tmpfs_dir), cnt == startcnt));
}
de = tmpfs_dir_lookupbycookie(node, startcookie);
}
if (de == NULL) {
return EINVAL;
}
KASSERT(cnt == startcnt);
KASSERT(de != NULL);
/* Read as much entries as possible; i.e., until we reach the end of
* the directory or we exhaust uio space. */
@ -698,22 +717,17 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio)
* advance pointers. */
error = uiomove(&d, d.d_reclen, uio);
cnt++;
(*cntp)++;
de = TAILQ_NEXT(de, td_entries);
} while (error == 0 && uio->uio_resid > 0 && de != NULL);
/* Update the offset in the uio structure to be correctly aligned
* with tmpfs_dirent structures. Otherwise, the offset is the
* size of the returned dirent structures, which is useless for us. */
uio->uio_offset = (cnt + 2) * sizeof(struct tmpfs_dirent);
/* Cache the current status. */
/* Update the offset and cache. */
if (de == NULL) {
KASSERT(cnt == node->tn_size / sizeof(struct tmpfs_dirent));
uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
node->tn_readdir_lastn = 0;
node->tn_readdir_lastp = NULL;
} else {
node->tn_readdir_lastn = cnt;
node->tn_readdir_lastn = uio->uio_offset = TMPFS_DIRCOOKIE(de);
node->tn_readdir_lastp = de;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: tmpfs_vnops.c,v 1.9 2005/09/14 20:27:26 yamt Exp $ */
/* $NetBSD: tmpfs_vnops.c,v 1.10 2005/09/15 12:34:35 yamt Exp $ */
/*
* Copyright (c) 2005 The NetBSD Foundation, Inc.
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.9 2005/09/14 20:27:26 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.10 2005/09/15 12:34:35 yamt Exp $");
#include <sys/param.h>
#include <sys/dirent.h>
@ -1040,11 +1040,6 @@ tmpfs_rmdir(void *v)
* interested parties and clean it from the cache). */
vput(vp);
/* As the directory has been modified, invalidate readdir cached
* values. */
dnode->tn_readdir_lastn = 0;
dnode->tn_readdir_lastp = NULL;
error = 0;
out:
@ -1084,7 +1079,8 @@ tmpfs_readdir(void *v)
int *ncookies = ((struct vop_readdir_args *)v)->a_ncookies;
int error;
off_t realsize, startoff;
off_t startoff;
off_t cnt;
struct tmpfs_node *node;
KASSERT(VOP_ISLOCKED(vp));
@ -1096,75 +1092,78 @@ tmpfs_readdir(void *v)
}
node = VP_TO_TMPFS_DIR(vp);
realsize = node->tn_size + sizeof(struct tmpfs_dirent) * 2;
/* The offset cannot extend past the end of the directory. */
if (uio->uio_offset >= realsize) {
error = EINVAL;
goto out;
}
/* The offset must be correctly aligned at the begining of
* tmpfs_dirent structures. */
if (uio->uio_offset % sizeof(struct tmpfs_dirent) > 0) {
error = EINVAL;
goto out;
}
startoff = uio->uio_offset;
if (uio->uio_offset == 0) {
cnt = 0;
if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
error = tmpfs_dir_getdotdent(node, uio);
if (error == -1) {
error = 0;
goto outok;
} else if (error != 0)
goto outok;
cnt++;
}
if (uio->uio_offset == sizeof(struct tmpfs_dirent)) {
if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
error = tmpfs_dir_getdotdotdent(node, uio);
if (error == -1) {
error = 0;
goto outok;
} else if (error != 0)
goto outok;
cnt++;
}
if (uio->uio_offset == realsize) {
error = 0;
goto outok;
}
KASSERT(uio->uio_offset < realsize);
error = tmpfs_dir_getdents(node, uio);
error = tmpfs_dir_getdents(node, uio, &cnt);
if (error == -1)
error = 0;
KASSERT(error >= 0);
outok:
/* This label assumes that startoff, node and realsize have been
/* This label assumes that startoff has been
* initialized. If the compiler didn't spit out warnings, we'd
* simply make this one be 'out' and drop 'outok'. */
if (eofflag != NULL)
*eofflag =
(error == 0 && uio->uio_offset == realsize);
(error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
/* Update NFS-related variables. */
if (error == 0 && cookies != NULL && ncookies != NULL) {
long cnt, startcnt;
off_t i;
off_t off = startoff;
struct tmpfs_dirent *de = NULL;
startcnt = startoff / sizeof(struct tmpfs_dirent);
cnt = uio->uio_offset / sizeof(struct tmpfs_dirent);
*ncookies = cnt - startcnt;
*cookies = (off_t *)malloc((*ncookies) * sizeof(off_t),
M_TEMP, M_WAITOK);
*ncookies = cnt;
*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
for (i = 0; i < *ncookies; i++)
(*cookies)[i] = (startcnt + i + 1) *
sizeof(struct tmpfs_dirent);
for (i = 0; i < cnt; i++) {
KASSERT(off != TMPFS_DIRCOOKIE_EOF);
if (off == TMPFS_DIRCOOKIE_DOT) {
off = TMPFS_DIRCOOKIE_DOTDOT;
} else {
if (off == TMPFS_DIRCOOKIE_DOTDOT) {
de = TAILQ_FIRST(&node->tn_dir);
} else if (de != NULL) {
de = TAILQ_NEXT(de, td_entries);
} else {
de = tmpfs_dir_lookupbycookie(node,
off);
KASSERT(de != NULL);
de = TAILQ_NEXT(de, td_entries);
}
if (de == NULL) {
off = TMPFS_DIRCOOKIE_EOF;
} else {
off = TMPFS_DIRCOOKIE(de);
}
}
(*cookies)[i] = off;
}
KASSERT(uio->uio_offset == off);
}
out: