git: 2c9cbc2d45b9 - stable/13 - msdosfs: fix rename

Konstantin Belousov kib at FreeBSD.org
Fri Sep 3 01:09:02 UTC 2021


The branch stable/13 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=2c9cbc2d45b94f3f3cc1515fe5481eabfe18b31f

commit 2c9cbc2d45b94f3f3cc1515fe5481eabfe18b31f
Author:     Konstantin Belousov <kib at FreeBSD.org>
AuthorDate: 2021-08-01 17:46:59 +0000
Commit:     Konstantin Belousov <kib at FreeBSD.org>
CommitDate: 2021-09-03 01:08:35 +0000

    msdosfs: fix rename
    
    (cherry picked from commit 95d42526e92cb2a9842d71d3c585aabf32da7534)
---
 sys/fs/msdosfs/denode.h         |   5 +-
 sys/fs/msdosfs/msdosfs_denode.c |   1 +
 sys/fs/msdosfs/msdosfs_lookup.c |  79 ++++----
 sys/fs/msdosfs/msdosfs_vnops.c  | 430 +++++++++++++++++++++-------------------
 4 files changed, 274 insertions(+), 241 deletions(-)

diff --git a/sys/fs/msdosfs/denode.h b/sys/fs/msdosfs/denode.h
index fae05749bede..03e3fa8577ad 100644
--- a/sys/fs/msdosfs/denode.h
+++ b/sys/fs/msdosfs/denode.h
@@ -267,7 +267,7 @@ int msdosfs_lookup(struct vop_cachedlookup_args *);
 int msdosfs_inactive(struct vop_inactive_args *);
 int msdosfs_reclaim(struct vop_reclaim_args *);
 int msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp,
-    struct componentname *cnp, uint64_t *inum);
+    struct componentname *cnp, daddr_t *scnp, u_long *blkoffp);
 #endif
 
 /*
@@ -286,6 +286,7 @@ int createde(struct denode *dep, struct denode *ddep, struct denode **depp, stru
 int deupdat(struct denode *dep, int waitfor);
 int removede(struct denode *pdep, struct denode *dep);
 int detrunc(struct denode *dep, u_long length, int flags, struct ucred *cred);
-int doscheckpath( struct denode *source, struct denode *target);
+int doscheckpath( struct denode *source, struct denode *target,
+    daddr_t *wait_scn);
 #endif	/* _KERNEL || MAKEFS */
 #endif	/* !_FS_MSDOSFS_DENODE_H_ */
diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c
index 9573a85490f0..de73579c7d16 100644
--- a/sys/fs/msdosfs/msdosfs_denode.c
+++ b/sys/fs/msdosfs/msdosfs_denode.c
@@ -166,6 +166,7 @@ deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
 	ldep->de_diroffset = diroffset;
 	ldep->de_inode = inode;
 	lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
