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-all mailing list