xref: /netbsd-src/sys/fs/msdosfs/msdosfs_rename.c (revision 6d4f51a741506096d8bd04500820ab9b33c0b4a7)
1*6d4f51a7Smlelstv /*	$NetBSD: msdosfs_rename.c,v 1.4 2024/05/04 05:49:39 mlelstv Exp $	*/
2e99cb814Shannken 
3e99cb814Shannken /*-
45b6c6a9aShannken  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5e99cb814Shannken  * All rights reserved.
65b6c6a9aShannken  *
75b6c6a9aShannken  * This code is derived from software contributed to The NetBSD Foundation
85b6c6a9aShannken  * by Taylor R Campbell.
9e99cb814Shannken  *
10e99cb814Shannken  * Redistribution and use in source and binary forms, with or without
11e99cb814Shannken  * modification, are permitted provided that the following conditions
12e99cb814Shannken  * are met:
13e99cb814Shannken  * 1. Redistributions of source code must retain the above copyright
14e99cb814Shannken  *    notice, this list of conditions and the following disclaimer.
15e99cb814Shannken  * 2. Redistributions in binary form must reproduce the above copyright
16e99cb814Shannken  *    notice, this list of conditions and the following disclaimer in the
17e99cb814Shannken  *    documentation and/or other materials provided with the distribution.
18e99cb814Shannken  *
195b6c6a9aShannken  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205b6c6a9aShannken  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215b6c6a9aShannken  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225b6c6a9aShannken  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235b6c6a9aShannken  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245b6c6a9aShannken  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255b6c6a9aShannken  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265b6c6a9aShannken  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275b6c6a9aShannken  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285b6c6a9aShannken  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295b6c6a9aShannken  * POSSIBILITY OF SUCH DAMAGE.
30e99cb814Shannken  */
315b6c6a9aShannken 
32e99cb814Shannken /*
335b6c6a9aShannken  * MS-DOS FS Rename
34e99cb814Shannken  */
35e99cb814Shannken 
365b6c6a9aShannken #include <sys/cdefs.h>
37*6d4f51a7Smlelstv __KERNEL_RCSID(0, "$NetBSD: msdosfs_rename.c,v 1.4 2024/05/04 05:49:39 mlelstv Exp $");
385b6c6a9aShannken 
39e99cb814Shannken #include <sys/param.h>
40e99cb814Shannken #include <sys/buf.h>
415b6c6a9aShannken #include <sys/errno.h>
42e99cb814Shannken #include <sys/kauth.h>
435b6c6a9aShannken #include <sys/namei.h>
445b6c6a9aShannken #include <sys/vnode.h>
455b6c6a9aShannken #include <sys/vnode_if.h>
46e99cb814Shannken 
47e99cb814Shannken #include <miscfs/genfs/genfs.h>
48e99cb814Shannken 
49e99cb814Shannken #include <fs/msdosfs/bpb.h>
50e99cb814Shannken #include <fs/msdosfs/direntry.h>
51e99cb814Shannken #include <fs/msdosfs/denode.h>
52e99cb814Shannken #include <fs/msdosfs/msdosfsmount.h>
53e99cb814Shannken #include <fs/msdosfs/fat.h>
54e99cb814Shannken 
555b6c6a9aShannken /*
565b6c6a9aShannken  * Forward declarations
575b6c6a9aShannken  */
585b6c6a9aShannken 
595b6c6a9aShannken static int msdosfs_sane_rename(struct vnode *, struct componentname *,
605b6c6a9aShannken     struct vnode *, struct componentname *,
615b6c6a9aShannken     kauth_cred_t, bool);
625b6c6a9aShannken static bool msdosfs_rmdired_p(struct vnode *);
635b6c6a9aShannken static int msdosfs_read_dotdot(struct vnode *, kauth_cred_t, unsigned long *);
645b6c6a9aShannken static int msdosfs_rename_replace_dotdot(struct vnode *,
655b6c6a9aShannken     struct vnode *, struct vnode *, kauth_cred_t);
665b6c6a9aShannken static int msdosfs_gro_lock_directory(struct mount *, struct vnode *);
675b6c6a9aShannken 
685b6c6a9aShannken static const struct genfs_rename_ops msdosfs_genfs_rename_ops;
695b6c6a9aShannken 
705b6c6a9aShannken /*
715b6c6a9aShannken  * msdosfs_rename: The hairiest vop, with the insanest API.
725b6c6a9aShannken  *
735b6c6a9aShannken  * Arguments:
745b6c6a9aShannken  *
755b6c6a9aShannken  * . fdvp (from directory vnode),
765b6c6a9aShannken  * . fvp (from vnode),
775b6c6a9aShannken  * . fcnp (from component name),
785b6c6a9aShannken  * . tdvp (to directory vnode),
795b6c6a9aShannken  * . tvp (to vnode, or NULL), and
805b6c6a9aShannken  * . tcnp (to component name).
815b6c6a9aShannken  *
825b6c6a9aShannken  * Any pair of vnode parameters may have the same vnode.
835b6c6a9aShannken  *
845b6c6a9aShannken  * On entry,
855b6c6a9aShannken  *
865b6c6a9aShannken  * . fdvp, fvp, tdvp, and tvp are referenced,
875b6c6a9aShannken  * . fdvp and fvp are unlocked, and
885b6c6a9aShannken  * . tdvp and tvp (if nonnull) are locked.
895b6c6a9aShannken  *
905b6c6a9aShannken  * On exit,
915b6c6a9aShannken  *
925b6c6a9aShannken  * . fdvp, fvp, tdvp, and tvp (if nonnull) are unreferenced, and
935b6c6a9aShannken  * . tdvp and tvp are unlocked.
945b6c6a9aShannken  */
95e99cb814Shannken int
msdosfs_rename(void * v)96e99cb814Shannken msdosfs_rename(void *v)
97e99cb814Shannken {
98e99cb814Shannken 	struct vop_rename_args  /* {
99e99cb814Shannken 		struct vnode *a_fdvp;
100e99cb814Shannken 		struct vnode *a_fvp;
101e99cb814Shannken 		struct componentname *a_fcnp;
102e99cb814Shannken 		struct vnode *a_tdvp;
103e99cb814Shannken 		struct vnode *a_tvp;
104e99cb814Shannken 		struct componentname *a_tcnp;
105e99cb814Shannken 	} */ *ap = v;
106e99cb814Shannken 	struct vnode *fdvp = ap->a_fdvp;
1075b6c6a9aShannken 	struct vnode *fvp = ap->a_fvp;
108e99cb814Shannken 	struct componentname *fcnp = ap->a_fcnp;
1095b6c6a9aShannken 	struct vnode *tdvp = ap->a_tdvp;
1105b6c6a9aShannken 	struct vnode *tvp = ap->a_tvp;
1115b6c6a9aShannken 	struct componentname *tcnp = ap->a_tcnp;
1125b6c6a9aShannken 	kauth_cred_t cred;
113e99cb814Shannken 	int error;
114e99cb814Shannken 
1155b6c6a9aShannken 	KASSERT(fdvp != NULL);
1165b6c6a9aShannken 	KASSERT(fvp != NULL);
1175b6c6a9aShannken 	KASSERT(fcnp != NULL);
1185b6c6a9aShannken 	KASSERT(fcnp->cn_nameptr != NULL);
1195b6c6a9aShannken 	KASSERT(tdvp != NULL);
1205b6c6a9aShannken 	KASSERT(tcnp != NULL);
1215b6c6a9aShannken 	KASSERT(fcnp->cn_nameptr != NULL);
1225b6c6a9aShannken 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
1235b6c6a9aShannken 	/* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */
1245b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
1255b6c6a9aShannken 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
1265b6c6a9aShannken 	KASSERT(fdvp->v_type == VDIR);
1275b6c6a9aShannken 	KASSERT(tdvp->v_type == VDIR);
1285b6c6a9aShannken 
1295b6c6a9aShannken 	cred = fcnp->cn_cred;
130*6d4f51a7Smlelstv 	KASSERT(kauth_cred_uidmatch(cred, tcnp->cn_cred));
131e99cb814Shannken 
132e99cb814Shannken 	/*
1335b6c6a9aShannken 	 * Sanitize our world from the VFS insanity.  Unlock the target
1345b6c6a9aShannken 	 * directory and node, which are locked.  Release the children,
1355b6c6a9aShannken 	 * which are referenced.  Check for rename("x", "y/."), which
1365b6c6a9aShannken 	 * it is our responsibility to reject, not the caller's.  (But
1375b6c6a9aShannken 	 * the caller does reject rename("x/.", "y").  Go figure.)
138e99cb814Shannken 	 */
139e99cb814Shannken 
140e99cb814Shannken 	VOP_UNLOCK(tdvp);
1415b6c6a9aShannken 	if ((tvp != NULL) && (tvp != tdvp))
1425b6c6a9aShannken 		VOP_UNLOCK(tvp);
1435b6c6a9aShannken 
1445b6c6a9aShannken 	vrele(fvp);
1455b6c6a9aShannken 	if (tvp != NULL)
1465b6c6a9aShannken 		vrele(tvp);
1475b6c6a9aShannken 
1485b6c6a9aShannken 	if (tvp == tdvp) {
1495b6c6a9aShannken 		error = EINVAL;
1505b6c6a9aShannken 		goto out;
151e99cb814Shannken 	}
152e99cb814Shannken 
1535b6c6a9aShannken 	error = msdosfs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, false);
1545b6c6a9aShannken 
1555b6c6a9aShannken out:	/*
1565b6c6a9aShannken 	 * All done, whether with success or failure.  Release the
1575b6c6a9aShannken 	 * directory nodes now, as the caller expects from the VFS
1585b6c6a9aShannken 	 * protocol.
159e99cb814Shannken 	 */
1605b6c6a9aShannken 	vrele(fdvp);
1615b6c6a9aShannken 	vrele(tdvp);
1625b6c6a9aShannken 
1635b6c6a9aShannken 	return error;
164e99cb814Shannken }
1655b6c6a9aShannken 
1665b6c6a9aShannken /*
1675b6c6a9aShannken  * msdosfs_sane_rename: The hairiest vop, with the saner API.
1685b6c6a9aShannken  *
1695b6c6a9aShannken  * Arguments:
1705b6c6a9aShannken  *
1715b6c6a9aShannken  * . fdvp (from directory vnode),
1725b6c6a9aShannken  * . fcnp (from component name),
1735b6c6a9aShannken  * . tdvp (to directory vnode), and
1745b6c6a9aShannken  * . tcnp (to component name).
1755b6c6a9aShannken  *
1765b6c6a9aShannken  * fdvp and tdvp must be referenced and unlocked.
1775b6c6a9aShannken  */
1785b6c6a9aShannken static int
msdosfs_sane_rename(struct vnode * fdvp,struct componentname * fcnp,struct vnode * tdvp,struct componentname * tcnp,kauth_cred_t cred,bool posixly_correct)1795b6c6a9aShannken msdosfs_sane_rename(
1805b6c6a9aShannken     struct vnode *fdvp, struct componentname *fcnp,
1815b6c6a9aShannken     struct vnode *tdvp, struct componentname *tcnp,
1825b6c6a9aShannken     kauth_cred_t cred, bool posixly_correct)
1835b6c6a9aShannken {
1845b6c6a9aShannken 	struct msdosfs_lookup_results fmlr, tmlr;
1855b6c6a9aShannken 
1865b6c6a9aShannken 	return genfs_sane_rename(&msdosfs_genfs_rename_ops,
1875b6c6a9aShannken 	    fdvp, fcnp, &fmlr, tdvp, tcnp, &tmlr,
1885b6c6a9aShannken 	    cred, posixly_correct);
189e99cb814Shannken }
1905b6c6a9aShannken 
1915b6c6a9aShannken /*
1925b6c6a9aShannken  * msdosfs_gro_directory_empty_p: Return true if the directory vp is
1935b6c6a9aShannken  * empty.  dvp is its parent.
1945b6c6a9aShannken  *
1955b6c6a9aShannken  * vp and dvp must be locked and referenced.
1965b6c6a9aShannken  */
1975b6c6a9aShannken static bool
msdosfs_gro_directory_empty_p(struct mount * mp,kauth_cred_t cred,struct vnode * vp,struct vnode * dvp)1985b6c6a9aShannken msdosfs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
1995b6c6a9aShannken     struct vnode *vp, struct vnode *dvp)
2005b6c6a9aShannken {
2015b6c6a9aShannken 
2025b6c6a9aShannken 	(void)mp;
2035b6c6a9aShannken 	(void)cred;
2045b6c6a9aShannken 	(void)dvp;
2055b6c6a9aShannken 	KASSERT(mp != NULL);
2065b6c6a9aShannken 	KASSERT(vp != NULL);
2075b6c6a9aShannken 	KASSERT(dvp != NULL);
2085b6c6a9aShannken 	KASSERT(vp != dvp);
2095b6c6a9aShannken 	KASSERT(vp->v_mount == mp);
2105b6c6a9aShannken 	KASSERT(dvp->v_mount == mp);
2115b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
2125b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
2135b6c6a9aShannken 
2148086f46eSthorpej 	return msdosfs_dosdirempty(VTODE(vp));
215e99cb814Shannken }
2165b6c6a9aShannken 
2175b6c6a9aShannken /*
2185b6c6a9aShannken  * Return a UFS-like mode for vp.
2195b6c6a9aShannken  */
2205b6c6a9aShannken static mode_t
msdosfs_vnode_mode(struct vnode * vp)2215b6c6a9aShannken msdosfs_vnode_mode(struct vnode *vp)
2225b6c6a9aShannken {
2235b6c6a9aShannken 	struct msdosfsmount *pmp;
2245b6c6a9aShannken 	mode_t mode, mask;
2255b6c6a9aShannken 
2265b6c6a9aShannken 	KASSERT(vp != NULL);
2275b6c6a9aShannken 
2285b6c6a9aShannken 	pmp = VTODE(vp)->de_pmp;
2295b6c6a9aShannken 	KASSERT(pmp != NULL);
2305b6c6a9aShannken 
2315b6c6a9aShannken 	if (VTODE(vp)->de_Attributes & ATTR_READONLY)
2325b6c6a9aShannken 		mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
2335b6c6a9aShannken 	else
2345b6c6a9aShannken 		mode = S_IRWXU|S_IRWXG|S_IRWXO;
2355b6c6a9aShannken 
2365b6c6a9aShannken 	if (vp->v_type == VDIR)
2375b6c6a9aShannken 		mask = pmp->pm_dirmask;
2385b6c6a9aShannken 	else
2395b6c6a9aShannken 		mask = pmp->pm_mask;
2405b6c6a9aShannken 
2415b6c6a9aShannken 	return (mode & mask);
2425b6c6a9aShannken }
2435b6c6a9aShannken 
2445b6c6a9aShannken /*
2455b6c6a9aShannken  * msdosfs_gro_rename_check_possible: Check whether renaming fvp in fdvp
2465b6c6a9aShannken  * to tvp in tdvp is possible independent of credentials.
2475b6c6a9aShannken  */
2485b6c6a9aShannken static int
msdosfs_gro_rename_check_possible(struct mount * mp,struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)2495b6c6a9aShannken msdosfs_gro_rename_check_possible(struct mount *mp,
2505b6c6a9aShannken     struct vnode *fdvp, struct vnode *fvp,
2515b6c6a9aShannken     struct vnode *tdvp, struct vnode *tvp)
2525b6c6a9aShannken {
2535b6c6a9aShannken 
2545b6c6a9aShannken 	(void)mp;
2555b6c6a9aShannken 	(void)fdvp;
2565b6c6a9aShannken 	(void)fvp;
2575b6c6a9aShannken 	(void)tdvp;
2585b6c6a9aShannken 	(void)tvp;
2595b6c6a9aShannken 	KASSERT(mp != NULL);
2605b6c6a9aShannken 	KASSERT(fdvp != NULL);
2615b6c6a9aShannken 	KASSERT(fvp != NULL);
2625b6c6a9aShannken 	KASSERT(tdvp != NULL);
2635b6c6a9aShannken 	KASSERT(fdvp != fvp);
2645b6c6a9aShannken 	KASSERT(fdvp != tvp);
2655b6c6a9aShannken 	KASSERT(tdvp != fvp);
2665b6c6a9aShannken 	KASSERT(tdvp != tvp);
2675b6c6a9aShannken 	KASSERT(fvp != tvp);
2685b6c6a9aShannken 	KASSERT(fdvp->v_mount == mp);
2695b6c6a9aShannken 	KASSERT(fvp->v_mount == mp);
2705b6c6a9aShannken 	KASSERT(tdvp->v_mount == mp);
2715b6c6a9aShannken 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
2725b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
2735b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
2745b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
2755b6c6a9aShannken 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
2765b6c6a9aShannken 
2775b6c6a9aShannken 	/* It's always possible: no error.  */
2785b6c6a9aShannken 	return 0;
2795b6c6a9aShannken }
2805b6c6a9aShannken 
2815b6c6a9aShannken /*
2825b6c6a9aShannken  * msdosfs_gro_rename_check_permitted: ...
2835b6c6a9aShannken  */
2845b6c6a9aShannken 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)2855b6c6a9aShannken msdosfs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
2865b6c6a9aShannken     struct vnode *fdvp, struct vnode *fvp,
2875b6c6a9aShannken     struct vnode *tdvp, struct vnode *tvp)
2885b6c6a9aShannken {
2895b6c6a9aShannken 	struct msdosfsmount *pmp;
2905b6c6a9aShannken 
2915b6c6a9aShannken 	KASSERT(mp != NULL);
2925b6c6a9aShannken 	KASSERT(fdvp != NULL);
2935b6c6a9aShannken 	KASSERT(fvp != NULL);
2945b6c6a9aShannken 	KASSERT(tdvp != NULL);
2955b6c6a9aShannken 	KASSERT(fdvp != fvp);
2965b6c6a9aShannken 	KASSERT(fdvp != tvp);
2975b6c6a9aShannken 	KASSERT(tdvp != fvp);
2985b6c6a9aShannken 	KASSERT(tdvp != tvp);
2995b6c6a9aShannken 	KASSERT(fvp != tvp);
3005b6c6a9aShannken 	KASSERT(fdvp->v_mount == mp);
3015b6c6a9aShannken 	KASSERT(fvp->v_mount == mp);
3025b6c6a9aShannken 	KASSERT(tdvp->v_mount == mp);
3035b6c6a9aShannken 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
3045b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
3055b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
3065b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
3075b6c6a9aShannken 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
3085b6c6a9aShannken 
3095b6c6a9aShannken 	pmp = VFSTOMSDOSFS(mp);
3105b6c6a9aShannken 	KASSERT(pmp != NULL);
3115b6c6a9aShannken 
3125b6c6a9aShannken 	return genfs_ufslike_rename_check_permitted(cred,
3135b6c6a9aShannken 	    fdvp, msdosfs_vnode_mode(fdvp), pmp->pm_uid,
3145b6c6a9aShannken 	    fvp, pmp->pm_uid,
3155b6c6a9aShannken 	    tdvp, msdosfs_vnode_mode(tdvp), pmp->pm_uid,
3165b6c6a9aShannken 	    tvp, (tvp? pmp->pm_uid : 0));
3175b6c6a9aShannken }
3185b6c6a9aShannken 
3195b6c6a9aShannken /*
3205b6c6a9aShannken  * msdosfs_gro_remove_check_possible: ...
3215b6c6a9aShannken  */
3225b6c6a9aShannken static int
msdosfs_gro_remove_check_possible(struct mount * mp,struct vnode * dvp,struct vnode * vp)3235b6c6a9aShannken msdosfs_gro_remove_check_possible(struct mount *mp,
3245b6c6a9aShannken     struct vnode *dvp, struct vnode *vp)
3255b6c6a9aShannken {
3265b6c6a9aShannken 
3275b6c6a9aShannken 	KASSERT(mp != NULL);
3285b6c6a9aShannken 	KASSERT(dvp != NULL);
3295b6c6a9aShannken 	KASSERT(vp != NULL);
3305b6c6a9aShannken 	KASSERT(dvp != vp);
3315b6c6a9aShannken 	KASSERT(dvp->v_mount == mp);
3325b6c6a9aShannken 	KASSERT(vp->v_mount == mp);
3335b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
3345b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
3355b6c6a9aShannken 
3365b6c6a9aShannken 	/* It's always possible: no error.  */
3375b6c6a9aShannken 	return 0;
3385b6c6a9aShannken }
3395b6c6a9aShannken 
3405b6c6a9aShannken /*
3415b6c6a9aShannken  * msdosfs_gro_remove_check_permitted: ...
3425b6c6a9aShannken  */
3435b6c6a9aShannken static int
msdosfs_gro_remove_check_permitted(struct mount * mp,kauth_cred_t cred,struct vnode * dvp,struct vnode * vp)3445b6c6a9aShannken msdosfs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
3455b6c6a9aShannken     struct vnode *dvp, struct vnode *vp)
3465b6c6a9aShannken {
3475b6c6a9aShannken 	struct msdosfsmount *pmp;
3485b6c6a9aShannken 
3495b6c6a9aShannken 	KASSERT(mp != NULL);
3505b6c6a9aShannken 	KASSERT(dvp != NULL);
3515b6c6a9aShannken 	KASSERT(vp != NULL);
3525b6c6a9aShannken 	KASSERT(dvp != vp);
3535b6c6a9aShannken 	KASSERT(dvp->v_mount == mp);
3545b6c6a9aShannken 	KASSERT(vp->v_mount == mp);
3555b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
3565b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
3575b6c6a9aShannken 
3585b6c6a9aShannken 	pmp = VFSTOMSDOSFS(mp);
3595b6c6a9aShannken 	KASSERT(pmp != NULL);
3605b6c6a9aShannken 
3615b6c6a9aShannken 	return genfs_ufslike_remove_check_permitted(cred,
3625b6c6a9aShannken 	    dvp, msdosfs_vnode_mode(dvp), pmp->pm_uid, vp, pmp->pm_uid);
3635b6c6a9aShannken }
3645b6c6a9aShannken 
3655b6c6a9aShannken /*
3665b6c6a9aShannken  * msdosfs_gro_rename: Actually perform the rename operation.
3675b6c6a9aShannken  */
3685b6c6a9aShannken 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)3695b6c6a9aShannken msdosfs_gro_rename(struct mount *mp, kauth_cred_t cred,
3705b6c6a9aShannken     struct vnode *fdvp, struct componentname *fcnp,
3715b6c6a9aShannken     void *fde, struct vnode *fvp,
3725b6c6a9aShannken     struct vnode *tdvp, struct componentname *tcnp,
3735b6c6a9aShannken     void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp)
3745b6c6a9aShannken {
3755b6c6a9aShannken 	struct msdosfs_lookup_results *fmlr = fde;
3765b6c6a9aShannken 	struct msdosfs_lookup_results *tmlr = tde;
3775b6c6a9aShannken 	struct msdosfsmount *pmp;
3785b6c6a9aShannken 	bool directory_p, reparent_p;
3795b6c6a9aShannken 	unsigned char toname[12], oldname[12];
3805b6c6a9aShannken 	int error;
3815b6c6a9aShannken 
3825b6c6a9aShannken 	KASSERT(mp != NULL);
3835b6c6a9aShannken 	KASSERT(fdvp != NULL);
3845b6c6a9aShannken 	KASSERT(fcnp != NULL);
3855b6c6a9aShannken 	KASSERT(fmlr != NULL);
3865b6c6a9aShannken 	KASSERT(fvp != NULL);
3875b6c6a9aShannken 	KASSERT(tdvp != NULL);
3885b6c6a9aShannken 	KASSERT(tcnp != NULL);
3895b6c6a9aShannken 	KASSERT(tmlr != NULL);
3905b6c6a9aShannken 	KASSERT(fmlr != tmlr);
3915b6c6a9aShannken 	KASSERT(fdvp != fvp);
3925b6c6a9aShannken 	KASSERT(fdvp != tvp);
3935b6c6a9aShannken 	KASSERT(tdvp != fvp);
3945b6c6a9aShannken 	KASSERT(tdvp != tvp);
3955b6c6a9aShannken 	KASSERT(fvp != tvp);
3965b6c6a9aShannken 	KASSERT(fdvp->v_mount == mp);
3975b6c6a9aShannken 	KASSERT(fvp->v_mount == mp);
3985b6c6a9aShannken 	KASSERT(tdvp->v_mount == mp);
3995b6c6a9aShannken 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
4005b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
4015b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
4025b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
4035b6c6a9aShannken 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
4045b6c6a9aShannken 
4055b6c6a9aShannken 	/*
4065b6c6a9aShannken 	 * We shall need to temporarily bump the reference count, so
4075b6c6a9aShannken 	 * make sure there is room to do so.
4085b6c6a9aShannken 	 */
4095b6c6a9aShannken 	if (VTODE(fvp)->de_refcnt >= LONG_MAX)
4105b6c6a9aShannken 		return EMLINK;
4115b6c6a9aShannken 
4125b6c6a9aShannken 	/*
4135b6c6a9aShannken 	 * XXX There is a pile of logic here to handle a voodoo flag
4145b6c6a9aShannken 	 * DE_RENAME.  I think this is a vestige of days when the file
4155b6c6a9aShannken 	 * system hackers didn't understand concurrency or race
4165b6c6a9aShannken 	 * conditions; I believe it serves no useful function
4175b6c6a9aShannken 	 * whatsoever.
4185b6c6a9aShannken 	 */
4195b6c6a9aShannken 
4205b6c6a9aShannken 	directory_p = (fvp->v_type == VDIR);
4215b6c6a9aShannken 	KASSERT(directory_p ==
4225b6c6a9aShannken 	    ((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0));
4235b6c6a9aShannken 	KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR)));
4245b6c6a9aShannken 	KASSERT((tvp == NULL) || (directory_p ==
4255b6c6a9aShannken 		((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0)));
4265b6c6a9aShannken 	if (directory_p) {
4275b6c6a9aShannken 		if (VTODE(fvp)->de_flag & DE_RENAME)
4285b6c6a9aShannken 			return EINVAL;
4295b6c6a9aShannken 		VTODE(fvp)->de_flag |= DE_RENAME;
4305b6c6a9aShannken 	}
4315b6c6a9aShannken 
4325b6c6a9aShannken 	reparent_p = (fdvp != tdvp);
4335b6c6a9aShannken 	KASSERT(reparent_p == (VTODE(fdvp)->de_StartCluster !=
4345b6c6a9aShannken 		VTODE(tdvp)->de_StartCluster));
4355b6c6a9aShannken 
4365b6c6a9aShannken 	/*
4375b6c6a9aShannken 	 * XXX Hold it right there -- surely if we crash after
4385b6c6a9aShannken 	 * removede, we'll fail to provide rename's guarantee that
4395b6c6a9aShannken 	 * there will be something at the target pathname?
4405b6c6a9aShannken 	 */
4415b6c6a9aShannken 	if (tvp != NULL) {
4428086f46eSthorpej 		error = msdosfs_removede(VTODE(tdvp), VTODE(tvp), tmlr);
4435b6c6a9aShannken 		if (error)
4445b6c6a9aShannken 			goto out;
445e99cb814Shannken 	}
446e99cb814Shannken 
447e99cb814Shannken 	/*
448e99cb814Shannken 	 * Convert the filename in tcnp into a dos filename. We copy this
449e99cb814Shannken 	 * into the denode and directory entry for the destination
450e99cb814Shannken 	 * file/directory.
451e99cb814Shannken 	 */
4528086f46eSthorpej 	error = msdosfs_uniqdosname(VTODE(tdvp), tcnp, toname);
4535b6c6a9aShannken 	if (error)
4545b6c6a9aShannken 		goto out;
455e99cb814Shannken 
456e99cb814Shannken 	/*
4575b6c6a9aShannken 	 * First write a new entry in the destination directory and
4585b6c6a9aShannken 	 * mark the entry in the source directory as deleted.  Then
4595b6c6a9aShannken 	 * move the denode to the correct hash chain for its new
4605b6c6a9aShannken 	 * location in the filesystem.  And, if we moved a directory,
4615b6c6a9aShannken 	 * then update its .. entry to point to the new parent
4625b6c6a9aShannken 	 * directory.
463e99cb814Shannken 	 */
464e99cb814Shannken 
4655b6c6a9aShannken 	/* Save the old name in case we need to back out.  */
4665b6c6a9aShannken 	memcpy(oldname, VTODE(fvp)->de_Name, 11);
4675b6c6a9aShannken 	memcpy(VTODE(fvp)->de_Name, toname, 11);
468e99cb814Shannken 
4698086f46eSthorpej 	error = msdosfs_createde(VTODE(fvp), VTODE(tdvp), tmlr, 0, tcnp);
470e99cb814Shannken 	if (error) {
4715b6c6a9aShannken 		/* Directory entry didn't take -- back out the name change.  */
4725b6c6a9aShannken 		memcpy(VTODE(fvp)->de_Name, oldname, 11);
4735b6c6a9aShannken 		goto out;
474e99cb814Shannken 	}
475e99cb814Shannken 
4765b6c6a9aShannken 	/*
4775b6c6a9aShannken 	 * createde doesn't increment de_refcnt, but removede
4785b6c6a9aShannken 	 * decrements it.  Go figure.
4795b6c6a9aShannken 	 */
4805b6c6a9aShannken 	KASSERT(VTODE(fvp)->de_refcnt < LONG_MAX);
4815b6c6a9aShannken 	VTODE(fvp)->de_refcnt++;
4825b6c6a9aShannken 
4835b6c6a9aShannken 	/*
4845b6c6a9aShannken 	 * XXX Yes, createde and removede have arguments swapped.  Go figure.
4855b6c6a9aShannken 	 */
4868086f46eSthorpej 	error = msdosfs_removede(VTODE(fdvp), VTODE(fvp), fmlr);
487e99cb814Shannken 	if (error) {
4885b6c6a9aShannken #if 0		/* XXX Back out the new directory entry?  Panic?  */
4898086f46eSthorpej 		(void)msdosfs_removede(VTODE(tdvp), VTODE(fvp), tmlr);
4905b6c6a9aShannken 		memcpy(VTODE(fvp)->de_Name, oldname, 11);
4915b6c6a9aShannken #endif
4925b6c6a9aShannken 		goto out;
493e99cb814Shannken 	}
4945b6c6a9aShannken 
4955b6c6a9aShannken 	pmp = VFSTOMSDOSFS(mp);
4965b6c6a9aShannken 
4975b6c6a9aShannken 	if (!directory_p) {
4985b6c6a9aShannken 		struct denode_key old_key = VTODE(fvp)->de_key;
4995b6c6a9aShannken 		struct denode_key new_key = VTODE(fvp)->de_key;
5005b6c6a9aShannken 
5018086f46eSthorpej 		error = msdosfs_pcbmap(VTODE(tdvp),
5025b6c6a9aShannken 		    de_cluster(pmp, tmlr->mlr_fndoffset), NULL,
5035b6c6a9aShannken 		    &new_key.dk_dirclust, NULL);
5045b6c6a9aShannken 		if (error)	/* XXX Back everything out?  Panic?  */
5055b6c6a9aShannken 			goto out;
5065b6c6a9aShannken 		new_key.dk_diroffset = tmlr->mlr_fndoffset;
507e99cb814Shannken 		if (new_key.dk_dirclust != MSDOSFSROOT)
508e99cb814Shannken 			new_key.dk_diroffset &= pmp->pm_crbomask;
509e99cb814Shannken 		vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key,
510e99cb814Shannken 		    sizeof(old_key), &new_key, sizeof(new_key));
5115b6c6a9aShannken 		VTODE(fvp)->de_key = new_key;
512e99cb814Shannken 		vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key,
5135b6c6a9aShannken 		    sizeof(old_key), &VTODE(fvp)->de_key,
5145b6c6a9aShannken 		    sizeof(VTODE(fvp)->de_key));
515e99cb814Shannken 	}
516e99cb814Shannken 
517e99cb814Shannken 	/*
518e99cb814Shannken 	 * If we moved a directory to a new parent directory, then we must
519e99cb814Shannken 	 * fixup the ".." entry in the moved directory.
520e99cb814Shannken 	 */
5215b6c6a9aShannken 	if (directory_p && reparent_p) {
5225b6c6a9aShannken 		error = msdosfs_rename_replace_dotdot(fvp, fdvp, tdvp, cred);
5235b6c6a9aShannken 		if (error)
5245b6c6a9aShannken 			goto out;
5255b6c6a9aShannken 	}
5265b6c6a9aShannken 
5275b6c6a9aShannken out:;
5285b6c6a9aShannken 	if (tvp != NULL)
5295b6c6a9aShannken 		*tvp_nlinkp = (error ? 1 : 0);
5305b6c6a9aShannken 
5315b6c6a9aShannken 	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
5325b6c6a9aShannken 
5335b6c6a9aShannken 	if (directory_p)
5345b6c6a9aShannken 		VTODE(fvp)->de_flag &=~ DE_RENAME;
5355b6c6a9aShannken 
5365b6c6a9aShannken 	return error;
5375b6c6a9aShannken }
5385b6c6a9aShannken 
5395b6c6a9aShannken /*
5405b6c6a9aShannken  * msdosfs_gro_remove: Rename an object over another link to itself,
5415b6c6a9aShannken  * effectively removing just the original link.
5425b6c6a9aShannken  */
5435b6c6a9aShannken 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)5445b6c6a9aShannken msdosfs_gro_remove(struct mount *mp, kauth_cred_t cred,
5455b6c6a9aShannken     struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
5465b6c6a9aShannken     nlink_t *tvp_nlinkp)
5475b6c6a9aShannken {
5485b6c6a9aShannken 	struct msdosfs_lookup_results *mlr = de;
5495b6c6a9aShannken 	int error;
5505b6c6a9aShannken 
5515b6c6a9aShannken 	KASSERT(mp != NULL);
5525b6c6a9aShannken 	KASSERT(dvp != NULL);
5535b6c6a9aShannken 	KASSERT(cnp != NULL);
5545b6c6a9aShannken 	KASSERT(mlr != NULL);
5555b6c6a9aShannken 	KASSERT(vp != NULL);
5565b6c6a9aShannken 	KASSERT(dvp != vp);
5575b6c6a9aShannken 	KASSERT(dvp->v_mount == mp);
5585b6c6a9aShannken 	KASSERT(vp->v_mount == mp);
5595b6c6a9aShannken 	KASSERT(dvp->v_type == VDIR);
5605b6c6a9aShannken 	KASSERT(vp->v_type != VDIR);
5615b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
5625b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
5635b6c6a9aShannken 
5648086f46eSthorpej 	error = msdosfs_removede(VTODE(dvp), VTODE(vp), mlr);
5655b6c6a9aShannken 
5665b6c6a9aShannken 	*tvp_nlinkp = (error ? 1 : 0);
5675b6c6a9aShannken 
5685b6c6a9aShannken 	return error;
5695b6c6a9aShannken }
5705b6c6a9aShannken 
5715b6c6a9aShannken /*
5725b6c6a9aShannken  * msdosfs_gro_lookup: Look up and save the lookup results.
5735b6c6a9aShannken  */
5745b6c6a9aShannken static int
msdosfs_gro_lookup(struct mount * mp,struct vnode * dvp,struct componentname * cnp,void * de_ret,struct vnode ** vp_ret)5755b6c6a9aShannken msdosfs_gro_lookup(struct mount *mp, struct vnode *dvp,
5765b6c6a9aShannken     struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
5775b6c6a9aShannken {
5785b6c6a9aShannken 	struct msdosfs_lookup_results *mlr_ret = de_ret;
5795b6c6a9aShannken 	struct vnode *vp;
5805b6c6a9aShannken 	int error;
5815b6c6a9aShannken 
5825b6c6a9aShannken 	(void)mp;
5835b6c6a9aShannken 	KASSERT(mp != NULL);
5845b6c6a9aShannken 	KASSERT(dvp != NULL);
5855b6c6a9aShannken 	KASSERT(cnp != NULL);
5865b6c6a9aShannken 	KASSERT(mlr_ret != NULL);
5875b6c6a9aShannken 	KASSERT(vp_ret != NULL);
5885b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
5895b6c6a9aShannken 
5905b6c6a9aShannken 	/* Kludge cargo-culted from dholland's ufs_rename.  */
5915b6c6a9aShannken 	cnp->cn_flags &=~ MODMASK;
5925b6c6a9aShannken 	cnp->cn_flags |= (LOCKPARENT | LOCKLEAF);
5935b6c6a9aShannken 
5945b6c6a9aShannken 	error = relookup(dvp, &vp, cnp, 0);
5955b6c6a9aShannken 	if ((error == 0) && (vp == NULL)) {
5965b6c6a9aShannken 		error = ENOENT;
5975b6c6a9aShannken 		goto out;
5985b6c6a9aShannken 	}
5995b6c6a9aShannken 	if (error)
6005b6c6a9aShannken 		return error;
6015b6c6a9aShannken 
6025b6c6a9aShannken 	/*
6035b6c6a9aShannken 	 * Thanks to VFS insanity, relookup locks vp, which screws us
6045b6c6a9aShannken 	 * in various ways.
6055b6c6a9aShannken 	 */
6065b6c6a9aShannken 	VOP_UNLOCK(vp);
6075b6c6a9aShannken 
6085b6c6a9aShannken out:
6095b6c6a9aShannken 	*mlr_ret = VTODE(dvp)->de_crap;
6105b6c6a9aShannken 	*vp_ret = vp;
6115b6c6a9aShannken 	return error;
6125b6c6a9aShannken }
6135b6c6a9aShannken 
6145b6c6a9aShannken /*
6155b6c6a9aShannken  * msdosfs_rmdired_p: Check whether the directory vp has been rmdired.
6165b6c6a9aShannken  *
6175b6c6a9aShannken  * vp must be locked and referenced.
6185b6c6a9aShannken  */
6195b6c6a9aShannken static bool
msdosfs_rmdired_p(struct vnode * vp)6205b6c6a9aShannken msdosfs_rmdired_p(struct vnode *vp)
6215b6c6a9aShannken {
6225b6c6a9aShannken 
6235b6c6a9aShannken 	KASSERT(vp != NULL);
6245b6c6a9aShannken 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
6255b6c6a9aShannken 	KASSERT(vp->v_type == VDIR);
6265b6c6a9aShannken 
6275b6c6a9aShannken 	return (VTODE(vp)->de_FileSize == 0);
6285b6c6a9aShannken }
6295b6c6a9aShannken 
6305b6c6a9aShannken /*
6315b6c6a9aShannken  * msdosfs_gro_genealogy: Analyze the genealogy of the source and target
6325b6c6a9aShannken  * directories.
6335b6c6a9aShannken  */
6345b6c6a9aShannken static int
msdosfs_gro_genealogy(struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct vnode * tdvp,struct vnode ** intermediate_node_ret)6355b6c6a9aShannken msdosfs_gro_genealogy(struct mount *mp, kauth_cred_t cred,
6365b6c6a9aShannken     struct vnode *fdvp, struct vnode *tdvp,
6375b6c6a9aShannken     struct vnode **intermediate_node_ret)
6385b6c6a9aShannken {
6395b6c6a9aShannken 	struct msdosfsmount *pmp;
6405b6c6a9aShannken 	struct vnode *vp, *dvp;
6415b6c6a9aShannken 	unsigned long dotdot_cn;
6425b6c6a9aShannken 	int error;
6435b6c6a9aShannken 
6445b6c6a9aShannken 	KASSERT(mp != NULL);
6455b6c6a9aShannken 	KASSERT(fdvp != NULL);
6465b6c6a9aShannken 	KASSERT(tdvp != NULL);
6475b6c6a9aShannken 	KASSERT(fdvp != tdvp);
6485b6c6a9aShannken 	KASSERT(intermediate_node_ret != NULL);
6495b6c6a9aShannken 	KASSERT(fdvp->v_mount == mp);
6505b6c6a9aShannken 	KASSERT(tdvp->v_mount == mp);
6515b6c6a9aShannken 	KASSERT(fdvp->v_type == VDIR);
6525b6c6a9aShannken 	KASSERT(tdvp->v_type == VDIR);
6535b6c6a9aShannken 
6545b6c6a9aShannken 	pmp = VFSTOMSDOSFS(mp);
6555b6c6a9aShannken 	KASSERT(pmp != NULL);
6565b6c6a9aShannken 
6575b6c6a9aShannken 	/*
6585b6c6a9aShannken 	 * We need to provisionally lock tdvp to keep rmdir from
6595b6c6a9aShannken 	 * deleting it -- or any ancestor -- at an inopportune moment.
6605b6c6a9aShannken 	 */
6615b6c6a9aShannken 	error = msdosfs_gro_lock_directory(mp, tdvp);
6625b6c6a9aShannken 	if (error)
6635b6c6a9aShannken 		return error;
6645b6c6a9aShannken 
6655b6c6a9aShannken 	vp = tdvp;
6665b6c6a9aShannken 	vref(vp);
6675b6c6a9aShannken 
6685b6c6a9aShannken 	for (;;) {
6695b6c6a9aShannken 		KASSERT(vp->v_type == VDIR);
6705b6c6a9aShannken 
6715b6c6a9aShannken 		/* Did we hit the root without finding fdvp?  */
6725b6c6a9aShannken 		if ((vp->v_vflag & VV_ROOT) != 0) {
6735b6c6a9aShannken 			vput(vp);
6745b6c6a9aShannken 			*intermediate_node_ret = NULL;
6755b6c6a9aShannken 			return 0;
6765b6c6a9aShannken 		}
6775b6c6a9aShannken 
6785b6c6a9aShannken 		error = msdosfs_read_dotdot(vp, cred, &dotdot_cn);
6795b6c6a9aShannken 		if (error) {
6805b6c6a9aShannken 			vput(vp);
6815b6c6a9aShannken 			return error;
6825b6c6a9aShannken 		}
6835b6c6a9aShannken 
6845b6c6a9aShannken 		/* Did we find that fdvp is an ancestor?  */
6855b6c6a9aShannken 		if (VTODE(fdvp)->de_StartCluster == dotdot_cn) {
6865b6c6a9aShannken 			/* Unlock vp, but keep it referenced.  */
6875b6c6a9aShannken 			VOP_UNLOCK(vp);
6885b6c6a9aShannken 			*intermediate_node_ret = vp;
6895b6c6a9aShannken 			return 0;
6905b6c6a9aShannken 		}
6915b6c6a9aShannken 
6925b6c6a9aShannken 		/* Neither -- keep ascending.  */
6935b6c6a9aShannken 
6948086f46eSthorpej 		error = msdosfs_deget(pmp, dotdot_cn,
6958086f46eSthorpej 		    (dotdot_cn ? 0 : MSDOSFSROOT_OFS), &dvp);
6965b6c6a9aShannken 		vput(vp);
6975b6c6a9aShannken 		if (error)
6985b6c6a9aShannken 			return error;
6995b6c6a9aShannken 		error = vn_lock(dvp, LK_EXCLUSIVE);
7005b6c6a9aShannken 		if (error) {
7015b6c6a9aShannken 			vrele(dvp);
7025b6c6a9aShannken 			return error;
7035b6c6a9aShannken 		}
7045b6c6a9aShannken 
7055b6c6a9aShannken 		KASSERT(dvp != NULL);
7065b6c6a9aShannken 		KASSERT(dvp->v_type == VDIR);
7075b6c6a9aShannken 
7085b6c6a9aShannken 		vp = dvp;
7095b6c6a9aShannken 
7105b6c6a9aShannken 		if (msdosfs_rmdired_p(vp)) {
7115b6c6a9aShannken 			vput(vp);
7125b6c6a9aShannken 			return ENOENT;
7135b6c6a9aShannken 		}
7145b6c6a9aShannken 	}
7155b6c6a9aShannken }
7165b6c6a9aShannken 
7175b6c6a9aShannken /*
7185b6c6a9aShannken  * msdosfs_read_dotdot: Store in *cn_ret the cluster number of the
7195b6c6a9aShannken  * parent of the directory vp.
7205b6c6a9aShannken  */
7215b6c6a9aShannken static int
msdosfs_read_dotdot(struct vnode * vp,kauth_cred_t cred,unsigned long * cn_ret)7225b6c6a9aShannken msdosfs_read_dotdot(struct vnode *vp, kauth_cred_t cred, unsigned long *cn_ret)
7235b6c6a9aShannken {
7245b6c6a9aShannken 	struct msdosfsmount *pmp;
7255b6c6a9aShannken 	unsigned long start_cn, cn;
7265b6c6a9aShannken 	struct buf *bp;
7275b6c6a9aShannken 	struct direntry *ep;
7285b6c6a9aShannken 	int error;
7295b6c6a9aShannken 
7305b6c6a9aShannken 	KASSERT(vp != NULL);
7315b6c6a9aShannken 	KASSERT(cn_ret != NULL);
7325b6c6a9aShannken 	KASSERT(vp->v_type == VDIR);
7335b6c6a9aShannken 	KASSERT(VTODE(vp) != NULL);
7345b6c6a9aShannken 
7355b6c6a9aShannken 	pmp = VTODE(vp)->de_pmp;
7365b6c6a9aShannken 	KASSERT(pmp != NULL);
7375b6c6a9aShannken 
7385b6c6a9aShannken 	start_cn = VTODE(vp)->de_StartCluster;
7395b6c6a9aShannken 	error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, start_cn)),
7405b6c6a9aShannken 	    pmp->pm_bpcluster, 0, &bp);
7415b6c6a9aShannken 	if (error)
7425b6c6a9aShannken 		return error;
7435b6c6a9aShannken 
7445b6c6a9aShannken 	ep = (struct direntry *)bp->b_data + 1;
7455b6c6a9aShannken 	if (((ep->deAttributes & ATTR_DIRECTORY) == ATTR_DIRECTORY) &&
7465b6c6a9aShannken 	    (memcmp(ep->deName, "..         ", 11) == 0)) {
7475b6c6a9aShannken 		cn = getushort(ep->deStartCluster);
7485b6c6a9aShannken 		if (FAT32(pmp))
7495b6c6a9aShannken 			cn |= getushort(ep->deHighClust) << 16;
7505b6c6a9aShannken 		*cn_ret = cn;
7515b6c6a9aShannken 		error = 0;
7525b6c6a9aShannken 	} else {
7535b6c6a9aShannken 		error = ENOTDIR;
7545b6c6a9aShannken 	}
7555b6c6a9aShannken 
7565b6c6a9aShannken 	brelse(bp, 0);
7575b6c6a9aShannken 
7585b6c6a9aShannken 	return error;
7595b6c6a9aShannken }
7605b6c6a9aShannken 
7615b6c6a9aShannken /*
7625b6c6a9aShannken  * msdosfs_rename_replace_dotdot: Change the target of the `..' entry of
7635b6c6a9aShannken  * the directory vp from fdvp to tdvp.
7645b6c6a9aShannken  */
7655b6c6a9aShannken static int
msdosfs_rename_replace_dotdot(struct vnode * vp,struct vnode * fdvp,struct vnode * tdvp,kauth_cred_t cred)7665b6c6a9aShannken msdosfs_rename_replace_dotdot(struct vnode *vp,
7675b6c6a9aShannken     struct vnode *fdvp, struct vnode *tdvp,
7685b6c6a9aShannken     kauth_cred_t cred)
7695b6c6a9aShannken {
7705b6c6a9aShannken 	struct msdosfsmount *pmp;
7715b6c6a9aShannken 	struct direntry *dotdotp;
7725b6c6a9aShannken 	struct buf *bp;
7735b6c6a9aShannken 	daddr_t bn;
7745b6c6a9aShannken 	u_long cn;
7755b6c6a9aShannken 	int error;
7765b6c6a9aShannken 
7775b6c6a9aShannken 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
7785b6c6a9aShannken 
7795b6c6a9aShannken 	cn = VTODE(vp)->de_StartCluster;
780e99cb814Shannken 	if (cn == MSDOSFSROOT) {
781e99cb814Shannken 		/* this should never happen */
782e99cb814Shannken 		panic("msdosfs_rename: updating .. in root directory?");
783e99cb814Shannken 	} else
784e99cb814Shannken 		bn = cntobn(pmp, cn);
7855b6c6a9aShannken 
786e99cb814Shannken 	error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
787e99cb814Shannken 	    pmp->pm_bpcluster, B_MODIFY, &bp);
7885b6c6a9aShannken 	if (error)
7895b6c6a9aShannken 		return error;
7905b6c6a9aShannken 
791e99cb814Shannken 	dotdotp = (struct direntry *)bp->b_data + 1;
7925b6c6a9aShannken 	putushort(dotdotp->deStartCluster, VTODE(tdvp)->de_StartCluster);
793e99cb814Shannken 	if (FAT32(pmp)) {
794e99cb814Shannken 		putushort(dotdotp->deHighClust,
7955b6c6a9aShannken 			VTODE(tdvp)->de_StartCluster >> 16);
796e99cb814Shannken 	} else {
797e99cb814Shannken 		putushort(dotdotp->deHighClust, 0);
798e99cb814Shannken 	}
799e99cb814Shannken 
8005b6c6a9aShannken 	error = bwrite(bp);
801e99cb814Shannken 
8025b6c6a9aShannken 	return error;
803e99cb814Shannken }
804e99cb814Shannken 
805e99cb814Shannken /*
8065b6c6a9aShannken  * msdosfs_gro_lock_directory: Lock the directory vp, but fail if it has
8075b6c6a9aShannken  * been rmdir'd.
808e99cb814Shannken  */
8095b6c6a9aShannken static int
msdosfs_gro_lock_directory(struct mount * mp,struct vnode * vp)8105b6c6a9aShannken msdosfs_gro_lock_directory(struct mount *mp, struct vnode *vp)
811e99cb814Shannken {
8125b6c6a9aShannken 	int error;
813e99cb814Shannken 
8145b6c6a9aShannken 	(void)mp;
8155b6c6a9aShannken 	KASSERT(vp != NULL);
816e99cb814Shannken 
817e99cb814Shannken 	error = vn_lock(vp, LK_EXCLUSIVE);
8185b6c6a9aShannken 	if (error)
8195b6c6a9aShannken 		return error;
8205b6c6a9aShannken 
8215b6c6a9aShannken 	KASSERT(mp != NULL);
8225b6c6a9aShannken 	KASSERT(vp->v_mount == mp);
8235b6c6a9aShannken 
8245b6c6a9aShannken 	if (msdosfs_rmdired_p(vp)) {
8255b6c6a9aShannken 		VOP_UNLOCK(vp);
8265b6c6a9aShannken 		return ENOENT;
827e99cb814Shannken 	}
8285b6c6a9aShannken 
8295b6c6a9aShannken 	return 0;
830e99cb814Shannken }
8315b6c6a9aShannken 
8325b6c6a9aShannken static const struct genfs_rename_ops msdosfs_genfs_rename_ops = {
8335b6c6a9aShannken 	.gro_directory_empty_p		= msdosfs_gro_directory_empty_p,
8345b6c6a9aShannken 	.gro_rename_check_possible	= msdosfs_gro_rename_check_possible,
8355b6c6a9aShannken 	.gro_rename_check_permitted	= msdosfs_gro_rename_check_permitted,
8365b6c6a9aShannken 	.gro_remove_check_possible	= msdosfs_gro_remove_check_possible,
8375b6c6a9aShannken 	.gro_remove_check_permitted	= msdosfs_gro_remove_check_permitted,
8385b6c6a9aShannken 	.gro_rename			= msdosfs_gro_rename,
8395b6c6a9aShannken 	.gro_remove			= msdosfs_gro_remove,
8405b6c6a9aShannken 	.gro_lookup			= msdosfs_gro_lookup,
8415b6c6a9aShannken 	.gro_genealogy			= msdosfs_gro_genealogy,
8425b6c6a9aShannken 	.gro_lock_directory		= msdosfs_gro_lock_directory,
8435b6c6a9aShannken };
844