Module Name: src
Committed By: riastradh
Date: Wed May 9 00:16:07 UTC 2012
Modified Files:
src/sys/fs/tmpfs: files.tmpfs tmpfs_vnops.c
src/sys/rump/fs/lib/libtmpfs: Makefile
Added Files:
src/sys/fs/tmpfs: tmpfs_rename.c
Log Message:
Adapt tmpfs_rename to use genfs_rename.
To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/fs/tmpfs/files.tmpfs
cvs rdiff -u -r0 -r1.1 src/sys/fs/tmpfs/tmpfs_rename.c
cvs rdiff -u -r1.96 -r1.97 src/sys/fs/tmpfs/tmpfs_vnops.c
cvs rdiff -u -r1.4 -r1.5 src/sys/rump/fs/lib/libtmpfs/Makefile
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/tmpfs/files.tmpfs
diff -u src/sys/fs/tmpfs/files.tmpfs:1.4 src/sys/fs/tmpfs/files.tmpfs:1.5
--- src/sys/fs/tmpfs/files.tmpfs:1.4 Tue Jun 22 18:32:07 2010
+++ src/sys/fs/tmpfs/files.tmpfs Wed May 9 00:16:07 2012
@@ -1,9 +1,10 @@
-# $NetBSD: files.tmpfs,v 1.4 2010/06/22 18:32:07 rmind Exp $
+# $NetBSD: files.tmpfs,v 1.5 2012/05/09 00:16:07 riastradh Exp $
deffs TMPFS
file fs/tmpfs/tmpfs_fifoops.c tmpfs
file fs/tmpfs/tmpfs_mem.c tmpfs
+file fs/tmpfs/tmpfs_rename.c tmpfs
file fs/tmpfs/tmpfs_specops.c tmpfs
file fs/tmpfs/tmpfs_subr.c tmpfs
file fs/tmpfs/tmpfs_vfsops.c tmpfs
Index: src/sys/fs/tmpfs/tmpfs_vnops.c
diff -u src/sys/fs/tmpfs/tmpfs_vnops.c:1.96 src/sys/fs/tmpfs/tmpfs_vnops.c:1.97
--- src/sys/fs/tmpfs/tmpfs_vnops.c:1.96 Tue Mar 13 18:40:50 2012
+++ src/sys/fs/tmpfs/tmpfs_vnops.c Wed May 9 00:16:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: tmpfs_vnops.c,v 1.96 2012/03/13 18:40:50 elad Exp $ */
+/* $NetBSD: tmpfs_vnops.c,v 1.97 2012/05/09 00:16:07 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.96 2012/03/13 18:40:50 elad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.97 2012/05/09 00:16:07 riastradh Exp $");
#include <sys/param.h>
#include <sys/dirent.h>
@@ -775,1274 +775,6 @@ out:
return error;
}
-/*
- * 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).
- *
- * => Caller holds a reference on fdvp and fvp, they are unlocked.
- * Note: fdvp and fvp can refer to the same object (i.e. when it is root).
- *
- * => 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)
-{
- struct vop_rename_args /* {
- struct vnode *a_fdvp;
- struct vnode *a_fvp;
- struct componentname *a_fcnp;
- struct vnode *a_tdvp;
- struct vnode *a_tvp;
- struct componentname *a_tcnp;
- } */ *ap = v;
- struct vnode *fdvp = ap->a_fdvp;
- struct vnode *fvp = ap->a_fvp;
- struct componentname *fcnp = ap->a_fcnp;
- struct vnode *tdvp = ap->a_tdvp;
- struct vnode *tvp = ap->a_tvp;
- struct componentname *tcnp = ap->a_tcnp;
- 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(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);
- 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] != '.'));
-
- /*
- * 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);
-
- 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);
-
- /*
- * 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(tmpfs, tcnp->cn_namelen);
- if (newname == NULL) {
- error = ENOSPC;
- goto out_unlocked;
- }
- } else {
- newname = NULL;
- }
-
- /*
- * 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)
- goto out_unlocked;
-
- /*
- * Check that everything is locked and looks right.
- */
- KASSERT(fde != NULL);
- KASSERT(fvp != NULL);
- KASSERT(fde->td_node != NULL);
- KASSERT(fde->td_node->tn_vnode == fvp);
- KASSERT(fde->td_node->tn_type == fvp->v_type);
- KASSERT((tde == NULL) == (tvp == NULL));
- KASSERT((tde == NULL) || (tde->td_node != NULL));
- KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp));
- KASSERT((tde == NULL) || (tde->td_node->tn_type == tvp->v_type));
- KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
- KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
-
- /*
- * If the source and destination are the same object, we need
- * only at most delete the source entry.
- */
- if (fvp == tvp) {
- KASSERT(tvp != NULL);
- if (fde->td_node->tn_type == VDIR) {
- /* XXX How can this possibly happen? */
- error = EINVAL;
- goto out_locked;
- }
- if (!posixly_correct && (fde != tde)) {
- /* XXX Doesn't work because of locking.
- * error = VOP_REMOVE(fdvp, fvp);
- */
- error = tmpfs_do_remove(tmpfs, fdvp, fdnode, fde, fvp,
- cred);
- if (error)
- goto out_locked;
- }
- goto success;
- }
- KASSERT(fde != tde);
- KASSERT(fvp != tvp);
-
- /*
- * If the target exists, refuse to rename a directory over a
- * non-directory or vice versa, or to clobber a non-empty
- * directory.
- */
- if (tvp != NULL) {
- KASSERT(tde != NULL);
- KASSERT(tde->td_node != NULL);
- if (fvp->v_type == VDIR && tvp->v_type == VDIR)
- error = ((tde->td_node->tn_size > 0)? ENOTEMPTY : 0);
- else if (fvp->v_type == VDIR && tvp->v_type != VDIR)
- error = ENOTDIR;
- else if (fvp->v_type != VDIR && tvp->v_type == VDIR)
- error = EISDIR;
- else
- error = 0;
- if (error)
- goto out_locked;
- KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
- }
-
- /*
- * Authorize the rename.
- */
- error = tmpfs_rename_check_possible(fdnode, fde->td_node,
- tdnode, (tde? tde->td_node : NULL));
- if (error)
- goto out_locked;
- error = tmpfs_rename_check_permitted(cred, fdnode, fde->td_node,
- tdnode, (tde? tde->td_node : NULL));
- error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, fvp, fdvp,
- error);
- error = kauth_authorize_vnode(cred, KAUTH_VNODE_RENAME, tvp, tdvp,
- error);
- if (error)
- goto out_locked;
-
- /*
- * Everything is hunky-dory. Shuffle the directory entries.
- */
- tmpfs_rename_attachdetach(tmpfs, fdvp, fde, fvp, tdvp, tde, tvp);
-
- /*
- * Update the directory entry's name necessary, and flag
- * metadata updates. A memory allocation failure here is not
- * OK because we've already committed some changes that we
- * can't back out at this point, and we have things locked so
- * we can't sleep, hence the early allocation above.
- */
- if (newname != NULL) {
- KASSERT(tcnp->cn_namelen <= TMPFS_MAXNAMLEN);
-
- tmpfs_strname_free(tmpfs, fde->td_name, fde->td_namelen);
- fde->td_namelen = (uint16_t)tcnp->cn_namelen;
- (void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
- /* Commit newname and don't free it on the way out. */
- fde->td_name = newname;
- newname = NULL;
-
- fde->td_node->tn_status |= TMPFS_NODE_CHANGED;
- tdnode->tn_status |= TMPFS_NODE_MODIFIED;
- }
-
-success:
- VN_KNOTE(fvp, NOTE_RENAME);
- error = 0;
-
-out_locked:
- tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp);
-
-out_unlocked:
- /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
- /* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
- /* KASSERT((fvp == NULL) || (VOP_ISLOCKED(fvp) != LK_EXCLUSIVE)); */
- /* KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) != LK_EXCLUSIVE)); */
-
- if (newname != NULL)
- tmpfs_strname_free(tmpfs, newname, tcnp->cn_namelen);
-
- return error;
-}
-
-/*
- * Look up fcnp in fdnode/fdvp and store its directory entry in fde_ret
- * and the associated vnode in fvp_ret; fail if not found. Look up
- * tcnp in tdnode/tdvp and store its directory entry in tde_ret and the
- * associated vnode in tvp_ret; store null instead if not found. Fail
- * if anything has been mounted on any of the nodes involved.
- *
- * fdvp and tdvp must be referenced.
- *
- * On entry, nothing is locked.
- *
- * On success, everything is locked, and *fvp_ret, and *tvp_ret if
- * nonnull, are referenced. The only pairs of vnodes that may be
- * identical are {fdvp, tdvp} and {fvp, tvp}.
- *
- * On failure, everything remains as was.
- *
- * Locking everything including the source and target nodes is
- * necessary to make sure that, e.g., link count updates are OK. The
- * locking order is, in general, ancestor-first, matching the order you
- * need to use to look up a descendant anyway.
- */
-static int
-tmpfs_rename_enter(struct mount *mount, struct tmpfs_mount *tmpfs,
- kauth_cred_t cred,
- struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp,
- struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
- struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp,
- struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
-{
- int error;
-
- KASSERT(mount != NULL);
- KASSERT(tmpfs != NULL);
- KASSERT(fdvp != NULL);
- KASSERT(fdnode != NULL);
- KASSERT(fcnp != NULL);
- KASSERT(fde_ret != NULL);
- KASSERT(fvp_ret != NULL);
- KASSERT(tdvp != NULL);
- KASSERT(tdnode != NULL);
- KASSERT(tcnp != NULL);
- KASSERT(tde_ret != NULL);
- KASSERT(tvp_ret != NULL);
- KASSERT(fdnode->tn_vnode == fdvp);
- KASSERT(tdnode->tn_vnode == tdvp);
- KASSERT(fdnode->tn_type == VDIR);
- KASSERT(tdnode->tn_type == VDIR);
-
- if (fdvp == tdvp) {
- KASSERT(fdnode == tdnode);
- error = tmpfs_rename_enter_common(mount, tmpfs, cred, fdvp,
- fdnode, fcnp, fde_ret, fvp_ret, tcnp, tde_ret, tvp_ret);
- } else {
- KASSERT(fdnode != tdnode);
- error = tmpfs_rename_enter_separate(mount, tmpfs, cred,
- fdvp, fdnode, fcnp, fde_ret, fvp_ret,
- tdvp, tdnode, tcnp, tde_ret, tvp_ret);
- }
-
- if (error)
- return error;
-
- KASSERT(*fde_ret != NULL);
- KASSERT(*fvp_ret != NULL);
- KASSERT((*tde_ret == NULL) == (*tvp_ret == NULL));
- KASSERT((*tde_ret == NULL) || ((*tde_ret)->td_node != NULL));
- KASSERT((*tde_ret == NULL) ||
- ((*tde_ret)->td_node->tn_vnode == *tvp_ret));
- KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(*fvp_ret) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
- KASSERT((*tvp_ret == NULL) ||
- (VOP_ISLOCKED(*tvp_ret) == LK_EXCLUSIVE));
- KASSERT(*fvp_ret != fdvp);
- KASSERT(*fvp_ret != tdvp);
- KASSERT(*tvp_ret != fdvp);
- KASSERT(*tvp_ret != tdvp);
- return 0;
-}
-
-/*
- * Lock and look up with a common source/target directory.
- */
-static int
-tmpfs_rename_enter_common(struct mount *mount, struct tmpfs_mount *tmpfs,
- kauth_cred_t cred,
- struct vnode *dvp, struct tmpfs_node *dnode,
- struct componentname *fcnp,
- struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
- struct componentname *tcnp,
- struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
-{
- struct tmpfs_dirent *fde, *tde;
- struct vnode *fvp, *tvp;
- int error;
-
- error = tmpfs_rename_lock_directory(dvp, dnode);
- if (error)
- goto fail0;
-
- /* Did we lose a race with mount? */
- if (dvp->v_mountedhere != NULL) {
- error = EBUSY;
- goto fail1;
- }
-
- /* Make sure the caller may read the directory. */
- error = VOP_ACCESS(dvp, VEXEC, cred);
- if (error)
- goto fail1;
-
- /*
- * The order in which we lock the source and target nodes is
- * irrelevant because there can only be one rename on this
- * directory in flight at a time, and we have it locked.
- */
-
- fde = tmpfs_dir_lookup(dnode, fcnp);
- if (fde == NULL) {
- error = ENOENT;
- goto fail1;
- }
-
- KASSERT(fde->td_node != NULL);
- /* We ruled out `.' earlier. */
- KASSERT(fde->td_node != dnode);
- /* We ruled out `..' earlier. */
- KASSERT(fde->td_node != dnode->tn_spec.tn_dir.tn_parent);
- mutex_enter(&fde->td_node->tn_vlock);
- error = tmpfs_vnode_get(mount, fde->td_node, &fvp);
- if (error)
- goto fail1;
- KASSERT(fvp != NULL);
- KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
- KASSERT(fvp != dvp);
- KASSERT(fvp->v_mount == mount);
-
- /* Refuse to rename a mount point. */
- if ((fvp->v_type == VDIR) && (fvp->v_mountedhere != NULL)) {
- error = EBUSY;
- goto fail2;
- }
-
- tde = tmpfs_dir_lookup(dnode, tcnp);
- if (tde == NULL) {
- tvp = NULL;
- } else {
- KASSERT(tde->td_node != NULL);
- /* We ruled out `.' earlier. */
- KASSERT(tde->td_node != dnode);
- /* We ruled out `..' earlier. */
- KASSERT(tde->td_node != dnode->tn_spec.tn_dir.tn_parent);
- if (tde->td_node != fde->td_node) {
- mutex_enter(&tde->td_node->tn_vlock);
- error = tmpfs_vnode_get(mount, tde->td_node, &tvp);
- if (error)
- goto fail2;
- KASSERT(tvp->v_mount == mount);
- /* Refuse to rename over a mount point. */
- if ((tvp->v_type == VDIR) &&
- (tvp->v_mountedhere != NULL)) {
- error = EBUSY;
- goto fail3;
- }
- } else {
- tvp = fvp;
- vref(tvp);
- }
- KASSERT(tvp != NULL);
- KASSERT(VOP_ISLOCKED(tvp) == LK_EXCLUSIVE);
- }
- KASSERT(tvp != dvp);
-
- *fde_ret = fde;
- *fvp_ret = fvp;
- *tde_ret = tde;
- *tvp_ret = tvp;
- return 0;
-
-fail3: if (tvp != NULL) {
- if (tvp != fvp)
- vput(tvp);
- else
- vrele(tvp);
- }
-
-fail2: vput(fvp);
-fail1: VOP_UNLOCK(dvp);
-fail0: return error;
-}
-
-/*
- * Lock and look up with separate source and target directories.
- */
-static int
-tmpfs_rename_enter_separate(struct mount *mount, struct tmpfs_mount *tmpfs,
- kauth_cred_t cred,
- struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp,
- struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
- struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp,
- struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
-{
- struct tmpfs_node *intermediate_node;
- struct tmpfs_dirent *fde, *tde;
- struct vnode *fvp, *tvp;
- int error;
-
- KASSERT(fdvp != tdvp);
- KASSERT(fdnode != tdnode);
-
-#if 0 /* XXX */
- mutex_enter(&tmpfs->tm_rename_lock);
-#endif
-
- error = tmpfs_rename_genealogy(fdnode, tdnode, &intermediate_node);
- if (error)
- goto fail;
-
- /*
- * intermediate_node == NULL means fdnode is not an ancestor of
- * tdnode.
- */
- if (intermediate_node == NULL)
- error = tmpfs_rename_lock(mount, cred, ENOTEMPTY,
- tdvp, tdnode, tcnp, true, &tde, &tvp,
- fdvp, fdnode, fcnp, false, &fde, &fvp);
- else
- error = tmpfs_rename_lock(mount, cred, EINVAL,
- fdvp, fdnode, fcnp, false, &fde, &fvp,
- tdvp, tdnode, tcnp, true, &tde, &tvp);
- if (error)
- goto fail;
-
- KASSERT(fde != NULL);
- KASSERT(fde->td_node != NULL);
-
- /*
- * Reject rename("foo/bar", "foo/bar/baz/quux/zot").
- */
- if (fde->td_node == intermediate_node) {
- tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp);
- return EINVAL;
- }
-
- *fde_ret = fde;
- *fvp_ret = fvp;
- *tde_ret = tde;
- *tvp_ret = tvp;
- return 0;
-
-fail:
-#if 0 /* XXX */
- mutex_exit(&tmpfs->tm_rename_lock);
-#endif
- return error;
-}
-
-/*
- * Unlock everything we locked for rename.
- *
- * fdvp and tdvp must be referenced.
- *
- * On entry, everything is locked, and fvp and tvp referenced.
- *
- * On exit, everything is unlocked, and fvp and tvp are released.
- */
-static void
-tmpfs_rename_exit(struct tmpfs_mount *tmpfs,
- struct vnode *fdvp, struct vnode *fvp,
- struct vnode *tdvp, struct vnode *tvp)
-{
-
- KASSERT(tmpfs != NULL);
- KASSERT(fdvp != NULL);
- KASSERT(fvp != NULL);
- KASSERT(fdvp != fvp);
- KASSERT(fdvp != tvp);
- KASSERT(tdvp != tvp);
- KASSERT(tdvp != fvp);
- KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
- KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
-
- if (tvp != NULL) {
- if (tvp != fvp)
- vput(tvp);
- else
- vrele(tvp);
- }
- VOP_UNLOCK(tdvp);
- vput(fvp);
- if (fdvp != tdvp)
- VOP_UNLOCK(fdvp);
-
-#if 0 /* XXX */
- if (fdvp != tdvp)
- mutex_exit(&tmpfs->tm_rename_lock);
-#endif
-}
-
-/*
- * Lock a directory, but fail if it has been rmdir'd.
- *
- * vp must be referenced.
- */
-static int
-tmpfs_rename_lock_directory(struct vnode *vp, struct tmpfs_node *node)
-{
-
- KASSERT(vp != NULL);
- KASSERT(node != NULL);
- KASSERT(node->tn_vnode == vp);
- KASSERT(node->tn_type == VDIR);
-
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
- if (node->tn_spec.tn_dir.tn_parent == NULL) {
- VOP_UNLOCK(vp);
- return ENOENT;
- }
-
- return 0;
-}
-
-/*
- * Analyze the genealogy of the source and target nodes.
- *
- * On success, stores in *intermediate_node_ret either the child of
- * fdnode of which tdnode is a descendant, or null if tdnode is not a
- * descendant of fdnode at all.
- *
- * fdnode and tdnode must be unlocked and referenced. The file
- * system's rename lock must also be held, to exclude concurrent
- * changes to the file system's genealogy other than rmdir.
- *
- * XXX This causes an extra lock/unlock of tdnode in the case when
- * we're just about to lock it again before locking anything else.
- * However, changing that requires reorganizing the code to make it
- * even more horrifically obscure.
- */
-static int
-tmpfs_rename_genealogy(struct tmpfs_node *fdnode, struct tmpfs_node *tdnode,
- struct tmpfs_node **intermediate_node_ret)
-{
- struct tmpfs_node *node = tdnode, *parent;
- int error;
-
- KASSERT(fdnode != NULL);
- KASSERT(tdnode != NULL);
- KASSERT(fdnode != tdnode);
- KASSERT(intermediate_node_ret != NULL);
-
- KASSERT(fdnode->tn_vnode != NULL);
- KASSERT(tdnode->tn_vnode != NULL);
- KASSERT(fdnode->tn_type == VDIR);
- KASSERT(tdnode->tn_type == VDIR);
-
- /*
- * We need to provisionally lock tdnode->tn_vnode to keep rmdir
- * from deleting it -- or any ancestor -- at an inopportune
- * moment.
- */
- error = tmpfs_rename_lock_directory(tdnode->tn_vnode, tdnode);
- if (error)
- return error;
-
- for (;;) {
- parent = node->tn_spec.tn_dir.tn_parent;
- KASSERT(parent != NULL);
- KASSERT(parent->tn_type == VDIR);
-
- /* Did we hit the root without finding fdnode? */
- if (parent == node) {
- *intermediate_node_ret = NULL;
- break;
- }
-
- /* Did we find that fdnode is an ancestor? */
- if (parent == fdnode) {
- *intermediate_node_ret = node;
- break;
- }
-
- /* Neither -- keep ascending the family tree. */
- node = parent;
- }
-
- VOP_UNLOCK(tdnode->tn_vnode);
- return 0;
-}
-
-/*
- * Lock directories a and b, which must be distinct, and look up and
- * lock nodes a and b. Do a first and then b. Directory b may not be
- * an ancestor of directory a, although directory a may be an ancestor
- * of directory b. Fail with overlap_error if node a is directory b.
- * Neither componentname may be `.' or `..'.
- *
- * a_dvp and b_dvp must be referenced.
- *
- * On entry, a_dvp and b_dvp are unlocked.
- *
- * On success,
- * . a_dvp and b_dvp are locked,
- * . *a_dirent_ret is filled with a directory entry whose node is
- * locked and referenced,
- * . *b_vp_ret is filled with the corresponding vnode,
- * . *b_dirent_ret is filled either with null or with a directory entry
- * whose node is locked and referenced,
- * . *b_vp is filled either with null or with the corresponding vnode,
- * and
- * . the only pair of vnodes that may be identical is a_vp and b_vp.
- *
- * On failure, a_dvp and b_dvp are left unlocked, and *a_dirent_ret,
- * *a_vp, *b_dirent_ret, and *b_vp are left alone.
- */
-static int
-tmpfs_rename_lock(struct mount *mount, kauth_cred_t cred, int overlap_error,
- struct vnode *a_dvp, struct tmpfs_node *a_dnode,
- struct componentname *a_cnp, bool a_missing_ok,
- struct tmpfs_dirent **a_dirent_ret, struct vnode **a_vp_ret,
- struct vnode *b_dvp, struct tmpfs_node *b_dnode,
- struct componentname *b_cnp, bool b_missing_ok,
- struct tmpfs_dirent **b_dirent_ret, struct vnode **b_vp_ret)
-{
- struct tmpfs_dirent *a_dirent, *b_dirent;
- struct vnode *a_vp, *b_vp;
- int error;
-
- KASSERT(a_dvp != NULL);
- KASSERT(a_dnode != NULL);
- KASSERT(a_cnp != NULL);
- KASSERT(a_dirent_ret != NULL);
- KASSERT(a_vp_ret != NULL);
- KASSERT(b_dvp != NULL);
- KASSERT(b_dnode != NULL);
- KASSERT(b_cnp != NULL);
- KASSERT(b_dirent_ret != NULL);
- KASSERT(b_vp_ret != NULL);
- KASSERT(a_dvp != b_dvp);
- KASSERT(a_dnode != b_dnode);
- KASSERT(a_dnode->tn_vnode == a_dvp);
- KASSERT(b_dnode->tn_vnode == b_dvp);
- KASSERT(a_dnode->tn_type == VDIR);
- KASSERT(b_dnode->tn_type == VDIR);
- KASSERT(a_missing_ok != b_missing_ok);
-
- error = tmpfs_rename_lock_directory(a_dvp, a_dnode);
- if (error)
- goto fail0;
-
- /* Did we lose a race with mount? */
- if (a_dvp->v_mountedhere != NULL) {
- error = EBUSY;
- goto fail1;
- }
-
- /* Make sure the caller may read the directory. */
- error = VOP_ACCESS(a_dvp, VEXEC, cred);
- if (error)
- goto fail1;
-
- a_dirent = tmpfs_dir_lookup(a_dnode, a_cnp);
- if (a_dirent != NULL) {
- KASSERT(a_dirent->td_node != NULL);
- /* We ruled out `.' earlier. */
- KASSERT(a_dirent->td_node != a_dnode);
- /* We ruled out `..' earlier. */
- KASSERT(a_dirent->td_node !=
- a_dnode->tn_spec.tn_dir.tn_parent);
- if (a_dirent->td_node == b_dnode) {
- error = overlap_error;
- goto fail1;
- }
- mutex_enter(&a_dirent->td_node->tn_vlock);
- error = tmpfs_vnode_get(mount, a_dirent->td_node, &a_vp);
- if (error)
- goto fail1;
- KASSERT(a_vp->v_mount == mount);
- /* Refuse to rename (over) a mount point. */
- if ((a_vp->v_type == VDIR) && (a_vp->v_mountedhere != NULL)) {
- error = EBUSY;
- goto fail2;
- }
- } else if (!a_missing_ok) {
- error = ENOENT;
- goto fail1;
- } else {
- a_vp = NULL;
- }
- KASSERT(a_vp != a_dvp);
- KASSERT(a_vp != b_dvp);
-
- error = tmpfs_rename_lock_directory(b_dvp, b_dnode);
- if (error)
- goto fail2;
-
- /* Did we lose a race with mount? */
- if (b_dvp->v_mountedhere != NULL) {
- error = EBUSY;
- goto fail3;
- }
-
- /* Make sure the caller may read the directory. */
- error = VOP_ACCESS(b_dvp, VEXEC, cred);
- if (error)
- goto fail3;
-
- b_dirent = tmpfs_dir_lookup(b_dnode, b_cnp);
- if (b_dirent != NULL) {
- KASSERT(b_dirent->td_node != NULL);
- /* We ruled out `.' earlier. */
- KASSERT(b_dirent->td_node != b_dnode);
- /* We ruled out `..' earlier. */
- KASSERT(b_dirent->td_node !=
- b_dnode->tn_spec.tn_dir.tn_parent);
- /* b is not an ancestor of a. */
- KASSERT(b_dirent->td_node != a_dnode);
- /* But the source and target nodes might be the same. */
- if ((a_dirent == NULL) ||
- (a_dirent->td_node != b_dirent->td_node)) {
- mutex_enter(&b_dirent->td_node->tn_vlock);
- error = tmpfs_vnode_get(mount, b_dirent->td_node,
- &b_vp);
- if (error)
- goto fail3;
- KASSERT(b_vp->v_mount == mount);
- KASSERT(a_vp != b_vp);
- /* Refuse to rename (over) a mount point. */
- if ((b_vp->v_type == VDIR) &&
- (b_vp->v_mountedhere != NULL)) {
- error = EBUSY;
- goto fail4;
- }
- } else {
- b_vp = a_vp;
- vref(b_vp);
- }
- } else if (!b_missing_ok) {
- error = ENOENT;
- goto fail3;
- } else {
- b_vp = NULL;
- }
- KASSERT(b_vp != a_dvp);
- KASSERT(b_vp != b_dvp);
-
- KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
- KASSERT(a_missing_ok || (a_dirent != NULL));
- KASSERT(a_missing_ok || (a_dirent->td_node != NULL));
- KASSERT(b_missing_ok || (b_dirent != NULL));
- KASSERT(b_missing_ok || (b_dirent->td_node != NULL));
- KASSERT((a_dirent == NULL) || (a_dirent->td_node != NULL));
- KASSERT((a_dirent == NULL) || (a_dirent->td_node->tn_vnode == a_vp));
- KASSERT((b_dirent == NULL) || (b_dirent->td_node != NULL));
- KASSERT((b_dirent == NULL) || (b_dirent->td_node->tn_vnode == b_vp));
- KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE));
- KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE));
-
- *a_dirent_ret = a_dirent;
- *b_dirent_ret = b_dirent;
- *a_vp_ret = a_vp;
- *b_vp_ret = b_vp;
- return 0;
-
-fail4: if (b_vp != NULL) {
- KASSERT(VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE);
- if (b_vp != a_vp)
- vput(b_vp);
- else
- vrele(a_vp);
- }
-
-fail3: KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
- VOP_UNLOCK(b_dvp);
-
-fail2: if (a_vp != NULL) {
- KASSERT(VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE);
- vput(a_vp);
- }
-
-fail1: KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
- VOP_UNLOCK(a_dvp);
-
-fail0: /* KASSERT(VOP_ISLOCKED(a_dvp) != LK_EXCLUSIVE); */
- /* KASSERT(VOP_ISLOCKED(b_dvp) != LK_EXCLUSIVE); */
- /* KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) != LK_EXCLUSIVE)); */
- /* KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) != LK_EXCLUSIVE)); */
- return error;
-}
-
-/*
- * Shuffle the directory entries to move fvp from the directory fdvp
- * into the directory tdvp. fde is fvp's directory entry in fdvp. If
- * we are overwriting a target node, it is tvp, and tde is its
- * directory entry in tdvp.
- *
- * fdvp, fvp, tdvp, and tvp must all be locked and referenced.
- */
-static void
-tmpfs_rename_attachdetach(struct tmpfs_mount *tmpfs,
- struct vnode *fdvp, struct tmpfs_dirent *fde, struct vnode *fvp,
- struct vnode *tdvp, struct tmpfs_dirent *tde, struct vnode *tvp)
-{
-
- KASSERT(tmpfs != NULL);
- KASSERT(fdvp != NULL);
- KASSERT(fde != NULL);
- KASSERT(fvp != NULL);
- KASSERT(tdvp != NULL);
- KASSERT(fde->td_node != NULL);
- KASSERT(fde->td_node->tn_vnode == fvp);
- KASSERT((tde == NULL) == (tvp == NULL));
- KASSERT((tde == NULL) || (tde->td_node != NULL));
- KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp));
- KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
- KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
-
- /*
- * If we are moving from one directory to another, detach the
- * source entry and reattach it to the target directory.
- */
- if (fdvp != tdvp) {
- /* tmpfs_dir_detach clobbers fde->td_node, so save it. */
- struct tmpfs_node *fnode = fde->td_node;
- tmpfs_dir_detach(fdvp, fde);
- tmpfs_dir_attach(tdvp, fde, fnode);
- } else if (tvp == NULL) {
- /*
- * We are changing the directory. tmpfs_dir_attach and
- * tmpfs_dir_detach note the events for us, but for
- * this case we don't call them, so we must note the
- * event explicitly.
- */
- VN_KNOTE(fdvp, NOTE_WRITE);
- }
-
- /*
- * If we are replacing an existing target entry, delete it.
- */
- if (tde != NULL) {
- KASSERT(tvp != NULL);
- KASSERT(tde->td_node != NULL);
- KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
- if (tde->td_node->tn_type == VDIR) {
- KASSERT(tde->td_node->tn_size == 0);
- KASSERT(tde->td_node->tn_links == 2);
- /* Decrement the extra link count for `.' so
- * the vnode will be recycled when released. */
- tde->td_node->tn_links--;
- }
- tmpfs_dir_detach(tdvp, tde);
- tmpfs_free_dirent(tmpfs, tde);
- }
-}
-
-/*
- * Remove the entry de for the non-directory vp from the directory dvp.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_do_remove(struct tmpfs_mount *tmpfs, struct vnode *dvp,
- struct tmpfs_node *dnode, struct tmpfs_dirent *de, struct vnode *vp,
- kauth_cred_t cred)
-{
- int error;
-
- KASSERT(tmpfs != NULL);
- KASSERT(dvp != NULL);
- KASSERT(dnode != NULL);
- KASSERT(de != NULL);
- KASSERT(vp != NULL);
- KASSERT(dnode->tn_vnode == dvp);
- KASSERT(de->td_node != NULL);
- KASSERT(de->td_node->tn_vnode == vp);
- KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
-
- error = tmpfs_remove_check_possible(dnode, de->td_node);
- if (error)
- return error;
-
- error = tmpfs_remove_check_permitted(cred, dnode, de->td_node);
- error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, vp, dvp,
- error);
- if (error)
- return error;
-
- tmpfs_dir_detach(dvp, de);
- tmpfs_free_dirent(tmpfs, de);
-
- return 0;
-}
-
-/*
- * Check whether a rename is possible independent of credentials.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_rename_check_possible(
- struct tmpfs_node *fdnode, struct tmpfs_node *fnode,
- struct tmpfs_node *tdnode, struct tmpfs_node *tnode)
-{
-
- KASSERT(fdnode != NULL);
- KASSERT(fnode != NULL);
- KASSERT(tdnode != NULL);
- KASSERT(fdnode != fnode);
- KASSERT(tdnode != tnode);
- KASSERT(fnode != tnode);
- KASSERT(fdnode->tn_vnode != NULL);
- KASSERT(fnode->tn_vnode != NULL);
- KASSERT(tdnode->tn_vnode != NULL);
- KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL));
- KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT((tnode == NULL) ||
- (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE));
-
- /*
- * If fdnode is immutable, we can't write to it. If fdnode is
- * append-only, the only change we can make is to add entries
- * to it. If fnode is immutable, we can't change the links to
- * it. If fnode is append-only...well, this is what UFS does.
- */
- if ((fdnode->tn_flags | fnode->tn_flags) & (IMMUTABLE | APPEND))
- return EPERM;
-
- /*
- * If tdnode is immutable, we can't write to it. If tdnode is
- * append-only, we can add entries, but we can't change
- * existing entries.
- */
- if (tdnode->tn_flags & (IMMUTABLE | (tnode? APPEND : 0)))
- return EPERM;
-
- /*
- * If tnode is immutable, we can't replace links to it. If
- * tnode is append-only...well, this is what UFS does.
- */
- if (tnode != NULL) {
- KASSERT(tnode != NULL);
- if ((tnode->tn_flags & (IMMUTABLE | APPEND)) != 0)
- return EPERM;
- }
-
- return 0;
-}
-
-/*
- * Check whether a rename is permitted given our credentials.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_rename_check_permitted(kauth_cred_t cred,
- struct tmpfs_node *fdnode, struct tmpfs_node *fnode,
- struct tmpfs_node *tdnode, struct tmpfs_node *tnode)
-{
- int error;
-
- KASSERT(fdnode != NULL);
- KASSERT(fnode != NULL);
- KASSERT(tdnode != NULL);
- KASSERT(fdnode != fnode);
- KASSERT(tdnode != tnode);
- KASSERT(fnode != tnode);
- KASSERT(fdnode->tn_vnode != NULL);
- KASSERT(fnode->tn_vnode != NULL);
- KASSERT(tdnode->tn_vnode != NULL);
- KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL));
- KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT((tnode == NULL) ||
- (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE));
-
- /*
- * We need to remove or change an entry in the source directory.
- */
- error = VOP_ACCESS(fdnode->tn_vnode, VWRITE, cred);
- if (error)
- return error;
-
- /*
- * If we are changing directories, then we need to write to the
- * target directory to add or change an entry. Also, if fnode
- * is a directory, we need to write to it to change its `..'
- * entry.
- */
- if (fdnode != tdnode) {
- error = VOP_ACCESS(tdnode->tn_vnode, VWRITE, cred);
- if (error)
- return error;
- if (fnode->tn_type == VDIR) {
- error = VOP_ACCESS(fnode->tn_vnode, VWRITE, cred);
- if (error)
- return error;
- }
- }
-
- error = tmpfs_check_sticky(cred, fdnode, fnode);
- if (error)
- return error;
-
- error = tmpfs_check_sticky(cred, tdnode, tnode);
- if (error)
- return error;
-
- return 0;
-}
-
-/*
- * Check whether removing node's entry in dnode is possible independent
- * of credentials.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_remove_check_possible(struct tmpfs_node *dnode, struct tmpfs_node *node)
-{
-
- KASSERT(dnode != NULL);
- KASSERT(dnode->tn_vnode != NULL);
- KASSERT(node != NULL);
- KASSERT(dnode != node);
- KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE);
-
- /*
- * We want to delete the entry. If dnode is immutable, we
- * can't write to it to delete the entry. If dnode is
- * append-only, the only change we can make is to add entries,
- * so we can't delete entries. If node is immutable, we can't
- * change the links to it, so we can't delete the entry. If
- * node is append-only...well, this is what UFS does.
- */
- if ((dnode->tn_flags | node->tn_flags) & (IMMUTABLE | APPEND))
- return EPERM;
-
- return 0;
-}
-
-/*
- * Check whether removing node's entry in dnode is permitted given our
- * credentials.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_remove_check_permitted(kauth_cred_t cred,
- struct tmpfs_node *dnode, struct tmpfs_node *node)
-{
- int error;
-
- KASSERT(dnode != NULL);
- KASSERT(dnode->tn_vnode != NULL);
- KASSERT(node != NULL);
- KASSERT(dnode != node);
- KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE);
-
- /*
- * Check whether we are permitted to write to the source
- * directory in order to delete an entry from it.
- */
- error = VOP_ACCESS(dnode->tn_vnode, VWRITE, cred);
- if (error)
- return error;
-
- error = tmpfs_check_sticky(cred, dnode, node);
- if (error)
- return error;
-
- return 0;
-}
-
-/*
- * Check whether we may change an entry in a sticky directory. If the
- * directory is sticky, the user must own either the directory or, if
- * it exists, the node, in order to change the entry.
- *
- * Everything must be locked and referenced.
- */
-static int
-tmpfs_check_sticky(kauth_cred_t cred,
- struct tmpfs_node *dnode, struct tmpfs_node *node)
-{
-
- KASSERT(dnode != NULL);
- KASSERT(dnode->tn_vnode != NULL);
- KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
- KASSERT((node == NULL) || (node->tn_vnode != NULL));
- KASSERT((node == NULL) ||
- (VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE));
-
- if (node == NULL)
- return 0;
-
- if (dnode->tn_mode & S_ISTXT) {
- if (kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE,
- node->tn_vnode, dnode->tn_vnode, genfs_can_sticky(cred,
- dnode->tn_uid, node->tn_uid)) != 0)
- return EPERM;
- }
-
- return 0;
-}
-
int
tmpfs_mkdir(void *v)
{
Index: src/sys/rump/fs/lib/libtmpfs/Makefile
diff -u src/sys/rump/fs/lib/libtmpfs/Makefile:1.4 src/sys/rump/fs/lib/libtmpfs/Makefile:1.5
--- src/sys/rump/fs/lib/libtmpfs/Makefile:1.4 Tue Jun 22 18:32:08 2010
+++ src/sys/rump/fs/lib/libtmpfs/Makefile Wed May 9 00:16:07 2012
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.4 2010/06/22 18:32:08 rmind Exp $
+# $NetBSD: Makefile,v 1.5 2012/05/09 00:16:07 riastradh Exp $
#
.PATH: ${.CURDIR}/../../../../fs/tmpfs
@@ -6,7 +6,7 @@
LIB= rumpfs_tmpfs
SRCS= tmpfs_fifoops.c tmpfs_specops.c tmpfs_vfsops.c tmpfs_mem.c \
- tmpfs_subr.c tmpfs_vnops.c
+ tmpfs_rename.c tmpfs_subr.c tmpfs_vnops.c
.include <bsd.lib.mk>
.include <bsd.klinks.mk>
Added files:
Index: src/sys/fs/tmpfs/tmpfs_rename.c
diff -u /dev/null src/sys/fs/tmpfs/tmpfs_rename.c:1.1
--- /dev/null Wed May 9 00:16:08 2012
+++ src/sys/fs/tmpfs/tmpfs_rename.c Wed May 9 00:16:07 2012
@@ -0,0 +1,589 @@
+/* $NetBSD: tmpfs_rename.c,v 1.1 2012/05/09 00:16:07 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * tmpfs rename
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.1 2012/05/09 00:16:07 riastradh Exp $");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/kauth.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/vnode_if.h>
+
+#include <miscfs/genfs/genfs.h>
+
+#include <fs/tmpfs/tmpfs_vnops.h>
+#include <fs/tmpfs/tmpfs.h>
+
+/*
+ * Forward declarations
+ */
+
+static int tmpfs_sane_rename(struct vnode *, struct componentname *,
+ struct vnode *, struct componentname *,
+ kauth_cred_t, bool);
+static bool tmpfs_rmdired_p(struct vnode *);
+static int tmpfs_gro_lock_directory(struct mount *, struct vnode *);
+
+static const struct genfs_rename_ops tmpfs_genfs_rename_ops;
+
+/*
+ * tmpfs_sane_rename: The hairiest vop, with the saner API.
+ *
+ * Arguments:
+ *
+ * . fdvp (from directory vnode),
+ * . fcnp (from component name),
+ * . tdvp (to directory vnode),
+ * . tcnp (to component name),
+ * . cred (credentials structure), and
+ * . posixly_correct (flag for behaviour if target & source link same file).
+ *
+ * fdvp and tdvp may be the same, and 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 tmpfs_dirent *fdirent, *tdirent;
+
+ return genfs_sane_rename(&tmpfs_genfs_rename_ops,
+ fdvp, fcnp, &fdirent, tdvp, tcnp, &tdirent,
+ cred, posixly_correct);
+}
+
+/*
+ * tmpfs_rename: The hairiest vop, with the insanest API. Defer to
+ * genfs_insane_rename immediately.
+ */
+int
+tmpfs_rename(void *v)
+{
+
+ return genfs_insane_rename(v, &tmpfs_sane_rename);
+}
+
+/*
+ * tmpfs_gro_directory_empty_p: Return true if the directory vp is
+ * empty. dvp is its parent.
+ *
+ * vp and dvp must be locked and referenced.
+ */
+static bool
+tmpfs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
+ struct vnode *vp, struct vnode *dvp)
+{
+
+ (void)mp;
+ (void)cred;
+ (void)dvp;
+ KASSERT(mp != NULL);
+ KASSERT(vp != NULL);
+ KASSERT(dvp != NULL);
+ KASSERT(vp != dvp);
+ KASSERT(vp->v_mount == mp);
+ KASSERT(dvp->v_mount == mp);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+
+ return (VP_TO_TMPFS_NODE(vp)->tn_size == 0);
+}
+
+/*
+ * tmpfs_gro_rename_check_possible: Check whether a rename is possible
+ * independent of credentials.
+ */
+static int
+tmpfs_gro_rename_check_possible(struct mount *mp,
+ struct vnode *fdvp, struct vnode *fvp,
+ struct vnode *tdvp, struct vnode *tvp)
+{
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(fdvp != NULL);
+ KASSERT(fvp != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(fdvp != fvp);
+ KASSERT(fdvp != tvp);
+ KASSERT(tdvp != fvp);
+ KASSERT(tdvp != tvp);
+ KASSERT(fvp != tvp);
+ KASSERT(fdvp->v_type == VDIR);
+ KASSERT(tdvp->v_type == VDIR);
+ KASSERT(fdvp->v_mount == mp);
+ KASSERT(fvp->v_mount == mp);
+ KASSERT(tdvp->v_mount == mp);
+ KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+ 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));
+
+ return genfs_ufslike_rename_check_possible(
+ VP_TO_TMPFS_NODE(fdvp)->tn_flags, VP_TO_TMPFS_NODE(fvp)->tn_flags,
+ VP_TO_TMPFS_NODE(tdvp)->tn_flags,
+ (tvp? VP_TO_TMPFS_NODE(tvp)->tn_flags : 0), (tvp != NULL),
+ IMMUTABLE, APPEND);
+}
+
+/*
+ * tmpfs_gro_rename_check_permitted: Check whether a rename is
+ * permitted given our credentials.
+ */
+static int
+tmpfs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
+ struct vnode *fdvp, struct vnode *fvp,
+ struct vnode *tdvp, struct vnode *tvp)
+{
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(fdvp != NULL);
+ KASSERT(fvp != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(fdvp != fvp);
+ KASSERT(fdvp != tvp);
+ KASSERT(tdvp != fvp);
+ KASSERT(tdvp != tvp);
+ KASSERT(fvp != tvp);
+ KASSERT(fdvp->v_type == VDIR);
+ KASSERT(tdvp->v_type == VDIR);
+ KASSERT(fdvp->v_mount == mp);
+ KASSERT(fvp->v_mount == mp);
+ KASSERT(tdvp->v_mount == mp);
+ KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+ 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));
+
+ return genfs_ufslike_rename_check_permitted(cred,
+ fdvp, VP_TO_TMPFS_NODE(fdvp)->tn_mode,
+ VP_TO_TMPFS_NODE(fdvp)->tn_uid,
+ fvp, VP_TO_TMPFS_NODE(fvp)->tn_uid,
+ tdvp, VP_TO_TMPFS_NODE(tdvp)->tn_mode,
+ VP_TO_TMPFS_NODE(tdvp)->tn_uid,
+ tvp, (tvp? VP_TO_TMPFS_NODE(tvp)->tn_uid : 0));
+}
+
+/*
+ * tmpfs_gro_remove_check_possible: Check whether a remove is possible
+ * independent of credentials.
+ */
+static int
+tmpfs_gro_remove_check_possible(struct mount *mp,
+ struct vnode *dvp, struct vnode *vp)
+{
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(dvp != NULL);
+ KASSERT(vp != NULL);
+ KASSERT(dvp != vp);
+ KASSERT(dvp->v_type == VDIR);
+ KASSERT(vp->v_type != VDIR);
+ KASSERT(dvp->v_mount == mp);
+ KASSERT(vp->v_mount == mp);
+ KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+ return genfs_ufslike_remove_check_possible(
+ VP_TO_TMPFS_NODE(dvp)->tn_flags, VP_TO_TMPFS_NODE(vp)->tn_flags,
+ IMMUTABLE, APPEND);
+}
+
+/*
+ * tmpfs_gro_remove_check_permitted: Check whether a remove is
+ * permitted given our credentials.
+ */
+static int
+tmpfs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
+ struct vnode *dvp, struct vnode *vp)
+{
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(dvp != NULL);
+ KASSERT(vp != NULL);
+ KASSERT(dvp != vp);
+ KASSERT(dvp->v_type == VDIR);
+ KASSERT(vp->v_type != VDIR);
+ KASSERT(dvp->v_mount == mp);
+ KASSERT(vp->v_mount == mp);
+ KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+ return genfs_ufslike_remove_check_permitted(cred,
+ dvp, VP_TO_TMPFS_NODE(dvp)->tn_mode, VP_TO_TMPFS_NODE(dvp)->tn_uid,
+ vp, VP_TO_TMPFS_NODE(vp)->tn_uid);
+}
+
+/*
+ * tmpfs_gro_rename: Actually perform the rename operation.
+ */
+static int
+tmpfs_gro_rename(struct mount *mp, kauth_cred_t cred,
+ struct vnode *fdvp, struct componentname *fcnp,
+ void *fde, struct vnode *fvp,
+ struct vnode *tdvp, struct componentname *tcnp,
+ void *tde, struct vnode *tvp)
+{
+ struct tmpfs_dirent **fdep = fde;
+ struct tmpfs_dirent **tdep = tde;
+ char *newname;
+
+ (void)cred;
+ KASSERT(mp != NULL);
+ KASSERT(fdvp != NULL);
+ KASSERT(fcnp != NULL);
+ KASSERT(fdep != NULL);
+ KASSERT(fvp != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(tcnp != NULL);
+ KASSERT(tdep != NULL);
+ KASSERT(fdep != tdep);
+ KASSERT((*fdep) != (*tdep));
+ KASSERT((*fdep) != NULL);
+ KASSERT((*fdep)->td_node == VP_TO_TMPFS_NODE(fvp));
+ KASSERT((tvp == NULL) || ((*tdep) != NULL));
+ KASSERT((tvp == NULL) || ((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp)));
+ KASSERT(fdvp != fvp);
+ KASSERT(fdvp != tvp);
+ KASSERT(tdvp != fvp);
+ KASSERT(tdvp != tvp);
+ KASSERT(fvp != tvp);
+ KASSERT(fdvp->v_mount == mp);
+ KASSERT(fvp->v_mount == mp);
+ KASSERT(tdvp->v_mount == mp);
+ KASSERT((tvp == NULL) || (tvp->v_mount == mp));
+ 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));
+
+ if (tmpfs_strname_neqlen(fcnp, tcnp)) {
+ newname = tmpfs_strname_alloc(VFS_TO_TMPFS(mp),
+ tcnp->cn_namelen);
+ if (newname == NULL)
+ return ENOSPC;
+ } else {
+ newname = NULL;
+ }
+
+ /*
+ * If we are moving from one directory to another, detach the
+ * source entry and reattach it to the target directory.
+ */
+ if (fdvp != tdvp) {
+ tmpfs_dir_detach(fdvp, *fdep);
+ tmpfs_dir_attach(tdvp, *fdep, VP_TO_TMPFS_NODE(fvp));
+ } else if (tvp == NULL) {
+ /*
+ * We are changing the directory. tmpfs_dir_attach and
+ * tmpfs_dir_detach note the events for us, but for
+ * this case we don't call them, so we must note the
+ * event explicitly.
+ */
+ VN_KNOTE(fdvp, NOTE_WRITE);
+ }
+
+ /*
+ * If we are replacing an existing target entry, delete it.
+ *
+ * XXX What if the target is a directory with whiteout entries?
+ */
+ if (tvp != NULL) {
+ KASSERT((*tdep) != NULL);
+ KASSERT((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp));
+ KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
+ if (tvp->v_type == VDIR) {
+ KASSERT(VP_TO_TMPFS_NODE(tvp)->tn_size == 0);
+ KASSERT(VP_TO_TMPFS_NODE(tvp)->tn_links == 2);
+
+ /*
+ * Decrement the extra link count for `.' so
+ * the vnode will be recycled when released.
+ *
+ * XXX Why not just release directory vnodes
+ * when their link count is 1 instead of 0 in
+ * tmpfs_inactive, since `.' is being treated
+ * specially anyway?
+ */
+ VP_TO_TMPFS_NODE(tvp)->tn_links--;
+ }
+ tmpfs_dir_detach(tdvp, *tdep);
+ tmpfs_free_dirent(VFS_TO_TMPFS(mp), *tdep);
+ }
+
+ /*
+ * Update the directory entry's name if necessary, and flag
+ * metadata updates. A memory allocation failure here is not
+ * OK because we've already committed some changes that we
+ * can't back out at this point, hence the early allocation
+ * above.
+ */
+ if (newname != NULL) {
+ KASSERT(tcnp->cn_namelen <= TMPFS_MAXNAMLEN);
+
+ tmpfs_strname_free(VFS_TO_TMPFS(mp), (*fdep)->td_name,
+ (*fdep)->td_namelen);
+ (*fdep)->td_namelen = (uint16_t)tcnp->cn_namelen;
+ (void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
+ (*fdep)->td_name = newname;
+
+ VP_TO_TMPFS_NODE(fvp)->tn_status |= TMPFS_NODE_CHANGED;
+ VP_TO_TMPFS_NODE(tdvp)->tn_status |= TMPFS_NODE_MODIFIED;
+ }
+
+#if 0 /* XXX */
+ genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
+#endif
+
+ return 0;
+}
+
+/*
+ * tmpfs_gro_remove: Rename an object over another link to itself,
+ * effectively removing just the original link.
+ */
+static int
+tmpfs_gro_remove(struct mount *mp, kauth_cred_t cred,
+ struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp)
+{
+ struct tmpfs_dirent **dep = de;
+
+ (void)vp;
+ KASSERT(mp != NULL);
+ KASSERT(dvp != NULL);
+ KASSERT(cnp != NULL);
+ KASSERT(dep != NULL);
+ KASSERT(vp != NULL);
+ KASSERT(dvp != vp);
+ KASSERT(dvp->v_mount == mp);
+ KASSERT(vp->v_mount == mp);
+ KASSERT(dvp->v_type == VDIR);
+ KASSERT(vp->v_type != VDIR);
+ KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+ tmpfs_dir_detach(dvp, *dep);
+ tmpfs_free_dirent(VFS_TO_TMPFS(mp), *dep);
+
+ return 0;
+}
+
+/*
+ * tmpfs_gro_lookup: Look up and save the lookup results.
+ */
+static int
+tmpfs_gro_lookup(struct mount *mp, struct vnode *dvp,
+ struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
+{
+ struct tmpfs_dirent *dirent, **dep_ret = de_ret;
+ struct vnode *vp;
+ int error;
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(dvp != NULL);
+ KASSERT(cnp != NULL);
+ KASSERT(dep_ret != NULL);
+ KASSERT(vp_ret != NULL);
+ KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+
+ dirent = tmpfs_dir_lookup(VP_TO_TMPFS_NODE(dvp), cnp);
+ if (dirent == NULL)
+ return ENOENT;
+
+ mutex_enter(&dirent->td_node->tn_vlock);
+ error = tmpfs_vnode_get(mp, dirent->td_node, &vp);
+ /* Note: tmpfs_vnode_get always releases dirent->td_node->tn_vlock. */
+ if (error)
+ return error;
+
+ KASSERT(vp != NULL);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+ /*
+ * tmpfs_vnode_get returns this locked for us, which would be
+ * convenient but for lossage in other file systems. So, for
+ * hysterical raisins, we have to unlock it here. The caller
+ * will lock it again, and we don't rely on any interesting
+ * invariants in the interim, beyond that it won't vanish from
+ * under us, which it won't because it's referenced.
+ *
+ * XXX Once namei is fixed, we can change the genfs_rename
+ * protocol so that this unlock is not necessary.
+ */
+ VOP_UNLOCK(vp);
+
+ *dep_ret = dirent;
+ *vp_ret = vp;
+ return 0;
+}
+
+/*
+ * tmpfs_rmdired_p: Check whether the directory vp has been rmdired.
+ *
+ * vp must be locked and referenced.
+ */
+static bool
+tmpfs_rmdired_p(struct vnode *vp)
+{
+
+ KASSERT(vp != NULL);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ KASSERT(vp->v_type == VDIR);
+
+ return (VP_TO_TMPFS_NODE(vp)->tn_spec.tn_dir.tn_parent == NULL);
+}
+
+/*
+ * tmpfs_gro_genealogy: Analyze the genealogy of the source and target
+ * directories.
+ */
+static int
+tmpfs_gro_genealogy(struct mount *mp, kauth_cred_t cred,
+ struct vnode *fdvp, struct vnode *tdvp,
+ struct vnode **intermediate_node_ret)
+{
+ struct vnode *vp;
+ struct tmpfs_node *dnode;
+ int error;
+
+ (void)cred;
+ KASSERT(mp != NULL);
+ KASSERT(fdvp != NULL);
+ KASSERT(tdvp != NULL);
+ KASSERT(fdvp != tdvp);
+ KASSERT(intermediate_node_ret != NULL);
+ KASSERT(fdvp->v_mount == mp);
+ KASSERT(tdvp->v_mount == mp);
+ KASSERT(fdvp->v_type == VDIR);
+ KASSERT(tdvp->v_type == VDIR);
+
+ /*
+ * We need to provisionally lock tdvp to keep rmdir from
+ * deleting it -- or any ancestor -- at an inopportune moment.
+ */
+ error = tmpfs_gro_lock_directory(mp, tdvp);
+ if (error)
+ return error;
+
+ vp = tdvp;
+ vref(vp);
+
+ for (;;) {
+ KASSERT(vp != NULL);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ KASSERT(vp->v_mount == mp);
+ KASSERT(vp->v_type == VDIR);
+ KASSERT(!tmpfs_rmdired_p(vp));
+
+ dnode = VP_TO_TMPFS_NODE(vp)->tn_spec.tn_dir.tn_parent;
+
+ /*
+ * If dnode is null then vp has been rmdir'd, which is
+ * not supposed to happen because we have it locked.
+ */
+ KASSERT(dnode != NULL);
+
+ /* Did we hit the root without finding fdvp? */
+ if (dnode == VP_TO_TMPFS_NODE(vp)) {
+ vput(vp);
+ *intermediate_node_ret = NULL;
+ return 0;
+ }
+
+ /* Did we find that fdvp is an ancestor of tdvp? */
+ if (dnode == VP_TO_TMPFS_NODE(fdvp)) {
+ KASSERT(dnode->tn_vnode == fdvp);
+ /* Unlock vp, but keep it referenced. */
+ VOP_UNLOCK(vp);
+ *intermediate_node_ret = vp;
+ return 0;
+ }
+
+ /* Neither -- keep ascending the family tree. */
+ mutex_enter(&dnode->tn_vlock);
+ vput(vp);
+ vp = NULL; /* Just in case, for the kassert above... */
+ error = tmpfs_vnode_get(mp, dnode, &vp);
+ if (error)
+ return error;
+ }
+}
+
+/*
+ * tmpfs_gro_lock_directory: Lock the directory vp, but fail if it has
+ * been rmdir'd.
+ */
+static int
+tmpfs_gro_lock_directory(struct mount *mp, struct vnode *vp)
+{
+
+ (void)mp;
+ KASSERT(mp != NULL);
+ KASSERT(vp != NULL);
+ KASSERT(vp->v_mount == mp);
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+ if (tmpfs_rmdired_p(vp)) {
+ VOP_UNLOCK(vp);
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+static const struct genfs_rename_ops tmpfs_genfs_rename_ops = {
+ .gro_directory_empty_p = tmpfs_gro_directory_empty_p,
+ .gro_rename_check_possible = tmpfs_gro_rename_check_possible,
+ .gro_rename_check_permitted = tmpfs_gro_rename_check_permitted,
+ .gro_remove_check_possible = tmpfs_gro_remove_check_possible,
+ .gro_remove_check_permitted = tmpfs_gro_remove_check_permitted,
+ .gro_rename = tmpfs_gro_rename,
+ .gro_remove = tmpfs_gro_remove,
+ .gro_lookup = tmpfs_gro_lookup,
+ .gro_genealogy = tmpfs_gro_genealogy,
+ .gro_lock_directory = tmpfs_gro_lock_directory,
+};