Module Name:    src
Committed By:   hannken
Date:           Sat Oct 23 07:45:03 UTC 2021

Modified Files:
        src/sys/fs/msdosfs: denode.h msdosfs_rename.c

Log Message:
Convert msdosfs_rename() to use genfs_sane_rename().

Based on work by Taylor R Campbell.


To generate a diff of this commit:
cvs rdiff -u -r1.27 -r1.28 src/sys/fs/msdosfs/denode.h
cvs rdiff -u -r1.1 -r1.2 src/sys/fs/msdosfs/msdosfs_rename.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.27 src/sys/fs/msdosfs/denode.h:1.28
--- src/sys/fs/msdosfs/denode.h:1.27	Sat Oct 23 07:38:33 2021
+++ src/sys/fs/msdosfs/denode.h	Sat Oct 23 07:45:03 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: denode.h,v 1.27 2021/10/23 07:38:33 hannken Exp $	*/
+/*	$NetBSD: denode.h,v 1.28 2021/10/23 07:45:03 hannken Exp $	*/
 
 /*-
  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
@@ -318,7 +318,6 @@ int deget(struct msdosfsmount *, u_long,
 #endif
 int detrunc(struct denode *, u_long, int, struct kauth_cred *);
 int deupdat(struct denode *, int);
-int doscheckpath(struct denode *, struct denode *);
 int dosdirempty(struct denode *);
 int readde(struct denode *, struct buf **, struct direntry **);
 int readep(struct msdosfsmount *, u_long, u_long,

Index: src/sys/fs/msdosfs/msdosfs_rename.c
diff -u src/sys/fs/msdosfs/msdosfs_rename.c:1.1 src/sys/fs/msdosfs/msdosfs_rename.c:1.2
--- src/sys/fs/msdosfs/msdosfs_rename.c:1.1	Sat Oct 23 07:41:37 2021
+++ src/sys/fs/msdosfs/msdosfs_rename.c	Sat Oct 23 07:45:03 2021
@@ -1,10 +1,11 @@
-/*	$NetBSD: msdosfs_rename.c,v 1.1 2021/10/23 07:41:37 hannken Exp $	*/
+/*	$NetBSD: msdosfs_rename.c,v 1.2 2021/10/23 07:45:03 hannken Exp $	*/
 
 /*-
- * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
- * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
  * All rights reserved.
- * Original code by Paul Popelka (pa...@uts.amdahl.com) (see below).
+ *
+ * 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
@@ -14,60 +15,36 @@
  * 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.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by TooLs GmbH.
- * 4. The name of TooLs GmbH may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
- */
-/*
- * Written by Paul Popelka (pa...@uts.amdahl.com)
- *
- * You can do anything you want with this software, just don't say you wrote
- * it, and don't remove this notice.
- *
- * This software is provided "as is".
- *
- * The author supplies this software to be publicly redistributed on the
- * understanding that the author is not responsible for the correct
- * functioning of this software in any circumstances and is not liable for
- * any damages caused by this software.
  *
- * October 1992
+ * 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.
  */
 
+/*
+ * MS-DOS FS Rename
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_rename.c,v 1.2 2021/10/23 07:45:03 hannken Exp $");
+
 #include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/namei.h>
-#include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
-#include <sys/kernel.h>
-#include <sys/file.h>		/* define FWRITE ... */
-#include <sys/stat.h>
 #include <sys/buf.h>
-#include <sys/proc.h>
-#include <sys/mount.h>
-#include <sys/vnode.h>
-#include <sys/signalvar.h>
-#include <sys/malloc.h>
-#include <sys/dirent.h>
-#include <sys/lockf.h>
+#include <sys/errno.h>
 #include <sys/kauth.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/vnode_if.h>
 
 #include <miscfs/genfs/genfs.h>
-#include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
-
-#include <uvm/uvm_extern.h>
 
 #include <fs/msdosfs/bpb.h>
 #include <fs/msdosfs/direntry.h>
@@ -75,10 +52,50 @@
 #include <fs/msdosfs/msdosfsmount.h>
 #include <fs/msdosfs/fat.h>
 
