Module Name:    src
Committed By:   hannken
Date:           Mon Apr  4 19:16:58 UTC 2011

Modified Files:
        src/sys/fs/msdosfs: denode.h msdosfs_denode.c msdosfs_vfsops.c
        src/tests/fs/vfs: t_vfsops.c

Log Message:
Msdosfs on-disk meta data is not sufficient to create or validate file handles.

Maintain a tree of file handles, create nodes from msdosfs_vptofh() and keep
them until either the file gets unlinked or the file system gets unmounted.

Fixes the msdosfs part of PR #43745 (fhopen of an unlinked file causes problems
on multiple file systems)


To generate a diff of this commit:
cvs rdiff -u -r1.18 -r1.19 src/sys/fs/msdosfs/denode.h
cvs rdiff -u -r1.42 -r1.43 src/sys/fs/msdosfs/msdosfs_denode.c
cvs rdiff -u -r1.89 -r1.90 src/sys/fs/msdosfs/msdosfs_vfsops.c
cvs rdiff -u -r1.10 -r1.11 src/tests/fs/vfs/t_vfsops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/fs/msdosfs/denode.h
diff -u src/sys/fs/msdosfs/denode.h:1.18 src/sys/fs/msdosfs/denode.h:1.19
--- src/sys/fs/msdosfs/denode.h:1.18	Thu Apr  8 16:04:35 2010
+++ src/sys/fs/msdosfs/denode.h	Mon Apr  4 19:16:58 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: denode.h,v 1.18 2010/04/08 16:04:35 pooka Exp $	*/
+/*	$NetBSD: denode.h,v 1.19 2011/04/04 19:16:58 hannken Exp $	*/
 
 /*-
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
@@ -249,9 +249,7 @@
 
 	u_int32_t defid_dirclust; /* cluster this dir entry came from */
 	u_int32_t defid_dirofs;	/* offset of entry within the cluster */
-#if 0
 	u_int32_t defid_gen;	/* generation number */
-#endif
 };
 
 /*
@@ -310,5 +308,9 @@
 void msdosfs_gop_markupdate(struct vnode *, int);
 void msdosfs_detimes(struct denode *, const struct timespec *,
     const struct timespec *, const struct timespec *, int);
+int msdosfs_fh_enter(struct msdosfsmount *, uint32_t, uint32_t, uint32_t *);
+int msdosfs_fh_remove(struct msdosfsmount *, uint32_t, uint32_t);
+int msdosfs_fh_lookup(struct msdosfsmount *, uint32_t, uint32_t, uint32_t *);
+void msdosfs_fh_destroy(struct msdosfsmount *);
 #endif	/* _KERNEL */
 #endif /* _MSDOSFS_DENODE_H_ */

Index: src/sys/fs/msdosfs/msdosfs_denode.c
diff -u src/sys/fs/msdosfs/msdosfs_denode.c:1.42 src/sys/fs/msdosfs/msdosfs_denode.c:1.43
--- src/sys/fs/msdosfs/msdosfs_denode.c:1.42	Tue Mar 22 20:33:51 2011
+++ src/sys/fs/msdosfs/msdosfs_denode.c	Mon Apr  4 19:16:58 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: msdosfs_denode.c,v 1.42 2011/03/22 20:33:51 hannken Exp $	*/
+/*	$NetBSD: msdosfs_denode.c,v 1.43 2011/04/04 19:16:58 hannken Exp $	*/
 
 /*-
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.42 2011/03/22 20:33:51 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.43 2011/04/04 19:16:58 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -84,6 +84,53 @@
 
 extern int prtactive;
 
+struct fh_key {
+	struct msdosfsmount *fhk_mount;
+	uint32_t fhk_dircluster;
+	uint32_t fhk_diroffset;
+};
+struct fh_node {
+	struct rb_node fh_rbnode;
+	struct fh_key fh_key;
+#define fh_mount	fh_key.fhk_mount
+#define fh_dircluster	fh_key.fhk_dircluster
+#define fh_diroffset	fh_key.fhk_diroffset
+	uint32_t fh_gen;
+};
+
+static int
+fh_compare_node_fh(void *ctx, const void *b, const void *key)
+{
+	const struct fh_node * const pnp = b;
+	const struct fh_key * const fhp = key;
+
+	/* msdosfs_fh_destroy() below depends on first sorting on fh_mount. */
+	if (pnp->fh_mount != fhp->fhk_mount)
+		return (intptr_t)pnp->fh_mount - (intptr_t)fhp->fhk_mount;
+	if (pnp->fh_dircluster != fhp->fhk_dircluster)
+		return pnp->fh_dircluster - fhp->fhk_dircluster;
+	return pnp->fh_diroffset - fhp->fhk_diroffset;
+}
+
+static int
+fh_compare_nodes(void *ctx, const void *parent, const void *node)
+{
+	const struct fh_node * const np = node;
+
+	return fh_compare_node_fh(ctx, parent, &np->fh_key);
+}
+
+static uint32_t fh_generation;
+static kmutex_t fh_lock;
+static struct pool fh_pool;
+static rb_tree_t fh_rbtree;
+static const rb_tree_ops_t fh_rbtree_ops = {
+	.rbto_compare_nodes = fh_compare_nodes,
+	.rbto_compare_key = fh_compare_node_fh,
+	.rbto_node_offset = offsetof(struct fh_node, fh_rbnode),
+	.rbto_context = NULL
+};
+
 static const struct genfs_ops msdosfs_genfsops = {
 	.gop_size = genfs_size,
 	.gop_alloc = msdosfs_gop_alloc,
@@ -106,8 +153,12 @@
 	malloc_type_attach(M_MSDOSFSTMP);
 	pool_init(&msdosfs_denode_pool, sizeof(struct denode), 0, 0, 0,
 	    "msdosnopl", &pool_allocator_nointr, IPL_NONE);
+	pool_init(&fh_pool, sizeof(struct fh_node), 0, 0, 0,
+	    "msdosfhpl", &pool_allocator_nointr, IPL_NONE);
 	dehashtbl = hashinit(desiredvnodes / 2, HASH_LIST, true, &dehash);
+	rb_tree_init(&fh_rbtree, &fh_rbtree_ops);
 	mutex_init(&msdosfs_ihash_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&fh_lock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&msdosfs_hashlock, MUTEX_DEFAULT, IPL_NONE);
 }
 
