git: aaaa4fb54e5c - main - msdosfs: use mntfs vnode for pm_devvp

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 08 Jan 2022 04:29:39 UTC
The branch main has been updated by kib:

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

commit aaaa4fb54e5c427b7a8d305c3562fe5dde4ec8ec
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-01-06 01:47:31 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-01-08 04:21:58 +0000

    msdosfs: use mntfs vnode for pm_devvp
    
    to prevent races with devfs VCHR vnode reclamation, same as it was
    done for UFS.
    
    Reported by:    pho
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D33721
---
 sys/fs/msdosfs/msdosfs_vfsops.c | 42 ++++++++++++++++++++++++++++++-----------
 sys/fs/msdosfs/msdosfsmount.h   |  1 +
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
index 3c2606ab5a91..15f90c2e0e23 100644
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -53,6 +53,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/buf.h>
+#include <sys/bufobj.h>
 #include <sys/conf.h>
 #include <sys/fcntl.h>
 #include <sys/iconv.h>
@@ -64,6 +65,7 @@
 #include <sys/namei.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
+#include <sys/rwlock.h>
 #include <sys/stat.h>
 #include <sys/taskqueue.h>
 #include <sys/vnode.h>
@@ -229,7 +231,7 @@ msdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
 static int
 msdosfs_mount(struct mount *mp)
 {
-	struct vnode *devvp;	  /* vnode for blk device to mount */
+	struct vnode *devvp, *odevvp;	  /* vnode for blk device to mount */
 	struct thread *td;
 	/* msdosfs specific mount control block */
 	struct msdosfsmount *pmp = NULL;
@@ -302,17 +304,17 @@ msdosfs_mount(struct mount *mp)
 			 * If upgrade to read-write by non-root, then verify
 			 * that user has necessary permissions on the device.
 			 */
-			devvp = pmp->pm_devvp;
-			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
-			error = VOP_ACCESS(devvp, VREAD | VWRITE,
+			odevvp = pmp->pm_odevvp;
+			vn_lock(odevvp, LK_EXCLUSIVE | LK_RETRY);
+			error = VOP_ACCESS(odevvp, VREAD | VWRITE,
 			    td->td_ucred, td);
 			if (error)
 				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
 			if (error) {
-				VOP_UNLOCK(devvp);
+				VOP_UNLOCK(odevvp);
 				return (error);
 			}
-			VOP_UNLOCK(devvp);
+			VOP_UNLOCK(odevvp);
 			g_topology_lock();
 			error = g_access(pmp->pm_cp, 0, 1, 0);
 			g_topology_unlock();
@@ -385,7 +387,7 @@ msdosfs_mount(struct mount *mp)
 #endif
 	} else {
 		vput(devvp);
-		if (devvp != pmp->pm_devvp)
+		if (devvp != pmp->pm_odevvp)
 			return (EINVAL);	/* XXX needs translation */
 	}
 	if (error) {
@@ -408,11 +410,12 @@ msdosfs_mount(struct mount *mp)
 }
 
 static int
-mountmsdosfs(struct vnode *devvp, struct mount *mp)
+mountmsdosfs(struct vnode *odevvp, struct mount *mp)
 {
 	struct msdosfsmount *pmp;
 	struct buf *bp;
 	struct cdev *dev;
+	struct vnode *devvp;
 	union bootsector *bsp;
 	struct byte_bpb33 *b33;
 	struct byte_bpb50 *b50;
@@ -427,10 +430,13 @@ mountmsdosfs(struct vnode *devvp, struct mount *mp)
 	pmp = NULL;
 	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
 
+	devvp = mntfs_allocvp(mp, odevvp);
+	VOP_UNLOCK(odevvp);
+	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
 	dev = devvp->v_rdev;
 	if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0,
 	    (uintptr_t)mp) == 0) {
-		VOP_UNLOCK(devvp);
+		mntfs_freevp(devvp);
 		return (EBUSY);
 	}
 	g_topology_lock();
@@ -438,11 +444,14 @@ mountmsdosfs(struct vnode *devvp, struct mount *mp)
 	g_topology_unlock();
 	if (error != 0) {
 		atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
-		VOP_UNLOCK(devvp);
+		mntfs_freevp(devvp);
 		return (error);
 	}
 	dev_ref(dev);
 	bo = &devvp->v_bufobj;
+	BO_LOCK(&odevvp->v_bufobj);
+	odevvp->v_bufobj.bo_flag |= BO_NOBUFS;
+	BO_UNLOCK(&odevvp->v_bufobj);
 	VOP_UNLOCK(devvp);
 	if (dev->si_iosize_max != 0)
 		mp->mnt_iosize_max = dev->si_iosize_max;
@@ -709,6 +718,7 @@ mountmsdosfs(struct vnode *devvp, struct mount *mp)
 	 * fillinusemap() needs pm_devvp.
 	 */
 	pmp->pm_devvp = devvp;
+	pmp->pm_odevvp = odevvp;
 	pmp->pm_dev = dev;
 
 	/*
@@ -764,7 +774,12 @@ error_exit:
 		free(pmp, M_MSDOSFSMNT);
 		mp->mnt_data = NULL;
 	}
+	BO_LOCK(&odevvp->v_bufobj);
+	odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
+	BO_UNLOCK(&odevvp->v_bufobj);
 	atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
+	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+	mntfs_freevp(devvp);
 	dev_rel(dev);
 	return (error);
 }
@@ -841,11 +856,16 @@ msdosfs_unmount(struct mount *mp, int mntflags)
 	if (susp)
 		vfs_write_resume(mp, VR_START_WRITE);
 
+	vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
 	g_topology_lock();
 	g_vfs_close(pmp->pm_cp);
 	g_topology_unlock();
+	BO_LOCK(&pmp->pm_odevvp->v_bufobj);
+	pmp->pm_odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
+	BO_UNLOCK(&pmp->pm_odevvp->v_bufobj);
 	atomic_store_rel_ptr((uintptr_t *)&pmp->pm_dev->si_mountpt, 0);
-	vrele(pmp->pm_devvp);
+	mntfs_freevp(pmp->pm_devvp);
+	vrele(pmp->pm_odevvp);
 	dev_rel(pmp->pm_dev);
 	free(pmp->pm_inusemap, M_MSDOSFSFAT);
 	lockdestroy(&pmp->pm_fatlock);
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
index 6a0ba896dff5..5caa40f2b648 100644
--- a/sys/fs/msdosfs/msdosfsmount.h
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -83,6 +83,7 @@ struct msdosfsmount {
 	mode_t pm_dirmask;	/* mask to and with file protection bits
 				   for directories */
 	struct vnode *pm_devvp;	/* vnode for character device mounted */
+	struct vnode *pm_odevvp;/* real devfs vnode */
 	struct cdev *pm_dev;	/* character device mounted */
 	struct bpb50 pm_bpb;	/* BIOS parameter blk for this fs */
 	u_long pm_BlkPerSec;	/* How many DEV_BSIZE blocks fit inside a physical sector */