Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144327854
D31464.id93929.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D31464.id93929.diff
View Options
diff --git a/sys/fs/msdosfs/denode.h b/sys/fs/msdosfs/denode.h
--- a/sys/fs/msdosfs/denode.h
+++ b/sys/fs/msdosfs/denode.h
@@ -173,7 +173,6 @@
#define DE_CREATE 0x0008 /* Creation time update */
#define DE_ACCESS 0x0010 /* Access time update */
#define DE_MODIFIED 0x0020 /* Denode has been modified */
-#define DE_RENAME 0x0040 /* Denode is in the process of being renamed */
/* Maximum size of a file on a FAT filesystem */
#define MSDOSFS_FILESIZE_MAX 0xFFFFFFFFLL
@@ -267,13 +266,15 @@
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, daddr_t *scnp, u_long *blkoffp);
#endif
/*
* Internal service routine prototypes.
*/
struct componentname;
-int deget(struct msdosfsmount *, u_long, u_long, struct denode **);
+int deget(struct msdosfsmount *, u_long, u_long, int, struct denode **);
int uniqdosname(struct denode *, struct componentname *, u_char *);
int readep(struct msdosfsmount *pmp, u_long dirclu, u_long dirofs, struct buf **bpp, struct direntry **epp);
@@ -286,6 +287,7 @@
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
--- a/sys/fs/msdosfs/msdosfs_denode.c
+++ b/sys/fs/msdosfs/msdosfs_denode.c
@@ -92,11 +92,12 @@
* diroffset is relative to the beginning of the root directory,
* otherwise it is cluster relative.
* diroffset - offset past begin of cluster of denode we want
+ * lkflags - locking flags (LK_NOWAIT)
* depp - returns the address of the gotten denode.
*/
int
deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
- struct denode **depp)
+ int lkflags, struct denode **depp)
{
int error;
uint64_t inode;
@@ -107,9 +108,11 @@
struct buf *bp;
#ifdef MSDOSFS_DEBUG
- printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
- pmp, dirclust, diroffset, depp);
+ printf("deget(pmp %p, dirclust %lu, diroffset %lx, flags %#x, "
+ "depp %p)\n",
+ pmp, dirclust, diroffset, flags, depp);
#endif
+ MPASS((lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE);
/*
* On FAT32 filesystems, root is a (more or less) normal
@@ -133,7 +136,7 @@
*/
inode = (uint64_t)pmp->pm_bpcluster * dirclust + diroffset;
- error = vfs_hash_get(mntp, inode, LK_EXCLUSIVE, curthread, &nvp,
+ error = vfs_hash_get(mntp, inode, lkflags, curthread, &nvp,
de_vncmpf, &inode);
if (error)
return (error);
@@ -164,6 +167,7 @@
ldep->de_inode = inode;
cluster_init_vn(&ldep->de_clusterw);
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) {
@@ -171,7 +175,7 @@
*depp = NULL;
return (error);
}
- error = vfs_hash_insert(nvp, inode, LK_EXCLUSIVE, curthread, &xvp,
+ error = vfs_hash_insert(nvp, inode, lkflags, curthread, &xvp,
de_vncmpf, &inode);
if (error) {
*depp = NULL;
diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c
--- a/sys/fs/msdosfs/msdosfs_lookup.c
+++ b/sys/fs/msdosfs/msdosfs_lookup.c
@@ -63,14 +63,12 @@
#include <fs/msdosfs/fat.h>
#include <fs/msdosfs/msdosfsmount.h>
-static int msdosfs_lookup_(struct vnode *vdp, struct vnode **vpp,
- struct componentname *cnp, uint64_t *inum);
-
int
msdosfs_lookup(struct vop_cachedlookup_args *ap)
{
- return (msdosfs_lookup_(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 {
@@ -89,7 +87,8 @@
pmp = VFSTOMSDOSFS(mp);
dd_arg = arg;
- error = deget(pmp, dd_arg->cluster, dd_arg->blkoff, &rdp);
+ error = deget(pmp, dd_arg->cluster, dd_arg->blkoff,
+ LK_EXCLUSIVE, &rdp);
if (error == 0)
*rvp = DETOV(rdp);
return (error);
@@ -110,9 +109,9 @@
* out to disk. This way disk blocks containing directory entries and in
* memory denode's will be in synch.
*/
-static int
-msdosfs_lookup_(struct vnode *vdp, struct vnode **vpp,
- struct componentname *cnp, uint64_t *dd_inum)
+int
+msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname
+ *cnp, daddr_t *scnp, u_long *blkoffp)
{
struct mbnambuf nb;
daddr_t bn;
@@ -121,11 +120,11 @@
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;
@@ -466,8 +465,9 @@
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 = scn;
+ *blkoffp = blkoff;
return (0);
}
@@ -498,7 +498,7 @@
*vpp = vdp;
return (0);
}
- error = deget(pmp, cluster, blkoff, &tdp);
+ error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE, &tdp);
if (error)
return (error);
*vpp = DETOV(tdp);
@@ -526,7 +526,8 @@
if (dp->de_StartCluster == scn && isadir)
return (EISDIR);
- if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
+ if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
+ &tdp)) != 0)
return (error);
*vpp = DETOV(tdp);
cnp->cn_flags |= SAVENAME;
@@ -558,12 +559,13 @@
* Recheck that ".." still points to the inode we
* looked up before pdp lock was dropped.
*/
- error = msdosfs_lookup_(pdp, NULL, cnp, &inode1);
+ error = msdosfs_lookup_ino(pdp, NULL, cnp, &scn, &blkoff);
if (error) {
vput(*vpp);
*vpp = NULL;
return (error);
}
+ inode1 = scn * pmp->pm_bpcluster + blkoff;
if (VTODE(*vpp)->de_inode != inode1) {
vput(*vpp);
goto restart;
@@ -572,7 +574,8 @@
VREF(vdp); /* we want ourself, ie "." */
*vpp = vdp;
} else {
- if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
+ if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
+ &tdp)) != 0)
return (error);
*vpp = DETOV(tdp);
}
@@ -711,7 +714,7 @@
else
diroffset = 0;
}
- return deget(pmp, dirclust, diroffset, depp);
+ return (deget(pmp, dirclust, diroffset, LK_EXCLUSIVE, depp));
}
return 0;
@@ -794,10 +797,9 @@
*
* 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 +808,26 @@
struct buf *bp = NULL;
int error = 0;
- dep = target;
+ *wait_scn = 0;
+
+ pmp = target->de_pmp;
+ lockmgr_assert(&pmp->pm_checkpath_lock, KA_XLOCKED);
+ 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 +835,22 @@
}
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 +867,14 @@
}
vput(DETOV(dep));
- brelse(bp);
- bp = NULL;
+ dep = NULL;
/* NOTE: deget() clears dep on error */
- if ((error = deget(pmp, scn, 0, &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_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -469,6 +469,7 @@
pmp->pm_bo = bo;
lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0);
+ lockinit(&pmp->pm_checkpath_lock, 0, "msdoscp", 0, 0);
/*
* Initialize ownerships and permissions, since nothing else will
@@ -740,6 +741,7 @@
}
if (pmp) {
lockdestroy(&pmp->pm_fatlock);
+ lockdestroy(&pmp->pm_checkpath_lock);
free(pmp->pm_inusemap, M_MSDOSFSFAT);
free(pmp, M_MSDOSFSMNT);
mp->mnt_data = NULL;
@@ -829,6 +831,7 @@
dev_rel(pmp->pm_dev);
free(pmp->pm_inusemap, M_MSDOSFSFAT);
lockdestroy(&pmp->pm_fatlock);
+ lockdestroy(&pmp->pm_checkpath_lock);
free(pmp, M_MSDOSFSMNT);
mp->mnt_data = NULL;
MNT_ILOCK(mp);
@@ -847,7 +850,7 @@
#ifdef MSDOSFS_DEBUG
printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
#endif
- error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep);
+ error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, LK_EXCLUSIVE, &ndep);
if (error)
return (error);
*vpp = DETOV(ndep);
@@ -988,7 +991,8 @@
struct denode *dep;
int error;
- error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep);
+ error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
+ LK_EXCLUSIVE, &dep);
if (error) {
*vpp = NULLVP;
return (error);
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -937,24 +937,27 @@
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 checkpath_locked, 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 @@
/*
* 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,92 @@
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);
+
+ checkpath_locked = false;
+
+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);
+
+ 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);
/*
* Be sure we are not renaming ".", "..", or an alias of ".". This
@@ -1000,34 +1076,28 @@
* "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;
+ tdip = VTODE(tdvp);
+ tip = tvp != NULL ? VTODE(tvp) : NULL;
+
/*
* Remember direntry place to use for destination
*/
- to_diroffset = dp->de_fndoffset;
- to_count = dp->de_fndcnt;
+ to_diroffset = tdip->de_fndoffset;
+ to_count = tdip->de_fndcnt;
/*
* If ".." must be changed (ie the directory gets a new
@@ -1040,55 +1110,61 @@
* 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;
+ lockmgr(&pmp->pm_checkpath_lock, LK_EXCLUSIVE, NULL);
+ checkpath_locked = true;
+ error = doscheckpath(fip, tdip, &wait_scn);
+ if (wait_scn != 0) {
+ lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
+ checkpath_locked = false;
+ 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 = removede(tdip, tip);
+ if (error != 0)
+ goto unlock;
vput(tvp);
- xp = NULL;
+ tvp = NULL;
+ tip = NULL;
}
/*
@@ -1096,146 +1172,84 @@
* 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;
+
+ from_diroffset = fdip->de_fndoffset;
/*
- * 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 */
+ tdip->de_fndoffset = to_diroffset;
+ tdip->de_fndcnt = to_count;
+ 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++;
+ fdip->de_fndoffset = from_diroffset;
+ 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 +1259,7 @@
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 +1271,38 @@
* namecache entries that were installed for this direntry.
*/
cache_purge(fvp);
- VOP_UNLOCK(fvp);
-bad:
- if (xp)
- vput(tvp);
+
+unlock:
+ if (checkpath_locked)
+ lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
+ 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:
+ MPASS(!checkpath_locked);
+ 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 +1462,7 @@
* non-empty.)
*/
error = 0;
- if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
+ if (!dosdirempty(ip)) {
error = ENOTEMPTY;
goto out;
}
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
--- a/sys/fs/msdosfs/msdosfsmount.h
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -114,6 +114,7 @@
void *pm_d2u; /* DOS->Local iconv handle */
#ifndef MAKEFS
struct lock pm_fatlock; /* lockmgr protecting allocations */
+ struct lock pm_checkpath_lock; /* protects doscheckpath result */
#endif
};
diff --git a/sys/kern/vfs_hash.c b/sys/kern/vfs_hash.c
--- a/sys/kern/vfs_hash.c
+++ b/sys/kern/vfs_hash.c
@@ -93,8 +93,14 @@
error = vget_finish(vp, flags, vs);
if (error == ENOENT && (flags & LK_NOWAIT) == 0)
break;
- if (error)
+ if (error != 0)
return (error);
+ if (vp->v_hash != hash ||
+ (fn != NULL && fn(vp, arg))) {
+ vput(vp);
+ /* Restart the bucket walk. */
+ break;
+ }
*vpp = vp;
return (0);
}
@@ -190,6 +196,7 @@
void
vfs_hash_rehash(struct vnode *vp, u_int hash)
{
+ ASSERT_VOP_ELOCKED(vp, "rehash requires excl lock");
rw_wlock(&vfs_hash_lock);
LIST_REMOVE(vp, v_hashlist);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 8, 9:12 PM (17 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28498105
Default Alt Text
D31464.id93929.diff (25 KB)
Attached To
Mode
D31464: Some fixes for msdosfs_rename and ufs_rename
Attached
Detach File
Event Timeline
Log In to Comment