@@ -147,7 +198,9 @@
 {
 	hashdone(dehashtbl, HASH_LIST, dehash);
 	pool_destroy(&msdosfs_denode_pool);
+	pool_destroy(&fh_pool);
 	mutex_destroy(&msdosfs_ihash_lock);
+	mutex_destroy(&fh_lock);
 	mutex_destroy(&msdosfs_hashlock);
 	malloc_type_detach(M_MSDOSFSTMP);
 	malloc_type_detach(M_MSDOSFSFAT);
@@ -686,6 +739,8 @@
 			error = detrunc(dep, (u_long)0, 0, NOCRED);
 		}
 		dep->de_Name[0] = SLOT_DELETED;
+		msdosfs_fh_remove(dep->de_pmp,
+		    dep->de_dirclust, dep->de_diroffset);
 	}
 	deupdat(dep, 0);
 out:
@@ -727,3 +782,99 @@
 		dep->de_flag |= mask;
 	}
 }
+
+int
+msdosfs_fh_enter(struct msdosfsmount *pmp,
+     uint32_t dircluster, uint32_t diroffset, uint32_t *genp)
+{
+	struct fh_key fhkey;
+	struct fh_node *fhp;
+
+	fhkey.fhk_mount = pmp;
+	fhkey.fhk_dircluster = dircluster;
+	fhkey.fhk_diroffset = diroffset;
+
+	mutex_enter(&fh_lock);
+	fhp = rb_tree_find_node(&fh_rbtree, &fhkey);
+	if (fhp == NULL) {
+		mutex_exit(&fh_lock);
+		fhp = pool_get(&fh_pool, PR_WAITOK);
+		mutex_enter(&fh_lock);
+		fhp->fh_key = fhkey;
+		fhp->fh_gen = fh_generation++;
+		rb_tree_insert_node(&fh_rbtree, fhp);
+	}
+	*genp = fhp->fh_gen;
+	mutex_exit(&fh_lock);
+	return 0;
+}
+
+int
+msdosfs_fh_remove(struct msdosfsmount *pmp,
+     uint32_t dircluster, uint32_t diroffset)
+{
+	struct fh_key fhkey;
+	struct fh_node *fhp;
+
+	fhkey.fhk_mount = pmp;
+	fhkey.fhk_dircluster = dircluster;
+	fhkey.fhk_diroffset = diroffset;
+
+	mutex_enter(&fh_lock);
+	fhp = rb_tree_find_node(&fh_rbtree, &fhkey);
+	if (fhp == NULL) {
+		mutex_exit(&fh_lock);
+		return ENOENT;
+	}
+	rb_tree_remove_node(&fh_rbtree, fhp);
+	mutex_exit(&fh_lock);
+	pool_put(&fh_pool, fhp);
+	return 0;
+}
+
+int
+msdosfs_fh_lookup(struct msdosfsmount *pmp,
+     uint32_t dircluster, uint32_t diroffset, uint32_t *genp)
+{
+	struct fh_key fhkey;
+	struct fh_node *fhp;
+
+	fhkey.fhk_mount = pmp;
+	fhkey.fhk_dircluster = dircluster;
+	fhkey.fhk_diroffset = diroffset;
+
+	mutex_enter(&fh_lock);
+	fhp = rb_tree_find_node(&fh_rbtree, &fhkey);
+	if (fhp == NULL) {
+		mutex_exit(&fh_lock);
+		return ESTALE;
+	}
+	*genp = fhp->fh_gen;
+	mutex_exit(&fh_lock);
+	return 0;
+}
+
+void
+msdosfs_fh_destroy(struct msdosfsmount *pmp)
+{
+	struct fh_key fhkey;
+	struct fh_node *fhp, *nfhp;
+
+	fhkey.fhk_mount = pmp;
+	fhkey.fhk_dircluster = 0;
+	fhkey.fhk_diroffset = 0;
+
+	mutex_enter(&fh_lock);
+	for (fhp = rb_tree_find_node_geq(&fh_rbtree, &fhkey);
+	    fhp != NULL && fhp->fh_mount == pmp; fhp = nfhp) {
+		nfhp = rb_tree_iterate(&fh_rbtree, fhp, RB_DIR_RIGHT);
+		rb_tree_remove_node(&fh_rbtree, fhp);
+		pool_put(&fh_pool, fhp);
+	}
+#ifdef DIAGNOSTIC
+	RB_TREE_FOREACH(fhp, &fh_rbtree) {
+		KASSERT(fhp->fh_mount != pmp);
+	}
+#endif
+	mutex_exit(&fh_lock);
+}