+	VN_LOCK_AREC(nvp);	/* for doscheckpath */
 	fc_purge(ldep, 0);	/* init the FAT cache for this denode */
 	error = insmntque(nvp, mntp);
 	if (error != 0) {
diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c
index d47f2969904b..2c3d02db37a0 100644
--- a/sys/fs/msdosfs/msdosfs_lookup.c
+++ b/sys/fs/msdosfs/msdosfs_lookup.c
@@ -67,7 +67,8 @@ int
 msdosfs_lookup(struct vop_cachedlookup_args *ap)
 {
 
-	return (msdosfs_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
+	return (msdosfs_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL,
+	    NULL));
 }
 
 struct deget_dotdot {
@@ -109,8 +110,8 @@ msdosfs_deget_dotdot(struct mount *mp, void *arg, int lkflags,
  * memory denode's will be in synch.
  */
 int
-msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp,
-    struct componentname *cnp, uint64_t *dd_inum)
+msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname
+    *cnp, daddr_t *scnp, u_long *blkoffp)
 {
 	struct mbnambuf nb;
 	daddr_t bn;
@@ -119,11 +120,11 @@ msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp,
 	int slotoffset = 0;
 	int frcn;
 	u_long cluster;
-	int blkoff;
+	u_long blkoff;
 	int diroff;
 	int blsize;
 	int isadir;		/* ~0 if found direntry is a directory	 */
-	u_long scn;		/* starting cluster number		 */
+	daddr_t scn;		/* starting cluster number		 */
 	struct vnode *pdp;
 	struct denode *dp;
 	struct denode *tdp;
@@ -464,8 +465,9 @@ foundroot:
 	if (FAT32(pmp) && scn == MSDOSFSROOT)
 		scn = pmp->pm_rootdirblk;
 
-	if (dd_inum != NULL) {
-		*dd_inum = (uint64_t)pmp->pm_bpcluster * scn + blkoff;
+	if (scnp != NULL) {
+		*scnp = cluster;
+		*blkoffp = blkoff;
 		return (0);
 	}
 
@@ -557,12 +559,15 @@ foundroot:
 		 * Recheck that ".." still points to the inode we
 		 * looked up before pdp lock was dropped.
 		 */
-		error = msdosfs_lookup_ino(pdp, NULL, cnp, &inode1);
+		error = msdosfs_lookup_ino(pdp, NULL, cnp, &scn, &blkoff);
 		if (error) {
 			vput(*vpp);
 			*vpp = NULL;
 			return (error);
 		}
+		if (FAT32(pmp) && scn == MSDOSFSROOT)
+			scn = pmp->pm_rootdirblk;
+		inode1 = scn * pmp->pm_bpcluster + blkoff;
 		if (VTODE(*vpp)->de_inode != inode1) {
 			vput(*vpp);
 			goto restart;
@@ -794,10 +799,9 @@ dosdirempty(struct denode *dep)
  *
  * 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.
  */
 int
-doscheckpath(struct denode *source, struct denode *target)
+doscheckpath(struct denode *source, struct denode *target, daddr_t *wait_scn)
 {
 	daddr_t scn;
 	struct msdosfsmount *pmp;
@@ -806,26 +810,25 @@ doscheckpath(struct denode *source, struct denode *target)
 	struct buf *bp = NULL;
 	int error = 0;
 
-	dep = target;
+	*wait_scn = 0;
+
+	pmp = target->de_pmp;
+	KASSERT(pmp == source->de_pmp,
+	    ("doscheckpath: source and target on different filesystems"));
+
 	if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
-	    (source->de_Attributes & ATTR_DIRECTORY) == 0) {
-		error = ENOTDIR;
-		goto out;
-	}
-	if (dep->de_StartCluster == source->de_StartCluster) {
-		error = EEXIST;
-		goto out;
-	}
-	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;
+	    (source->de_Attributes & ATTR_DIRECTORY) == 0)
+		return (ENOTDIR);
+
+	if (target->de_StartCluster == source->de_StartCluster)
+		return (EEXIST);
+
+	if (target->de_StartCluster == MSDOSFSROOT ||
+	    (FAT32(pmp) && target->de_StartCluster == pmp->pm_rootdirblk))
+		return (0);
 
+	dep = target;
+	vget(DETOV(dep), LK_EXCLUSIVE);
 	for (;;) {
 		if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
 			error = ENOTDIR;
@@ -833,19 +836,22 @@ doscheckpath(struct denode *source, struct denode *target)
 		}
 		scn = dep->de_StartCluster;
 		error = bread(pmp->pm_devvp, cntobn(pmp, scn),
-			      pmp->pm_bpcluster, NOCRED, &bp);
-		if (error)
+		    pmp->pm_bpcluster, NOCRED, &bp);
+		if (error != 0)
 			break;
 
-		ep = (struct direntry *) bp->b_data + 1;
+		ep = (struct direntry *)bp->b_data + 1;
 		if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
 		    bcmp(ep->deName, "..         ", 11) != 0) {
 			error = ENOTDIR;
+			brelse(bp);
 			break;
 		}
+
 		scn = getushort(ep->deStartCluster);
 		if (FAT32(pmp))
 			scn |= getushort(ep->deHighClust) << 16;
+		brelse(bp);
 
 		if (scn == source->de_StartCluster) {
 			error = EINVAL;
@@ -862,15 +868,14 @@ doscheckpath(struct denode *source, struct denode *target)
 		}
 
 		vput(DETOV(dep));
-		brelse(bp);
-		bp = NULL;
+		dep = NULL;
 		/* NOTE: deget() clears dep on error */
-		if ((error = deget(pmp, scn, 0, LK_EXCLUSIVE, &dep)) != 0)
+		error = deget(pmp, scn, 0, LK_EXCLUSIVE | LK_NOWAIT, &dep);
+		if (error != 0) {
+			*wait_scn = scn;
 			break;
+		}
 	}
