Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/fs/tmpfs Fix tmpfs_rename locking.
details: https://anonhg.NetBSD.org/src/rev/0d5ddeb0db2b
branches: trunk
changeset: 768542:0d5ddeb0db2b
user: riastradh <riastradh%NetBSD.org@localhost>
date: Thu Aug 18 21:42:18 2011 +0000
description:
Fix tmpfs_rename locking.
Fixes PR kern/36681. tmpfs now survives dirconc, all our vfs/tmpfs
tests and rename races in atf, and a bunch of hand-written tests
that I'd commit if atf didn't find them highly indigestible.
ok dholland
diffstat:
sys/fs/tmpfs/tmpfs_vnops.c | 1384 ++++++++++++++++++++++++++++++++++++++-----
1 files changed, 1207 insertions(+), 177 deletions(-)
diffs (truncated from 1457 to 300 lines):
diff -r 45dbbcc4e910 -r 0d5ddeb0db2b sys/fs/tmpfs/tmpfs_vnops.c
--- a/sys/fs/tmpfs/tmpfs_vnops.c Thu Aug 18 21:04:23 2011 +0000
+++ b/sys/fs/tmpfs/tmpfs_vnops.c Thu Aug 18 21:42:18 2011 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tmpfs_vnops.c,v 1.88 2011/07/13 03:28:41 riastradh Exp $ */
+/* $NetBSD: tmpfs_vnops.c,v 1.89 2011/08/18 21:42:18 riastradh Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.88 2011/07/13 03:28:41 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.89 2011/08/18 21:42:18 riastradh Exp $");
#include <sys/param.h>
#include <sys/dirent.h>
@@ -798,27 +798,8 @@
}
/*
- * tmpfs_parentcheck_p: check if 'lower' is a descendent of 'upper'.
- *
- * => Returns 'true' if parent, and 'false' otherwise.
- */
-static inline bool
-tmpfs_parentcheck_p(tmpfs_node_t *lower, tmpfs_node_t *upper)
-{
- tmpfs_node_t *un = lower;
-
- while (un != un->tn_spec.tn_dir.tn_parent) {
- KASSERT(un->tn_type == VDIR);
- if (un == upper) {
- return true;
- }
- un = un->tn_spec.tn_dir.tn_parent;
- }
- return false;
-}
-
-/*
- * tmpfs_rename: rename routine.
+ * tmpfs_rename: rename routine, the hairiest system call, with the
+ * insane API.
*
* Arguments: fdvp (from-parent vnode), fvp (from-leaf), tdvp (to-parent)
* and tvp (to-leaf), if exists (NULL if not).
@@ -829,6 +810,57 @@
* => Both tdvp and tvp are referenced and locked. It is our responsibility
* to release the references and unlock them (or destroy).
*/
+
+/*
+ * First, some forward declarations of subroutines.
+ */
+
+static int tmpfs_sane_rename(struct vnode *, struct componentname *,
+ struct vnode *, struct componentname *, kauth_cred_t, bool);
+static int tmpfs_rename_enter(struct mount *, struct tmpfs_mount *,
+ kauth_cred_t,
+ struct vnode *, struct tmpfs_node *, struct componentname *,
+ struct tmpfs_dirent **, struct vnode **,
+ struct vnode *, struct tmpfs_node *, struct componentname *,
+ struct tmpfs_dirent **, struct vnode **);
+static int tmpfs_rename_enter_common(struct mount *, struct tmpfs_mount *,
+ kauth_cred_t,
+ struct vnode *, struct tmpfs_node *,
+ struct componentname *, struct tmpfs_dirent **, struct vnode **,
+ struct componentname *, struct tmpfs_dirent **, struct vnode **);
+static int tmpfs_rename_enter_separate(struct mount *, struct tmpfs_mount *,
+ kauth_cred_t,
+ struct vnode *, struct tmpfs_node *, struct componentname *,
+ struct tmpfs_dirent **, struct vnode **,
+ struct vnode *, struct tmpfs_node *, struct componentname *,
+ struct tmpfs_dirent **, struct vnode **);
+static void tmpfs_rename_exit(struct tmpfs_mount *,
+ struct vnode *, struct vnode *, struct vnode *, struct vnode *);
+static int tmpfs_rename_lock_directory(struct vnode *, struct tmpfs_node *);
+static int tmpfs_rename_genealogy(struct tmpfs_node *, struct tmpfs_node *,
+ struct tmpfs_node **);
+static int tmpfs_rename_lock(struct mount *, kauth_cred_t, int,
+ struct vnode *, struct tmpfs_node *, struct componentname *, bool,
+ struct tmpfs_dirent **, struct vnode **,
+ struct vnode *, struct tmpfs_node *, struct componentname *, bool,
+ struct tmpfs_dirent **, struct vnode **);
+static void tmpfs_rename_attachdetach(struct tmpfs_mount *,
+ struct vnode *, struct tmpfs_dirent *, struct vnode *,
+ struct vnode *, struct tmpfs_dirent *, struct vnode *);
+static int tmpfs_do_remove(struct tmpfs_mount *, struct vnode *,
+ struct tmpfs_node *, struct tmpfs_dirent *, struct vnode *, kauth_cred_t);
+static int tmpfs_rename_check_possible(struct tmpfs_node *,
+ struct tmpfs_node *, struct tmpfs_node *, struct tmpfs_node *);
+static int tmpfs_rename_check_permitted(kauth_cred_t,
+ struct tmpfs_node *, struct tmpfs_node *,
+ struct tmpfs_node *, struct tmpfs_node *);
+static int tmpfs_remove_check_possible(struct tmpfs_node *,
+ struct tmpfs_node *);
+static int tmpfs_remove_check_permitted(kauth_cred_t,
+ struct tmpfs_node *, struct tmpfs_node *);
+static int tmpfs_check_sticky(kauth_cred_t,
+ struct tmpfs_node *, struct tmpfs_node *);
+
int
tmpfs_rename(void *v)
{
@@ -840,198 +872,1196 @@
struct vnode *a_tvp;
struct componentname *a_tcnp;
} */ *ap = v;
- vnode_t *fdvp = ap->a_fdvp;
- vnode_t *fvp = ap->a_fvp;
+ struct vnode *fdvp = ap->a_fdvp;
+ struct vnode *fvp = ap->a_fvp;
struct componentname *fcnp = ap->a_fcnp;
- vnode_t *tdvp = ap->a_tdvp;
- vnode_t *tvp = ap->a_tvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *tvp = ap->a_tvp;
struct componentname *tcnp = ap->a_tcnp;
- tmpfs_node_t *fdnode, *fnode, *tnode, *tdnode;
- tmpfs_dirent_t *de;
- tmpfs_mount_t *tmp;
- size_t namelen;
+ kauth_cred_t cred;
+ int error;
+
+ KASSERT(fdvp != NULL);
+ KASSERT(fvp != NULL);
+ KASSERT(fcnp != NULL);
+ KASSERT(fcnp->cn_nameptr != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(tcnp != NULL);
+ KASSERT(fcnp->cn_nameptr != NULL);
+ /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
+ /* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */
+ KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
+ KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
+ KASSERT(fdvp->v_type == VDIR);
+ KASSERT(tdvp->v_type == VDIR);
+
+ cred = fcnp->cn_cred;
+ KASSERT(tcnp->cn_cred == cred);
+
+ /*
+ * Sanitize our world from the VFS insanity. Unlock the target
+ * directory and node, which are locked. Release the children,
+ * which are referenced. Check for rename("x", "y/."), which
+ * it is our responsibility to reject, not the caller's. (But
+ * the caller does reject rename("x/.", "y"). Go figure.)
+ */
+
+ VOP_UNLOCK(tdvp);
+ if ((tvp != NULL) && (tvp != tdvp))
+ VOP_UNLOCK(tvp);
+
+ vrele(fvp);
+ if (tvp != NULL)
+ vrele(tvp);
+
+ if (tvp == tdvp) {
+ error = EINVAL;
+ goto out;
+ }
+
+ error = tmpfs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, false);
+
+out: /*
+ * All done, whether with success or failure. Release the
+ * directory nodes now, as the caller expects from the VFS
+ * protocol.
+ */
+ vrele(fdvp);
+ vrele(tdvp);
+
+ return error;
+}
+
+/*
+ * tmpfs_sane_rename: rename routine, the hairiest system call, with
+ * the sane API.
+ *
+ * Arguments:
+ *
+ * . fdvp (from directory vnode),
+ * . fcnp (from component name),
+ * . tdvp (to directory vnode), and
+ * . tcnp (to component name).
+ *
+ * fdvp and tdvp must be referenced and unlocked.
+ */
+static int
+tmpfs_sane_rename(struct vnode *fdvp, struct componentname *fcnp,
+ struct vnode *tdvp, struct componentname *tcnp, kauth_cred_t cred,
+ bool posixly_correct)
+{
+ struct mount *mount;
+ struct tmpfs_mount *tmpfs;
+ struct tmpfs_node *fdnode, *tdnode;
+ struct tmpfs_dirent *fde, *tde;
+ struct vnode *fvp, *tvp;
char *newname;
int error;
- KASSERT(VOP_ISLOCKED(tdvp));
- KASSERT(tvp == NULL || VOP_ISLOCKED(tvp) == LK_EXCLUSIVE);
+ KASSERT(fdvp != NULL);
+ KASSERT(fcnp != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(tcnp != NULL);
+ /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
+ /* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
+ KASSERT(fdvp->v_type == VDIR);
+ KASSERT(tdvp->v_type == VDIR);
+ KASSERT(fdvp->v_mount == tdvp->v_mount);
KASSERT((fcnp->cn_flags & ISDOTDOT) == 0);
KASSERT((tcnp->cn_flags & ISDOTDOT) == 0);
-
- newname = NULL;
- namelen = 0;
- tmp = NULL;
+ KASSERT((fcnp->cn_namelen != 1) || (fcnp->cn_nameptr[0] != '.'));
+ KASSERT((tcnp->cn_namelen != 1) || (tcnp->cn_nameptr[0] != '.'));
+ KASSERT((fcnp->cn_namelen != 2) || (fcnp->cn_nameptr[0] != '.') ||
+ (fcnp->cn_nameptr[1] != '.'));
+ KASSERT((tcnp->cn_namelen != 2) || (tcnp->cn_nameptr[0] != '.') ||
+ (tcnp->cn_nameptr[1] != '.'));
- /* Disallow cross-device renames. */
- if (fvp->v_mount != tdvp->v_mount ||
- (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
- error = EXDEV;
- goto out_unlocked;
- }
+ /*
+ * Pull out the tmpfs data structures.
+ */
+ fdnode = VP_TO_TMPFS_NODE(fdvp);
+ tdnode = VP_TO_TMPFS_NODE(tdvp);
+ KASSERT(fdnode != NULL);
+ KASSERT(tdnode != NULL);
+ KASSERT(fdnode->tn_vnode == fdvp);
+ KASSERT(tdnode->tn_vnode == tdvp);
+ KASSERT(fdnode->tn_type == VDIR);
+ KASSERT(tdnode->tn_type == VDIR);
- fnode = VP_TO_TMPFS_NODE(fvp);
- fdnode = VP_TO_TMPFS_DIR(fdvp);
- tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
- tdnode = VP_TO_TMPFS_DIR(tdvp);
- tmp = VFS_TO_TMPFS(tdvp->v_mount);
+ mount = fdvp->v_mount;
+ KASSERT(mount != NULL);
+ KASSERT(mount == tdvp->v_mount);
+ /* XXX How can we be sure this stays true? (Not that you're
+ * likely to mount a tmpfs read-only...) */
+ KASSERT((mount->mnt_flag & MNT_RDONLY) == 0);
+ tmpfs = VFS_TO_TMPFS(mount);
+ KASSERT(tmpfs != NULL);
- if (fdvp == tvp) {
- error = 0;
- goto out_unlocked;
- }
-
- /* Allocate memory, if necessary, for a new name. */
- namelen = tcnp->cn_namelen;
+ /*
+ * Decide whether we need a new name, and allocate memory for
+ * it if so. Do this before locking anything or taking
+ * destructive actions so that we can back out safely and sleep
+ * safely. XXX Is sleeping an issue here? Can this just be
+ * moved into tmpfs_rename_attachdetach?
+ */
if (tmpfs_strname_neqlen(fcnp, tcnp)) {
- newname = tmpfs_strname_alloc(tmp, namelen);
+ newname = tmpfs_strname_alloc(tmpfs, tcnp->cn_namelen);
if (newname == NULL) {
error = ENOSPC;
goto out_unlocked;
}
+ } else {
+ newname = NULL;
}
- /* XXX: Lock order violation! */
- if (fdvp != tdvp) {
- vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
- }
- if (fvp != tvp) {
- vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
+ /*
+ * Lock and look up everything. GCC is not very clever.
+ */
+ fde = tde = NULL;
+ fvp = tvp = NULL;
+ error = tmpfs_rename_enter(mount, tmpfs, cred,
+ fdvp, fdnode, fcnp, &fde, &fvp,
+ tdvp, tdnode, tcnp, &tde, &tvp);
+ if (error)
Home |
Main Index |
Thread Index |
Old Index