+/*
+ * Forward declarations
+ */
+
+static int msdosfs_sane_rename(struct vnode *, struct componentname *,
+    struct vnode *, struct componentname *,
+    kauth_cred_t, bool);
+static bool msdosfs_rmdired_p(struct vnode *);
+static int msdosfs_read_dotdot(struct vnode *, kauth_cred_t, unsigned long *);
+static int msdosfs_rename_replace_dotdot(struct vnode *,
+    struct vnode *, struct vnode *, kauth_cred_t);
+static int msdosfs_gro_lock_directory(struct mount *, struct vnode *);
+
+static const struct genfs_rename_ops msdosfs_genfs_rename_ops;
+
+/*
+ * msdosfs_rename: The hairiest vop, with the insanest API.
+ *
+ * Arguments:
+ *
+ * . fdvp (from directory vnode),
+ * . fvp (from vnode),
+ * . fcnp (from component name),
+ * . tdvp (to directory vnode),
+ * . tvp (to vnode, or NULL), and
+ * . tcnp (to component name).
+ *
+ * Any pair of vnode parameters may have the same vnode.
+ *
+ * On entry,
+ *
+ * . fdvp, fvp, tdvp, and tvp are referenced,
+ * . fdvp and fvp are unlocked, and
+ * . tdvp and tvp (if nonnull) are locked.
+ *
+ * On exit,
+ *
+ * . fdvp, fvp, tdvp, and tvp (if nonnull) are unreferenced, and
+ * . tdvp and tvp are unlocked.
+ */
 int
 msdosfs_rename(void *v)
 {
-	struct vop_rename_args /* {
+	struct vop_rename_args  /* {
 		struct vnode *a_fdvp;
 		struct vnode *a_fvp;
 		struct componentname *a_fcnp;
@@ -86,428 +103,740 @@ msdosfs_rename(void *v)
 		struct vnode *a_tvp;
 		struct componentname *a_tcnp;
 	} */ *ap = v;
-	struct vnode *tvp = ap->a_tvp;
-	struct vnode *tdvp = ap->a_tdvp;
-	struct vnode *fvp = ap->a_fvp;
 	struct vnode *fdvp = ap->a_fdvp;
-	struct componentname *tcnp = ap->a_tcnp;
+	struct vnode *fvp = ap->a_fvp;
 	struct componentname *fcnp = ap->a_fcnp;
-	struct denode *ip, *xp, *dp, *zp;
-	u_char toname[12], oldname[12];
-	u_long from_diroffset, to_diroffset;
-	u_char to_count;
-	int doingdirectory = 0, newparent = 0;
+	struct vnode *tdvp = ap->a_tdvp;
+	struct vnode *tvp = ap->a_tvp;
+	struct componentname *tcnp = ap->a_tcnp;
+	kauth_cred_t cred;
 	int error;
-	u_long cn;
-	daddr_t bn;
+
+	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 = msdosfs_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;
+}
+
+/*
+ * msdosfs_sane_rename: The hairiest vop, with the saner 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
+msdosfs_sane_rename(
+    struct vnode *fdvp, struct componentname *fcnp,
+    struct vnode *tdvp, struct componentname *tcnp,
+    kauth_cred_t cred, bool posixly_correct)
+{
+	struct msdosfs_lookup_results fmlr, tmlr;
+
+	return genfs_sane_rename(&msdosfs_genfs_rename_ops,
+	    fdvp, fcnp, &fmlr, tdvp, tcnp, &tmlr,
+	    cred, posixly_correct);
+}
+
+/*
+ * msdosfs_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
+msdosfs_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 dosdirempty(VTODE(vp));
+}
+
+/*
+ * Return a UFS-like mode for vp.
+ */
+static mode_t
+msdosfs_vnode_mode(struct vnode *vp)
+{
 	struct msdosfsmount *pmp;
-	struct direntry *dotdotp;
-	struct buf *bp;
+	mode_t mode, mask;
 
-	pmp = VFSTOMSDOSFS(fdvp->v_mount);
+	KASSERT(vp != NULL);
 
-	/*
-	 * Check for cross-device rename.
+	pmp = VTODE(vp)->de_pmp;
+	KASSERT(pmp != NULL);
+
+	if (VTODE(vp)->de_Attributes & ATTR_READONLY)
+		mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+	else
+		mode = S_IRWXU|S_IRWXG|S_IRWXO;
+
+	if (vp->v_type == VDIR)
+		mask = pmp->pm_dirmask;
+	else
+		mask = pmp->pm_mask;
+
+	return (mode & mask);
+}
+
+/*
+ * msdosfs_gro_rename_check_possible: Check whether renaming fvp in fdvp
+ * to tvp in tdvp is possible independent of credentials.
+ */
+static int
+msdosfs_gro_rename_check_possible(struct mount *mp,
+    struct vnode *fdvp, struct vnode *fvp,
+    struct vnode *tdvp, struct vnode *tvp)
+{
+
+	(void)mp;
+	(void)fdvp;
+	(void)fvp;
+	(void)tdvp;
+	(void)tvp;
+	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_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));
+
+	/* It's always possible: no error.  */
+	return 0;
+}
+
+/*
+ * msdosfs_gro_rename_check_permitted: ...
+ */
+static int
+msdosfs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
+    struct vnode *fdvp, struct vnode *fvp,
+    struct vnode *tdvp, struct vnode *tvp)
+{
+	struct msdosfsmount *pmp;
+
+	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_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));
+
+	pmp = VFSTOMSDOSFS(mp);
+	KASSERT(pmp != NULL);
+
+	return genfs_ufslike_rename_check_permitted(cred,
+	    fdvp, msdosfs_vnode_mode(fdvp), pmp->pm_uid,
+	    fvp, pmp->pm_uid,
+	    tdvp, msdosfs_vnode_mode(tdvp), pmp->pm_uid,
+	    tvp, (tvp? pmp->pm_uid : 0));
+}
+
+/*
+ * msdosfs_gro_remove_check_possible: ...
+ */
+static int
+msdosfs_gro_remove_check_possible(struct mount *mp,
+    struct vnode *dvp, struct vnode *vp)
+{
+
+	KASSERT(mp != NULL);
+	KASSERT(dvp != NULL);
+	KASSERT(vp != NULL);
+	KASSERT(dvp != vp);
+	KASSERT(dvp->v_mount == mp);
+	KASSERT(vp->v_mount == mp);
+	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+	/* It's always possible: no error.  */
+	return 0;
+}
+
+/*
+ * msdosfs_gro_remove_check_permitted: ...
+ */
+static int
+msdosfs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
+    struct vnode *dvp, struct vnode *vp)
+{
+	struct msdosfsmount *pmp;
+
+	KASSERT(mp != NULL);
+	KASSERT(dvp != NULL);
+	KASSERT(vp != NULL);
+	KASSERT(dvp != vp);
+	KASSERT(dvp->v_mount == mp);
+	KASSERT(vp->v_mount == mp);
+	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+
+	pmp = VFSTOMSDOSFS(mp);
+	KASSERT(pmp != NULL);
+
+	return genfs_ufslike_remove_check_permitted(cred,
+	    dvp, msdosfs_vnode_mode(dvp), pmp->pm_uid, vp, pmp->pm_uid);
+}
+
+/*
+ * msdosfs_gro_rename: Actually perform the rename operation.
+ */
+static int
+msdosfs_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, nlink_t *tvp_nlinkp)
+{
+	struct msdosfs_lookup_results *fmlr = fde;
+	struct msdosfs_lookup_results *tmlr = tde;
+	struct msdosfsmount *pmp;
+	bool directory_p, reparent_p;
+	unsigned char toname[12], oldname[12];
+	int error;
+
+	KASSERT(mp != NULL);
+	KASSERT(fdvp != NULL);
+	KASSERT(fcnp != NULL);
+	KASSERT(fmlr != NULL);
+	KASSERT(fvp != NULL);
+	KASSERT(tdvp != NULL);
+	KASSERT(tcnp != NULL);
+	KASSERT(tmlr != NULL);
+	KASSERT(fmlr != tmlr);
+	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));
+
+	/*
+	 * We shall need to temporarily bump the reference count, so
+	 * make sure there is room to do so.
+	 */
+	if (VTODE(fvp)->de_refcnt >= LONG_MAX)
+		return EMLINK;
+
+	/*
+	 * XXX There is a pile of logic here to handle a voodoo flag
+	 * DE_RENAME.  I think this is a vestige of days when the file
+	 * system hackers didn't understand concurrency or race
+	 * conditions; I believe it serves no useful function
+	 * whatsoever.
+	 */
+
+	directory_p = (fvp->v_type == VDIR);
+	KASSERT(directory_p ==
+	    ((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0));
+	KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR)));
+	KASSERT((tvp == NULL) || (directory_p ==
+		((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0)));
+	if (directory_p) {
+		if (VTODE(fvp)->de_flag & DE_RENAME)
+			return EINVAL;
+		VTODE(fvp)->de_flag |= DE_RENAME;
+	}
+
+	reparent_p = (fdvp != tdvp);
+	KASSERT(reparent_p == (VTODE(fdvp)->de_StartCluster !=
+		VTODE(tdvp)->de_StartCluster));
+
+	/*
+	 * XXX Hold it right there -- surely if we crash after
+	 * removede, we'll fail to provide rename's guarantee that
+	 * there will be something at the target pathname?
 	 */
-	if ((fvp->v_mount != tdvp->v_mount) ||
-	    (tvp && (fvp->v_mount != tvp->v_mount))) {
-		error = EXDEV;
-abortit:
-		VOP_ABORTOP(tdvp, tcnp);
-		if (tdvp == tvp)
-			vrele(tdvp);
-		else
-			vput(tdvp);
-		if (tvp)
-			vput(tvp);
-		VOP_ABORTOP(fdvp, fcnp);
-		vrele(fdvp);
-		vrele(fvp);
-		return (error);
+	if (tvp != NULL) {
+		error = removede(VTODE(tdvp), VTODE(tvp), tmlr);
+		if (error)
+			goto out;
 	}
 
 	/*
-	 * If source and dest are the same, do nothing.
+	 * Convert the filename in tcnp into a dos filename. We copy this
+	 * into the denode and directory entry for the destination
+	 * file/directory.
 	 */
-	if (tvp == fvp) {
-		error = 0;
-		goto abortit;
+	error = uniqdosname(VTODE(tdvp), tcnp, toname);
+	if (error)
+		goto out;
+
+	/*
+	 * First write a new entry in the destination directory and
+	 * mark the entry in the source directory as deleted.  Then
+	 * move the denode to the correct hash chain for its new
+	 * location in the filesystem.  And, if we moved a directory,
+	 * then update its .. entry to point to the new parent
+	 * directory.
+	 */
+
+	/* Save the old name in case we need to back out.  */
+	memcpy(oldname, VTODE(fvp)->de_Name, 11);
+	memcpy(VTODE(fvp)->de_Name, toname, 11);
+
+	error = createde(VTODE(fvp), VTODE(tdvp), tmlr, 0, tcnp);
+	if (error) {
+		/* Directory entry didn't take -- back out the name change.  */
+		memcpy(VTODE(fvp)->de_Name, oldname, 11);
+		goto out;
 	}
 
 	/*
-	 * XXX: This can deadlock since we hold tdvp/tvp locked.
-	 * But I'm not going to fix it now.
+	 * createde doesn't increment de_refcnt, but removede
+	 * decrements it.  Go figure.
 	 */
-	if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
-		goto abortit;
-	dp = VTODE(fdvp);
-	ip = VTODE(fvp);
-
-	/*
-	 * Be sure we are not renaming ".", "..", or an alias of ".". This
-	 * leads to a crippled directory tree.  It's pretty tough to do a
-	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
-	 * doesn't work if the ".." entry is missing.
-	 */
-	if (ip->de_Attributes & ATTR_DIRECTORY) {
-		/*
-		 * Avoid ".", "..", and aliases of "." for obvious reasons.
-		 */
-		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
-		    dp == ip ||
-		    (fcnp->cn_flags & ISDOTDOT) ||
-		    (tcnp->cn_flags & ISDOTDOT) ||
-		    (ip->de_flag & DE_RENAME)) {
-			VOP_UNLOCK(fvp);
-			error = EINVAL;
-			goto abortit;
-		}
-		ip->de_flag |= DE_RENAME;
-		doingdirectory++;
-	}
-	VN_KNOTE(fdvp, NOTE_WRITE);		/* XXXLUKEM/XXX: right place? */
+	KASSERT(VTODE(fvp)->de_refcnt < LONG_MAX);
+	VTODE(fvp)->de_refcnt++;
 
 	/*
-	 * When the target exists, both the directory
-	 * and target vnodes are returned locked.
+	 * XXX Yes, createde and removede have arguments swapped.  Go figure.
 	 */
-	dp = VTODE(tdvp);
-	xp = tvp ? VTODE(tvp) : NULL;
-	/*
-	 * Remember direntry place to use for destination
-	 */
-	to_diroffset = dp->de_crap.mlr_fndoffset;
-	to_count = dp->de_crap.mlr_fndcnt;
-
-	/*
-	 * If ".." must be changed (ie the directory gets a new
-	 * parent) then the source directory must not be in the
-	 * directory hierarchy above the target, as this would
-	 * orphan everything below the source directory. Also
-	 * the user must have write permission in the source so
-	 * as to be able to change "..". We must repeat the call
-	 * to namei, as the parent directory is unlocked by the
-	 * call to doscheckpath().
-	 */
-	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
-	VOP_UNLOCK(fvp);
-	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
-		newparent = 1;
-
-	if (doingdirectory && newparent) {
-		if (error)	/* write access check above */
-			goto tdvpbad;
-		if (xp != NULL)
-			vput(tvp);
-		tvp = NULL;
-		/*
-		 * doscheckpath() vput()'s tdvp (dp == VTODE(tdvp)),
-		 * so we have to get an extra ref to it first, and
-		 * because it's been unlocked we need to do a relookup
-		 * afterwards in case tvp has changed.
-		 */
-		vref(tdvp);
-		if ((error = doscheckpath(ip, dp)) != 0)
-			goto bad;
-		vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
-		if ((error = relookup(tdvp, &tvp, tcnp, 0)) != 0) {
-			VOP_UNLOCK(tdvp);
-			goto bad;
-		}
-		dp = VTODE(tdvp);
-		xp = tvp ? VTODE(tvp) : NULL;
+	error = removede(VTODE(fdvp), VTODE(fvp), fmlr);
+	if (error) {
+#if 0		/* XXX Back out the new directory entry?  Panic?  */
+		(void)removede(VTODE(tdvp), VTODE(fvp), tmlr);
+		memcpy(VTODE(fvp)->de_Name, oldname, 11);
+#endif
+		goto out;
 	}
 
-	if (xp != NULL) {
-		/*
-		 * Target must be empty if a directory and have no links
-		 * to it. Also, ensure source and target are compatible
-		 * (both directories, or both not directories).
-		 */
-		if (xp->de_Attributes & ATTR_DIRECTORY) {
-			if (!dosdirempty(xp)) {
-				error = ENOTEMPTY;
-				goto tdvpbad;
-			}
-			if (!doingdirectory) {
-				error = ENOTDIR;
-				goto tdvpbad;
-			}
-		} else if (doingdirectory) {
-			error = EISDIR;
-			goto tdvpbad;
-		}
-		if ((error = removede(dp, xp, &dp->de_crap)) != 0)
-			goto tdvpbad;
-		VN_KNOTE(tdvp, NOTE_WRITE);
-		VN_KNOTE(tvp, NOTE_DELETE);
-		cache_purge(tvp);
-		vput(tvp);
-		tvp = NULL;
-		xp = NULL;
+	pmp = VFSTOMSDOSFS(mp);
+
+	if (!directory_p) {
+		struct denode_key old_key = VTODE(fvp)->de_key;
+		struct denode_key new_key = VTODE(fvp)->de_key;
+
+		error = pcbmap(VTODE(tdvp),
+		    de_cluster(pmp, tmlr->mlr_fndoffset), NULL,
+		    &new_key.dk_dirclust, NULL);
+		if (error)	/* XXX Back everything out?  Panic?  */
+			goto out;
+		new_key.dk_diroffset = tmlr->mlr_fndoffset;
+		if (new_key.dk_dirclust != MSDOSFSROOT)
+			new_key.dk_diroffset &= pmp->pm_crbomask;
+		vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key,
+		    sizeof(old_key), &new_key, sizeof(new_key));
+		VTODE(fvp)->de_key = new_key;
+		vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key,
+		    sizeof(old_key), &VTODE(fvp)->de_key,
+		    sizeof(VTODE(fvp)->de_key));
 	}
 
 	/*
-	 * Convert the filename in tcnp into a dos filename. We copy this
-	 * into the denode and directory entry for the destination
-	 * file/directory.
+	 * If we moved a directory to a new parent directory, then we must
+	 * fixup the ".." entry in the moved directory.
 	 */
-	if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) {
-		goto abortit;
+	if (directory_p && reparent_p) {
+		error = msdosfs_rename_replace_dotdot(fvp, fdvp, tdvp, cred);
+		if (error)
+			goto out;
 	}
 
-	/*
-	 * Since from wasn't locked at various places above,
-	 * have to do a relookup here.
-	 */
-	fcnp->cn_flags &= ~MODMASK;
-	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-	VOP_UNLOCK(tdvp);
-	vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-	if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
-		VOP_UNLOCK(fdvp);
-		vrele(ap->a_fvp);
-		vrele(tdvp);
-		return (error);
-	}
-	if (fvp == NULL) {
-		/*
-		 * From name has disappeared.
-		 */
-		if (doingdirectory)
-			panic("rename: lost dir entry");
-		vput(fdvp);
-		vrele(ap->a_fvp);
-		vrele(tdvp);
-		return 0;
-	}
-	VOP_UNLOCK(fdvp);
-	xp = VTODE(fvp);
-	zp = VTODE(fdvp);
-	from_diroffset = zp->de_crap.mlr_fndoffset;
-
-	/*
-	 * Ensure that the directory entry still exists and has not
-	 * changed till now. If the source is a file the entry may
-	 * have been unlinked or renamed. In either case there is
-	 * no further work to be done. If the source is a directory
-	 * then it cannot have been rmdir'ed or renamed; this is
-	 * prohibited by the DE_RENAME flag.
-	 */
-	if (xp != ip) {
-		if (doingdirectory)
-			panic("rename: lost dir entry");
-		vrele(ap->a_fvp);
-		xp = NULL;
-	} else {
-		vrele(fvp);
-		xp = NULL;
+out:;
+	if (tvp != NULL)
+		*tvp_nlinkp = (error ? 1 : 0);
 
-		/*
-		 * First write a new entry in the destination
-		 * directory and mark the entry in the source directory
-		 * as deleted.  Then move the denode to the correct hash
-		 * chain for its new location in the filesystem.  And, if
-		 * we moved a directory, then update its .. entry to point
-		 * to the new parent directory.
-		 */
-		memcpy(oldname, ip->de_Name, 11);
-		memcpy(ip->de_Name, toname, 11);	/* update denode */
-		dp->de_crap.mlr_fndoffset = to_diroffset;
-		dp->de_crap.mlr_fndcnt = to_count;
-		error = createde(ip, dp, &dp->de_crap, (struct denode **)0,
-		    tcnp);
-		if (error) {
-			memcpy(ip->de_Name, oldname, 11);
-			VOP_UNLOCK(fvp);
-			goto bad;
-		}
-		ip->de_refcnt++;
-		zp->de_crap.mlr_fndoffset = from_diroffset;
-		if ((error = removede(zp, ip, &zp->de_crap)) != 0) {
-			/* XXX should really panic here, fs is corrupt */
-			VOP_UNLOCK(fvp);
-			goto bad;
-		}
-		cache_purge(fvp);
-		if (!doingdirectory) {
-			struct denode_key old_key = ip->de_key;
-			struct denode_key new_key = ip->de_key;
-
-			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
-				       &new_key.dk_dirclust, 0);
-			if (error) {
-				/* XXX should really panic here, fs is corrupt */
-				VOP_UNLOCK(fvp);
-				goto bad;
-			}
-			new_key.dk_diroffset = to_diroffset;
-			if (new_key.dk_dirclust != MSDOSFSROOT)
-				new_key.dk_diroffset &= pmp->pm_crbomask;
-			vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key,
-			    sizeof(old_key), &new_key, sizeof(new_key));
-			ip->de_key = new_key;
-			vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key,
-			    sizeof(old_key), &ip->de_key, sizeof(ip->de_key));
-		}
+	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
+
+	if (directory_p)
+		VTODE(fvp)->de_flag &=~ DE_RENAME;
+
+	return error;
+}
+
+/*
+ * msdosfs_gro_remove: Rename an object over another link to itself,
+ * effectively removing just the original link.
+ */
+static int
+msdosfs_gro_remove(struct mount *mp, kauth_cred_t cred,
+    struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
+    nlink_t *tvp_nlinkp)
+{
+	struct msdosfs_lookup_results *mlr = de;
+	int error;
+
+	KASSERT(mp != NULL);
+	KASSERT(dvp != NULL);
+	KASSERT(cnp != NULL);
+	KASSERT(mlr != 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);
+
+	error = removede(VTODE(dvp), VTODE(vp), mlr);
+
+	*tvp_nlinkp = (error ? 1 : 0);
+
+	return error;
+}
+
+/*
+ * msdosfs_gro_lookup: Look up and save the lookup results.
+ */
+static int
+msdosfs_gro_lookup(struct mount *mp, struct vnode *dvp,
+    struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
+{
+	struct msdosfs_lookup_results *mlr_ret = de_ret;
+	struct vnode *vp;
+	int error;
+
+	(void)mp;
+	KASSERT(mp != NULL);
+	KASSERT(dvp != NULL);
+	KASSERT(cnp != NULL);
+	KASSERT(mlr_ret != NULL);
+	KASSERT(vp_ret != NULL);
+	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
+
+	/* Kludge cargo-culted from dholland's ufs_rename.  */
+	cnp->cn_flags &=~ MODMASK;
+	cnp->cn_flags |= (LOCKPARENT | LOCKLEAF);
+
+	error = relookup(dvp, &vp, cnp, 0);
+	if ((error == 0) && (vp == NULL)) {
+		error = ENOENT;
+		goto out;
 	}
+	if (error)
+		return error;
 
 	/*
-	 * If we moved a directory to a new parent directory, then we must
-	 * fixup the ".." entry in the moved directory.
+	 * Thanks to VFS insanity, relookup locks vp, which screws us
+	 * in various ways.
 	 */
-	if (doingdirectory && newparent) {
-		cn = ip->de_StartCluster;
-		if (cn == MSDOSFSROOT) {
-			/* this should never happen */
-			panic("msdosfs_rename: updating .. in root directory?");
-		} else
-			bn = cntobn(pmp, cn);
-		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
-		    pmp->pm_bpcluster, B_MODIFY, &bp);
+	VOP_UNLOCK(vp);
+
+out:
+	*mlr_ret = VTODE(dvp)->de_crap;
+	*vp_ret = vp;
+	return error;
+}
+
+/*
+ * msdosfs_rmdired_p: Check whether the directory vp has been rmdired.
+ *
+ * vp must be locked and referenced.
+ */
+static bool
+msdosfs_rmdired_p(struct vnode *vp)
+{
+
+	KASSERT(vp != NULL);
+	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+	KASSERT(vp->v_type == VDIR);
+
+	return (VTODE(vp)->de_FileSize == 0);
+}
+
+/*
+ * msdosfs_gro_genealogy: Analyze the genealogy of the source and target
+ * directories.
+ */
+static int
+msdosfs_gro_genealogy(struct mount *mp, kauth_cred_t cred,
+    struct vnode *fdvp, struct vnode *tdvp,
+    struct vnode **intermediate_node_ret)
+{
+	struct msdosfsmount *pmp;
+	struct vnode *vp, *dvp;
+	unsigned long dotdot_cn;
+	int error;
+
+	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);
+
+	pmp = VFSTOMSDOSFS(mp);
+	KASSERT(pmp != NULL);
+
+	/*
+	 * We need to provisionally lock tdvp to keep rmdir from
+	 * deleting it -- or any ancestor -- at an inopportune moment.
+	 */
+	error = msdosfs_gro_lock_directory(mp, tdvp);
+	if (error)
+		return error;
+
+	vp = tdvp;
+	vref(vp);
+
+	for (;;) {
+		KASSERT(vp->v_type == VDIR);
+
+		/* Did we hit the root without finding fdvp?  */
+		if ((vp->v_vflag & VV_ROOT) != 0) {
+			vput(vp);
+			*intermediate_node_ret = NULL;
+			return 0;
+		}
+
+		error = msdosfs_read_dotdot(vp, cred, &dotdot_cn);
 		if (error) {
-			/* XXX should really panic here, fs is corrupt */
-			VOP_UNLOCK(fvp);
-			goto bad;
+			vput(vp);
+			return error;
 		}
-		dotdotp = (struct direntry *)bp->b_data + 1;
-		putushort(dotdotp->deStartCluster, dp->de_StartCluster);
-		if (FAT32(pmp)) {
-			putushort(dotdotp->deHighClust,
-				dp->de_StartCluster >> 16);
-		} else {
-			putushort(dotdotp->deHighClust, 0);
+
+		/* Did we find that fdvp is an ancestor?  */
+		if (VTODE(fdvp)->de_StartCluster == dotdot_cn) {
+			/* Unlock vp, but keep it referenced.  */
+			VOP_UNLOCK(vp);
+			*intermediate_node_ret = vp;
+			return 0;
 		}
-		if ((error = bwrite(bp)) != 0) {
-			/* XXX should really panic here, fs is corrupt */
-			VOP_UNLOCK(fvp);
-			goto bad;
+
+		/* Neither -- keep ascending.  */
+
+		error = deget(pmp, dotdot_cn, (dotdot_cn ? 0 : MSDOSFSROOT_OFS), &dvp);
+		vput(vp);
+		if (error)
+			return error;
+		error = vn_lock(dvp, LK_EXCLUSIVE);
+		if (error) {
+			vrele(dvp);
+			return error;
 		}
-	}
 
-	VN_KNOTE(fvp, NOTE_RENAME);
-	VOP_UNLOCK(fvp);
-bad:
-	if (tvp)
-		vput(tvp);
-	vrele(tdvp);
-	ip->de_flag &= ~DE_RENAME;
-	vrele(fdvp);
-	vrele(fvp);
-	return (error);
+		KASSERT(dvp != NULL);
+		KASSERT(dvp->v_type == VDIR);
 
-	/* XXX: uuuh */
-tdvpbad:
-	VOP_UNLOCK(tdvp);
-	goto bad;
+		vp = dvp;
+
+		if (msdosfs_rmdired_p(vp)) {
+			vput(vp);
+			return ENOENT;
+		}
+	}
 }
 
 /*
- * Check to see if the directory described by target is in some
- * subdirectory of source.  This prevents something like the following from
- * succeeding and leaving a bunch or files and directories orphaned. mv
- * /a/b/c /a/b/c/d/e/f Where c and f are directories.
- *
- * source - the inode for /a/b/c
- * target - the inode for /a/b/c/d/e/f
- *
- * Returns 0 if target is NOT a subdirectory of source.
- * Otherwise returns a non-zero error number.
- * The target inode is always unlocked on return.
+ * msdosfs_read_dotdot: Store in *cn_ret the cluster number of the
+ * parent of the directory vp.
  */
-int
-doscheckpath(struct denode *source, struct denode *target)
+static int
+msdosfs_read_dotdot(struct vnode *vp, kauth_cred_t cred, unsigned long *cn_ret)
 {
-	u_long scn;
 	struct msdosfsmount *pmp;
+	unsigned long start_cn, cn;
+	struct buf *bp;
 	struct direntry *ep;
-	struct denode *dep;
-	struct buf *bp = NULL;
-	int error = 0;
-
-	dep = target;
-	if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
-	    (source->de_Attributes & ATTR_DIRECTORY) == 0) {
+	int error;
+
+	KASSERT(vp != NULL);
+	KASSERT(cn_ret != NULL);
+	KASSERT(vp->v_type == VDIR);
+	KASSERT(VTODE(vp) != NULL);
+
+	pmp = VTODE(vp)->de_pmp;
+	KASSERT(pmp != NULL);
+
+	start_cn = VTODE(vp)->de_StartCluster;
+	error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, start_cn)),
+	    pmp->pm_bpcluster, 0, &bp);
+	if (error)
+		return error;
+
+	ep = (struct direntry *)bp->b_data + 1;
+	if (((ep->deAttributes & ATTR_DIRECTORY) == ATTR_DIRECTORY) &&
+	    (memcmp(ep->deName, "..         ", 11) == 0)) {
+		cn = getushort(ep->deStartCluster);
+		if (FAT32(pmp))
+			cn |= getushort(ep->deHighClust) << 16;
+		*cn_ret = cn;
+		error = 0;
+	} else {
 		error = ENOTDIR;
-		goto out;
 	}