-out:;
-	if (bp)
-		brelse(bp);
 #ifdef MSDOSFS_DEBUG
 	if (error == ENOTDIR)
 		printf("doscheckpath(): .. not a directory?\n");
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
index 8885ac856588..0ef8e187545d 100644
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -937,24 +937,27 @@ msdosfs_link(struct vop_link_args *ap)
 static int
 msdosfs_rename(struct vop_rename_args *ap)
 {
-	struct vnode *tdvp = ap->a_tdvp;
-	struct vnode *fvp = ap->a_fvp;
-	struct vnode *fdvp = ap->a_fdvp;
-	struct vnode *tvp = ap->a_tvp;
-	struct componentname *tcnp = ap->a_tcnp;
-	struct componentname *fcnp = ap->a_fcnp;
-	struct denode *ip, *xp, *dp, *zp;
+	struct vnode *fdvp, *fvp, *tdvp, *tvp, *vp;
+	struct componentname *fcnp, *tcnp;
+	struct denode *fdip, *fip, *tdip, *tip, *nip;
 	u_char toname[12], oldname[11];
 	u_long from_diroffset, to_diroffset;
+	bool doingdirectory, newparent;
 	u_char to_count;
-	int doingdirectory = 0, newparent = 0;
 	int error;
-	u_long cn, pcl;
-	daddr_t bn;
+	u_long cn, pcl, blkoff;
+	daddr_t bn, wait_scn, scn;
 	struct msdosfsmount *pmp;
+	struct mount *mp;
 	struct direntry *dotdotp;
 	struct buf *bp;
 
+	tdvp = ap->a_tdvp;
+	fvp = ap->a_fvp;
+	fdvp = ap->a_fdvp;
+	tvp = ap->a_tvp;
+	tcnp = ap->a_tcnp;
+	fcnp = ap->a_fcnp;
 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
 
 #ifdef DIAGNOSTIC
@@ -965,19 +968,11 @@ msdosfs_rename(struct vop_rename_args *ap)
 	/*
 	 * Check for cross-device rename.
 	 */
+	mp = fvp->v_mount;
 	if (fvp->v_mount != tdvp->v_mount ||
-	    (tvp && fvp->v_mount != tvp->v_mount)) {
+	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
 		error = EXDEV;
-abortit:
-		if (tdvp == tvp)
-			vrele(tdvp);
-		else
-			vput(tdvp);
-		if (tvp)
-			vput(tvp);
-		vrele(fdvp);
-		vrele(fvp);
-		return (error);
+		goto abortit;
 	}
 
 	/*
@@ -988,11 +983,99 @@ abortit:
 		goto abortit;
 	}
 
-	error = vn_lock(fvp, LK_EXCLUSIVE);
-	if (error)
-		goto abortit;
-	dp = VTODE(fdvp);
-	ip = VTODE(fvp);
+	/*
+	 * When the target exists, both the directory
+	 * and target vnodes are passed locked.
+	 */
+	VOP_UNLOCK(tdvp);
+	if (tvp != NULL && tvp != tdvp)
+		VOP_UNLOCK(tvp);
+
+relock:
+	doingdirectory = newparent = false;
+
+	error = vn_lock(fdvp, LK_EXCLUSIVE);
+	if (error != 0)
+		goto releout;
+	if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
+		VOP_UNLOCK(fdvp);
+		error = vn_lock(tdvp, LK_EXCLUSIVE);
+		if (error != 0)
+			goto releout;
+		VOP_UNLOCK(tdvp);
+		goto relock;
+	}
+
+	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
+	if (error != 0) {
+		VOP_UNLOCK(fdvp);
+		VOP_UNLOCK(tdvp);
+		goto releout;
+	}
+	error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT, &nip);
+	if (error != 0) {
+		VOP_UNLOCK(fdvp);
+		VOP_UNLOCK(tdvp);
+		if (error != EBUSY)
+			goto releout;
+		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE, &nip);
+		if (error != 0)
+			goto releout;
+		vp = fvp;
+		fvp = DETOV(nip);
+		VOP_UNLOCK(fvp);
+		vrele(vp);
+		goto relock;
+	}
+	vrele(fvp);
+	fvp = DETOV(nip);
+	from_diroffset = fdip->de_fndoffset;
+
+	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
+	if (error != 0 && error != EJUSTRETURN) {
+		VOP_UNLOCK(fdvp);
+		VOP_UNLOCK(tdvp);
+		VOP_UNLOCK(fvp);
+		goto releout;
+	}
+	if (error == EJUSTRETURN && tvp != NULL) {
+		vrele(tvp);
+		tvp = NULL;
+	}
+	if (error == 0) {
+		nip = NULL;
+		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT,
+		    &nip);
+		if (tvp != NULL) {
+			vrele(tvp);
+			tvp = NULL;
+		}
+		if (error != 0) {
+			VOP_UNLOCK(fdvp);
+			VOP_UNLOCK(tdvp);
+			VOP_UNLOCK(fvp);
+			if (error != EBUSY)
+				goto releout;
+			error = deget(pmp, scn, blkoff, LK_EXCLUSIVE,
+			    &nip);
+			if (error != 0)
+				goto releout;
+			vput(DETOV(nip));
+			goto relock;
+		}
+		tvp = DETOV(nip);
+	}
+
+	fdip = VTODE(fdvp);
+	fip = VTODE(fvp);
+	tdip = VTODE(tdvp);
+	tip = tvp != NULL ? VTODE(tvp) : NULL;
+
+	/*
+	 * Remember direntry place to use for destination
+	 */
+	to_diroffset = tdip->de_fndoffset;
+	to_count = tdip->de_fndcnt;
 
 	/*
 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
@@ -1000,35 +1083,20 @@ abortit:
 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
 	 * doesn't work if the ".." entry is missing.
 	 */
