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
This commit is contained in:
Konstantin Belousov 2022-01-06 03:47:31 +02:00
parent 41e85eeab9
commit aaaa4fb54e
2 changed files with 32 additions and 11 deletions

View file

@ -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);

View file

@ -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 */