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);