vn_open_vnode(): handle error when fp == NULL

If VOP_ADD_WRITECOUNT() or adv locking failed, so VOP_CLOSE() needs to
be called, we cannot use fp fo_close() when there is no fp.  This occurs
when e.g. kernel code directly calls vn_open() instead of the open(2)
syscall.

In this case, VOP_CLOSE() can be called directly, after possible lock
upgrade.

Reported by:	nvass@gmx.com
PR:	255119
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D29830
This commit is contained in:
Konstantin Belousov 2021-04-19 13:25:30 +03:00
parent 45aec46246
commit 54f98c4dbf

View File

@ -451,16 +451,34 @@ vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
/*
* Error from advlock or VOP_ADD_WRITECOUNT() still requires
* calling VOP_CLOSE() to pair with earlier VOP_OPEN().
* Arrange for that by having fdrop() to use vn_closefile().
*/
if (error != 0) {
fp->f_flag |= FOPENFAILED;
fp->f_vnode = vp;
if (fp->f_ops == &badfileops) {
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
if (fp != NULL) {
/*
* Arrange the call by having fdrop() to use
* vn_closefile(). This is to satisfy
* filesystems like devfs or tmpfs, which
* override fo_close().
*/
fp->f_flag |= FOPENFAILED;
fp->f_vnode = vp;
if (fp->f_ops == &badfileops) {
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
}
vref(vp);
} else {
/*
* If there is no fp, due to kernel-mode open,
* we can call VOP_CLOSE() now.
*/
if (vp->v_type != VFIFO && (fmode & FWRITE) != 0 &&
!MNT_EXTENDED_SHARED(vp->v_mount) &&
VOP_ISLOCKED(vp) != LK_EXCLUSIVE)
vn_lock(vp, LK_UPGRADE | LK_RETRY);
(void)VOP_CLOSE(vp, fmode & (FREAD | FWRITE | FEXEC),
cred, td);
}
vref(vp);
}
ASSERT_VOP_LOCKED(vp, "vn_open_vnode");