-	if (ip->de_Attributes & ATTR_DIRECTORY) {
+	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0) {
 		/*
 		 * 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);
+		    fdip == fip ||
+		    (fcnp->cn_flags & ISDOTDOT) != 0 ||
+		    (tcnp->cn_flags & ISDOTDOT) != 0) {
 			error = EINVAL;
-			goto abortit;
+			goto unlock;
 		}
-		ip->de_flag |= DE_RENAME;
-		doingdirectory++;
+		doingdirectory = true;
 	}
 
-	/*
-	 * When the target exists, both the directory
-	 * and target vnodes are returned locked.
-	 */
-	dp = VTODE(tdvp);
-	xp = tvp ? VTODE(tvp) : NULL;
-	/*
-	 * Remember direntry place to use for destination
-	 */
-	to_diroffset = dp->de_fndoffset;
-	to_count = dp->de_fndcnt;
-
 	/*
 	 * If ".." must be changed (ie the directory gets a new
 	 * parent) then the source directory must not be in the
@@ -1040,55 +1108,59 @@ abortit:
 	 * call to doscheckpath().
 	 */
 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
-	VOP_UNLOCK(fvp);
-	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
-		newparent = 1;
+	if (fdip->de_StartCluster != tdip->de_StartCluster)
+		newparent = true;
 	if (doingdirectory && newparent) {
-		if (error)	/* write access check above */
-			goto bad;
-		if (xp != NULL)
-			vput(tvp);
-		/*
-		 * doscheckpath() vput()'s dp,
-		 * so we have to do a relookup afterwards
-		 */
-		error = doscheckpath(ip, dp);
-		if (error)
-			goto out;
+		if (error != 0)	/* write access check above */
+			goto unlock;
+		error = doscheckpath(fip, tdip, &wait_scn);
+		if (wait_scn != 0) {
+			VOP_UNLOCK(fdvp);
+			VOP_UNLOCK(tdvp);
+			VOP_UNLOCK(fvp);
+			if (tvp != NULL && tvp != tdvp)
+				VOP_UNLOCK(tvp);
+			error = deget(pmp, wait_scn, 0, LK_EXCLUSIVE,
+			    &nip);
+			if (error == 0) {
+				vput(DETOV(nip));
+				goto relock;
+			}
+		}
+		if (error != 0)
+			goto unlock;
 		if ((tcnp->cn_flags & SAVESTART) == 0)
 			panic("msdosfs_rename: lost to startdir");
-		error = relookup(tdvp, &tvp, tcnp);
-		if (error)
-			goto out;
-		dp = VTODE(tdvp);
-		xp = tvp ? VTODE(tvp) : NULL;
 	}
 
-	if (xp != NULL) {
+	if (tip != 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)) {
+		if ((tip->de_Attributes & ATTR_DIRECTORY) != 0) {
+			if (!dosdirempty(tip)) {
 				error = ENOTEMPTY;
-				goto bad;
+				goto unlock;
 			}
 			if (!doingdirectory) {
 				error = ENOTDIR;
-				goto bad;
+				goto unlock;
 			}
 			cache_purge(tdvp);
 		} else if (doingdirectory) {
 			error = EISDIR;
-			goto bad;
+			goto unlock;
 		}
-		error = removede(dp, xp);
-		if (error)
-			goto bad;
+		error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
+		MPASS(error == 0);
+		error = removede(tdip, tip);
+		if (error != 0)
+			goto unlock;
 		vput(tvp);
-		xp = NULL;
+		tvp = NULL;
+		tip = NULL;
 	}
 
 	/*
@@ -1096,146 +1168,83 @@ abortit:
 	 * into the denode and directory entry for the destination
 	 * file/directory.
 	 */
-	error = uniqdosname(VTODE(tdvp), tcnp, toname);
-	if (error)
-		goto abortit;
+	error = uniqdosname(tdip, tcnp, toname);
+	if (error != 0)
+		goto unlock;
 
 	/*
-	 * Since from wasn't locked at various places above,
-	 * have to do a relookup here.
+	 * 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.
 	 */
-	fcnp->cn_flags &= ~MODMASK;
-	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-	if ((fcnp->cn_flags & SAVESTART) == 0)
-		panic("msdosfs_rename: lost from startdir");
-	if (!newparent)
-		VOP_UNLOCK(tdvp);
-	if (relookup(fdvp, &fvp, fcnp) == 0)
-		vrele(fdvp);
-	if (fvp == NULL) {
-		/*
-		 * From name has disappeared.
-		 */
-		if (doingdirectory)
-			panic("rename: lost dir entry");
-		if (newparent)
-			VOP_UNLOCK(tdvp);
-		vrele(tdvp);
-		vrele(ap->a_fvp);
-		/*
-		 * fdvp may be locked and has a reference. We need to
-		 * release the lock and reference, unless to and from
-		 * directories are the same.  In that case it is already
-		 * unlocked.
-		 */
-		if (tdvp != fdvp)
-			vput(fdvp);
-		return 0;
+	memcpy(oldname, fip->de_Name, 11);
+	memcpy(fip->de_Name, toname, 11);	/* update denode */
+	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
+	MPASS(error == EJUSTRETURN);
+	error = createde(fip, tdip, NULL, tcnp);
+	if (error != 0) {
+		memcpy(fip->de_Name, oldname, 11);
+		goto unlock;
 	}
-	xp = VTODE(fvp);
-	zp = VTODE(fdvp);
-	from_diroffset = zp->de_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 fip is for a directory, then its name should always
+	 * be "." since it is for the directory entry in the
+	 * directory itself (msdosfs_lookup() always translates
+	 * to the "." entry so as to get a unique denode, except
+	 * for the root directory there are different
+	 * complications).  However, we just corrupted its name
+	 * to pass the correct name to createde().  Undo this.
 	 */
-	if (xp != ip) {
-		if (doingdirectory)
-			panic("rename: lost dir entry");
-		if (newparent)
-			VOP_UNLOCK(fdvp);
-		vrele(ap->a_fvp);
-		xp = NULL;
-	} else {
-		vrele(fvp);
-		xp = NULL;
-
-		/*
-		 * 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_fndoffset = to_diroffset;
-		dp->de_fndcnt = to_count;
-		error = createde(ip, dp, (struct denode **)0, tcnp);
-		if (error) {
-			memcpy(ip->de_Name, oldname, 11);
-			if (newparent)
-				VOP_UNLOCK(fdvp);
-			VOP_UNLOCK(fvp);
-			goto bad;
-		}
-		/*
-		 * If ip is for a directory, then its name should always
-		 * be "." since it is for the directory entry in the
-		 * directory itself (msdosfs_lookup() always translates
-		 * to the "." entry so as to get a unique denode, except
-		 * for the root directory there are different
-		 * complications).  However, we just corrupted its name
-		 * to pass the correct name to createde().  Undo this.
-		 */
-		if ((ip->de_Attributes & ATTR_DIRECTORY) != 0)
-			memcpy(ip->de_Name, oldname, 11);
-		ip->de_refcnt++;
-		zp->de_fndoffset = from_diroffset;
-		error = removede(zp, ip);
-		if (error) {
-			/* XXX should downgrade to ro here, fs is corrupt */
-			if (newparent)
-				VOP_UNLOCK(fdvp);
-			VOP_UNLOCK(fvp);
-			goto bad;
-		}
-		if (!doingdirectory) {
-			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
-				       &ip->de_dirclust, 0);
-			if (error) {
-				/* XXX should downgrade to ro here, fs is corrupt */
-				if (newparent)
-					VOP_UNLOCK(fdvp);
-				VOP_UNLOCK(fvp);
-				goto bad;
-			}
-			if (ip->de_dirclust == MSDOSFSROOT)
-				ip->de_diroffset = to_diroffset;
-			else
-				ip->de_diroffset = to_diroffset & pmp->pm_crbomask;
+	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0)
+		memcpy(fip->de_Name, oldname, 11);
+	fip->de_refcnt++;
+	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
+	MPASS(error == 0);
+	error = removede(fdip, fip);
+	if (error != 0) {
+		/* XXX should downgrade to ro here, fs is corrupt */
+		goto unlock;
+	}
+	if (!doingdirectory) {
+		error = pcbmap(tdip, de_cluster(pmp, to_diroffset), 0,
+		    &fip->de_dirclust, 0);
+		if (error != 0) {
+			/*
+			 * XXX should downgrade to ro here,
+			 * fs is corrupt
+			 */
+			goto unlock;
 		}
-		reinsert(ip);
-		if (newparent)
-			VOP_UNLOCK(fdvp);
+		if (fip->de_dirclust == MSDOSFSROOT)
+			fip->de_diroffset = to_diroffset;
+		else
+			fip->de_diroffset = to_diroffset & pmp->pm_crbomask;
 	}
+	reinsert(fip);
 
 	/*
 	 * If we moved a directory to a new parent directory, then we must
 	 * fixup the ".." entry in the moved directory.
 	 */
 	if (doingdirectory && newparent) {
-		cn = ip->de_StartCluster;
+		cn = fip->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, bn, pmp->pm_bpcluster,
-			      NOCRED, &bp);
-		if (error) {
+		    NOCRED, &bp);
+		if (error != 0) {
 			/* XXX should downgrade to ro here, fs is corrupt */
-			VOP_UNLOCK(fvp);
-			goto bad;
+			goto unlock;
 		}
 		dotdotp = (struct direntry *)bp->b_data + 1;
-		pcl = dp->de_StartCluster;
+		pcl = tdip->de_StartCluster;
 		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
 			pcl = MSDOSFSROOT;
 		putushort(dotdotp->deStartCluster, pcl);
@@ -1245,8 +1254,7 @@ abortit:
 			bdwrite(bp);
 		else if ((error = bwrite(bp)) != 0) {
 			/* XXX should downgrade to ro here, fs is corrupt */
-			VOP_UNLOCK(fvp);
-			goto bad;
+			goto unlock;
 		}
 	}
 
@@ -1258,17 +1266,35 @@ abortit:
 	 * namecache entries that were installed for this direntry.
 	 */
 	cache_purge(fvp);
-	VOP_UNLOCK(fvp);
-bad:
-	if (xp)
-		vput(tvp);
+
+unlock:
+	vput(fdvp);
+	vput(fvp);
+	if (tvp != NULL) {
+		if (tvp != tdvp)
+			vput(tvp);
+		else
+			vrele(tvp);
+	}
 	vput(tdvp);
-out:
-	ip->de_flag &= ~DE_RENAME;
+	return (error);
+releout:
+	vrele(tdvp);
+	if (tvp != NULL)
+		vrele(tvp);
+	vrele(fdvp);
+	vrele(fvp);
+	return (error);
+abortit:
+	if (tdvp == tvp)
+		vrele(tdvp);
+	else
+		vput(tdvp);
+	if (tvp != NULL)
+		vput(tvp);
 	vrele(fdvp);
 	vrele(fvp);
 	return (error);
-
 }
 
 static struct {
@@ -1428,7 +1454,7 @@ msdosfs_rmdir(struct vop_rmdir_args *ap)
 	 *  non-empty.)
 	 */
 	error = 0;
-	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
+	if (!dosdirempty(ip)) {
 		error = ENOTEMPTY;
 		goto out;
 	}


More information about the dev-commits-src-branches mailing list