Fix file descriptor leak on pool import.

Descriptor leak can be easily reproduced by doing:

	# zpool import tank
	# sysctl kern.openfiles
	# zpool export tank; zpool import tank
	# sysctl kern.openfiles

We were leaking four file descriptors on every import.

Similar leak most likely existed when using file-based VDEVs.

External-issue: https://reviews.freebsd.org/D43529
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Pawel Jakub Dawidek <pawel@dawidek.net>
Closes #15630
This commit is contained in:
Pawel Jakub Dawidek 2024-01-23 15:03:48 -08:00 committed by Brian Behlendorf
parent 9e0304c363
commit 3425484eb9
1 changed files with 51 additions and 12 deletions

View File

@ -53,26 +53,65 @@ int
zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp)
{ {
struct thread *td; struct thread *td;
int rc, fd; struct vnode *vp;
struct file *fp;
struct nameidata nd;
int error;
td = curthread; td = curthread;
pwd_ensure_dirs(); pwd_ensure_dirs();
/* 12.x doesn't take a const char * */
rc = kern_openat(td, AT_FDCWD, __DECONST(char *, path), KASSERT((flags & (O_EXEC | O_PATH)) == 0,
UIO_SYSSPACE, flags, mode); ("invalid flags: 0x%x", flags));
if (rc) KASSERT((flags & O_ACCMODE) != O_ACCMODE,
return (SET_ERROR(rc)); ("invalid flags: 0x%x", flags));
fd = td->td_retval[0]; flags = FFLAGS(flags);
td->td_retval[0] = 0;
if (fget(curthread, fd, &cap_no_rights, fpp)) error = falloc_noinstall(td, &fp);
kern_close(td, fd); if (error != 0) {
return (error);
}
fp->f_flag = flags & FMASK;
#if __FreeBSD_version >= 1400043
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path);
#else
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
#endif
error = vn_open(&nd, &flags, mode, fp);
if (error != 0) {
falloc_abort(td, fp);
return (SET_ERROR(error));
}
NDFREE_PNBUF(&nd);
vp = nd.ni_vp;
fp->f_vnode = vp;
if (fp->f_ops == &badfileops) {
finit_vnode(fp, flags, NULL, &vnops);
}
VOP_UNLOCK(vp);
if (vp->v_type != VREG) {
zfs_file_close(fp);
return (SET_ERROR(EACCES));
}
if (flags & O_TRUNC) {
error = fo_truncate(fp, 0, td->td_ucred, td);
if (error != 0) {
zfs_file_close(fp);
return (SET_ERROR(error));
}
}
*fpp = fp;
return (0); return (0);
} }
void void
zfs_file_close(zfs_file_t *fp) zfs_file_close(zfs_file_t *fp)
{ {
fo_close(fp, curthread); fdrop(fp, curthread);
} }
static int static int
@ -263,7 +302,7 @@ zfs_file_get(int fd)
void void
zfs_file_put(zfs_file_t *fp) zfs_file_put(zfs_file_t *fp)
{ {
fdrop(fp, curthread); zfs_file_close(fp);
} }
loff_t loff_t