Index: src/sys/fs/msdosfs/msdosfs_vfsops.c
diff -u src/sys/fs/msdosfs/msdosfs_vfsops.c:1.89 src/sys/fs/msdosfs/msdosfs_vfsops.c:1.90
--- src/sys/fs/msdosfs/msdosfs_vfsops.c:1.89	Mon Dec 27 18:49:42 2010
+++ src/sys/fs/msdosfs/msdosfs_vfsops.c	Mon Apr  4 19:16:58 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: msdosfs_vfsops.c,v 1.89 2010/12/27 18:49:42 hannken Exp $	*/
+/*	$NetBSD: msdosfs_vfsops.c,v 1.90 2011/04/04 19:16:58 hannken Exp $	*/
 
 /*-
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.89 2010/12/27 18:49:42 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.90 2011/04/04 19:16:58 hannken Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_compat_netbsd.h"
@@ -887,6 +887,7 @@
 	(void) VOP_CLOSE(pmp->pm_devvp,
 	    pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED);
 	vput(pmp->pm_devvp);
+	msdosfs_fh_destroy(pmp);
 	free(pmp->pm_inusemap, M_MSDOSFSFAT);
 	free(pmp, M_MSDOSFSMNT);
 	mp->mnt_data = NULL;
@@ -1009,6 +1010,7 @@
 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
 	struct defid defh;
 	struct denode *dep;
+	uint32_t gen;
 	int error;
 
 	if (fhp->fid_len != sizeof(struct defid)) {
@@ -1016,8 +1018,15 @@
 		    sizeof(struct defid)));
 		return EINVAL;
 	}
-
 	memcpy(&defh, fhp, sizeof(defh));
+	error = msdosfs_fh_lookup(pmp, defh.defid_dirclust, defh.defid_dirofs,
+	    &gen);
+	if (error == 0 && gen != defh.defid_gen)
+		error = ESTALE;
+	if (error) {
+		*vpp = NULLVP;
+		return error;
+	}
 	error = deget(pmp, defh.defid_dirclust, defh.defid_dirofs, &dep);
 	if (error) {
 		DPRINTF(("deget %d\n", error));
@@ -1031,8 +1040,10 @@
 int
 msdosfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
 {
+	struct msdosfsmount *pmp = VFSTOMSDOSFS(vp->v_mount);
 	struct denode *dep;
 	struct defid defh;
+	int error;
 
 	if (*fh_size < sizeof(struct defid)) {
 		*fh_size = sizeof(struct defid);
@@ -1044,9 +1055,11 @@
 	defh.defid_len = sizeof(struct defid);
 	defh.defid_dirclust = dep->de_dirclust;
 	defh.defid_dirofs = dep->de_diroffset;
-	/* defh.defid_gen = dep->de_gen; */
-	memcpy(fhp, &defh, sizeof(defh));
-	return (0);
+	error = msdosfs_fh_enter(pmp, dep->de_dirclust, dep->de_diroffset,
+	     &defh.defid_gen);
+	if (error == 0)
+		memcpy(fhp, &defh, sizeof(defh));
+	return error;
 }
 
 int

Index: src/tests/fs/vfs/t_vfsops.c
diff -u src/tests/fs/vfs/t_vfsops.c:1.10 src/tests/fs/vfs/t_vfsops.c:1.11
--- src/tests/fs/vfs/t_vfsops.c:1.10	Sat Apr  2 14:24:53 2011
+++ src/tests/fs/vfs/t_vfsops.c	Mon Apr  4 19:16:58 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_vfsops.c,v 1.10 2011/04/02 14:24:53 hannken Exp $	*/
+/*	$NetBSD: t_vfsops.c,v 1.11 2011/04/04 19:16:58 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -146,7 +146,7 @@
 	RL(rump_sys_getfh(FNAME, fhp, &fhsize));
 	RL(rump_sys_unlink(FNAME));
 
-	if (FSTYPE_MSDOS(tc) || FSTYPE_LFS(tc))
+	if (FSTYPE_LFS(tc))
 		atf_tc_expect_fail("fhopen() for removed file succeeds "
 		    "(PR kern/43745)");
 	ATF_REQUIRE_ERRNO(ESTALE, rump_sys_fhopen(fhp, fhsize, O_RDONLY) == -1);

Reply via email to