svn commit: r204474 - head/sys/fs/msdosfs
Konstantin Belousov
kib at FreeBSD.org
Sun Feb 28 17:17:29 UTC 2010
Author: kib
Date: Sun Feb 28 17:17:29 2010
New Revision: 204474
URL: http://svn.freebsd.org/changeset/base/204474
Log:
Fix the race between dotdot lookup and forced unmount, by using
msdosfs-specific variant of vn_vget_ino(), msdosfs_deget_dotdot().
As was done for UFS, relookup the dotdot denode after the call to
msdosfs_deget_dotdot(), because vnode lock is dropped and directory
might be moved.
Tested by: pho
MFC after: 3 weeks
Modified:
head/sys/fs/msdosfs/msdosfs_lookup.c
Modified: head/sys/fs/msdosfs/msdosfs_lookup.c
==============================================================================
--- head/sys/fs/msdosfs/msdosfs_lookup.c Sun Feb 28 17:16:43 2010 (r204473)
+++ head/sys/fs/msdosfs/msdosfs_lookup.c Sun Feb 28 17:17:29 2010 (r204474)
@@ -61,6 +61,18 @@
#include <fs/msdosfs/fat.h>
#include <fs/msdosfs/msdosfsmount.h>
+static int msdosfs_lookup_(struct vnode *vdp, struct vnode **vpp,
+ struct componentname *cnp, u_int64_t *inum);
+static int msdosfs_deget_dotdot(struct vnode *vp, u_long cluster, int blkoff,
+ struct vnode **rvp);
+
+int
+msdosfs_lookup(struct vop_cachedlookup_args *ap)
+{
+
+ return (msdosfs_lookup_(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
+}
+
/*
* When we search a directory the blocks containing directory entries are
* read and examined. The directory entries contain information that would
@@ -76,18 +88,11 @@
* out to disk. This way disk blocks containing directory entries and in
* memory denode's will be in synch.
*/
-int
-msdosfs_lookup(ap)
- struct vop_cachedlookup_args /* {
- struct vnode *a_dvp;
- struct vnode **a_vpp;
- struct componentname *a_cnp;
- } */ *ap;
+static int
+msdosfs_lookup_(struct vnode *vdp, struct vnode **vpp,
+ struct componentname *cnp, u_int64_t *dd_inum)
{
struct mbnambuf nb;
- struct vnode *vdp = ap->a_dvp;
- struct vnode **vpp = ap->a_vpp;
- struct componentname *cnp = ap->a_cnp;
daddr_t bn;
int error;
int slotcount;
@@ -109,6 +114,7 @@ msdosfs_lookup(ap)
int flags = cnp->cn_flags;
int nameiop = cnp->cn_nameiop;
int unlen;
+ u_int64_t inode1;
int wincnt = 1;
int chksum = -1, chksum_ok;
@@ -119,12 +125,14 @@ msdosfs_lookup(ap)
#endif
dp = VTODE(vdp);
pmp = dp->de_pmp;
- *vpp = NULL;
+ if (vpp != NULL)
+ *vpp = NULL;
#ifdef MSDOSFS_DEBUG
printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
vdp, dp, dp->de_Attributes);
#endif
+ restart:
/*
* If they are going after the . or .. entry in the root directory,
* they won't find it. DOS filesystems don't have them in the root
@@ -436,6 +444,11 @@ foundroot:
if (FAT32(pmp) && scn == MSDOSFSROOT)
scn = pmp->pm_rootdirblk;
+ if (dd_inum != NULL) {
+ *dd_inum = (uint64_t)pmp->pm_bpcluster * scn + blkoff;
+ return (0);
+ }
+
/*
* If deleting, and at end of pathname, return
* parameters which can be used to remove file.
@@ -508,23 +521,25 @@ foundroot:
* inodes from the root, moving down the directory tree. Thus
* when following backward pointers ".." we must unlock the
* parent directory before getting the requested directory.
- * There is a potential race condition here if both the current
- * and parent directories are removed before the VFS_VGET for the
- * inode associated with ".." returns. We hope that this occurs
- * infrequently since we cannot avoid this race condition without
- * implementing a sophisticated deadlock detection algorithm.
- * Note also that this simple deadlock detection scheme will not
- * work if the filesystem has any hard links other than ".."
- * that point backwards in the directory structure.
*/
pdp = vdp;
if (flags & ISDOTDOT) {
- VOP_UNLOCK(pdp, 0);
- error = deget(pmp, cluster, blkoff, &tdp);
- vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
+ error = msdosfs_deget_dotdot(pdp, cluster, blkoff, vpp);
if (error)
return (error);
- *vpp = DETOV(tdp);
+ /*
+ * Recheck that ".." still points to the inode we
+ * looked up before pdp lock was dropped.
+ */
+ error = msdosfs_lookup_(pdp, NULL, cnp, &inode1);
+ if (error) {
+ vput(*vpp);
+ return (error);
+ }
+ if (VTODE(*vpp)->de_inode != inode1) {
+ vput(*vpp);
+ goto restart;
+ }
} else if (dp->de_StartCluster == scn && isadir) {
VREF(vdp); /* we want ourself, ie "." */
*vpp = vdp;
@@ -542,6 +557,49 @@ foundroot:
return (0);
}
+static int
+msdosfs_deget_dotdot(struct vnode *vp, u_long cluster, int blkoff,
+ struct vnode **rvp)
+{
+ struct mount *mp;
+ struct msdosfsmount *pmp;
+ struct denode *rdp;
+ int ltype, error;
+
+ mp = vp->v_mount;
+ pmp = VFSTOMSDOSFS(mp);
+ ltype = VOP_ISLOCKED(vp);
+ KASSERT(ltype == LK_EXCLUSIVE || ltype == LK_SHARED,
+ ("msdosfs_deget_dotdot: vp not locked"));
+
+ error = vfs_busy(mp, MBF_NOWAIT);
+ if (error != 0) {
+ vfs_ref(mp);
+ VOP_UNLOCK(vp, 0);
+ error = vfs_busy(mp, 0);
+ vn_lock(vp, ltype | LK_RETRY);
+ vfs_rel(mp);
+ if (error != 0)
+ return (ENOENT);
+ if (vp->v_iflag & VI_DOOMED) {
+ vfs_unbusy(mp);
+ return (ENOENT);
+ }
+ }
+ VOP_UNLOCK(vp, 0);
+ error = deget(pmp, cluster, blkoff, &rdp);
+ vfs_unbusy(mp);
+ if (error == 0)
+ *rvp = DETOV(rdp);
+ vn_lock(vp, ltype | LK_RETRY);
+ if (vp->v_iflag & VI_DOOMED) {
+ if (error == 0)
+ vput(*rvp);
+ error = ENOENT;
+ }
+ return (error);
+}
+
/*
* dep - directory entry to copy into the directory
* ddep - directory to add to
More information about the svn-src-head
mailing list