-	if (dep->de_StartCluster == source->de_StartCluster) {
-		error = EEXIST;
-		goto out;
+
+	brelse(bp, 0);
+
+	return error;
+}
+
+/*
+ * msdosfs_rename_replace_dotdot: Change the target of the `..' entry of
+ * the directory vp from fdvp to tdvp.
+ */
+static int
+msdosfs_rename_replace_dotdot(struct vnode *vp,
+    struct vnode *fdvp, struct vnode *tdvp,
+    kauth_cred_t cred)
+{
+	struct msdosfsmount *pmp;
+	struct direntry *dotdotp;
+	struct buf *bp;
+	daddr_t bn;
+	u_long cn;
+	int error;
+
+	pmp = VFSTOMSDOSFS(fdvp->v_mount);
+
+	cn = VTODE(vp)->de_StartCluster;
+	if (cn == MSDOSFSROOT) {
+		/* this should never happen */
+		panic("msdosfs_rename: updating .. in root directory?");
+	} else
+		bn = cntobn(pmp, cn);
+
+	error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
+	    pmp->pm_bpcluster, B_MODIFY, &bp);
+	if (error)
+		return error;
+
+	dotdotp = (struct direntry *)bp->b_data + 1;
+	putushort(dotdotp->deStartCluster, VTODE(tdvp)->de_StartCluster);
+	if (FAT32(pmp)) {
+		putushort(dotdotp->deHighClust,
+			VTODE(tdvp)->de_StartCluster >> 16);
+	} else {
+		putushort(dotdotp->deHighClust, 0);
 	}
-	if (dep->de_StartCluster == MSDOSFSROOT)
-		goto out;
-	pmp = dep->de_pmp;
-#ifdef	DIAGNOSTIC
-	if (pmp != source->de_pmp)
-		panic("doscheckpath: source and target on different filesystems");
-#endif
-	if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
-		goto out;
 
-	for (;;) {
-		if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
-			error = ENOTDIR;
-			break;
-		}
-		scn = dep->de_StartCluster;
-		error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, scn)),
-			      pmp->pm_bpcluster, 0, &bp);
-		if (error)
-			break;
+	error = bwrite(bp);
 
-		ep = (struct direntry *) bp->b_data + 1;
-		if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
-		    memcmp(ep->deName, "..         ", 11) != 0) {
-			error = ENOTDIR;
-			break;
-		}
-		scn = getushort(ep->deStartCluster);
-		if (FAT32(pmp))
-			scn |= getushort(ep->deHighClust) << 16;
+	return error;
+}
 
-		if (scn == source->de_StartCluster) {
-			error = EINVAL;
-			break;
-		}
-		if (scn == MSDOSFSROOT)
-			break;
-		if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
-			/*
-			 * scn should be 0 in this case,
-			 * but we silently ignore the error.
-			 */
-			break;
-		}
+/*
+ * msdosfs_gro_lock_directory: Lock the directory vp, but fail if it has
+ * been rmdir'd.
+ */
+static int
+msdosfs_gro_lock_directory(struct mount *mp, struct vnode *vp)
+{
+	int error;
 
-		vput(DETOV(dep));
-		brelse(bp, 0);
-		bp = NULL;
-#ifdef MAKEFS
-		/* NOTE: deget() clears dep on error */
-		if ((error = deget(pmp, scn, 0, &dep)) != 0)
-			break;
-#else
-		struct vnode *vp;
+	(void)mp;
+	KASSERT(vp != NULL);
 
-		dep = NULL;
-		error = deget(pmp, scn, 0, &vp);
-		if (error)
-			break;
-		error = vn_lock(vp, LK_EXCLUSIVE);
-		if (error) {
-			vrele(vp);
-			break;
-		}
-		dep = VTODE(vp);
-#endif
+	error = vn_lock(vp, LK_EXCLUSIVE);
+	if (error)
+		return error;
+
+	KASSERT(mp != NULL);
+	KASSERT(vp->v_mount == mp);
+
+	if (msdosfs_rmdired_p(vp)) {
+		VOP_UNLOCK(vp);
+		return ENOENT;
 	}
-out:
-	if (bp)
-		brelse(bp, 0);
-	if (error == ENOTDIR)
-		printf("doscheckpath(): .. not a directory?\n");
-	if (dep != NULL)
-		vput(DETOV(dep));
-	return (error);
+
+	return 0;
 }
+
+static const struct genfs_rename_ops msdosfs_genfs_rename_ops = {
+	.gro_directory_empty_p		= msdosfs_gro_directory_empty_p,
+	.gro_rename_check_possible	= msdosfs_gro_rename_check_possible,
+	.gro_rename_check_permitted	= msdosfs_gro_rename_check_permitted,
+	.gro_remove_check_possible	= msdosfs_gro_remove_check_possible,
+	.gro_remove_check_permitted	= msdosfs_gro_remove_check_permitted,
+	.gro_rename			= msdosfs_gro_rename,
+	.gro_remove			= msdosfs_gro_remove,
+	.gro_lookup			= msdosfs_gro_lookup,
+	.gro_genealogy			= msdosfs_gro_genealogy,
+	.gro_lock_directory		= msdosfs_gro_lock_directory,
+};

Reply via email to