From a4bf6baaeb70a01a4c13fd6139ebdc97bad172e9 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Tue, 23 Jan 2024 15:03:48 -0800 Subject: [PATCH] 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 Signed-off-by: Pawel Jakub Dawidek Closes #15630 --- module/os/freebsd/zfs/zfs_file_os.c | 63 +++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/module/os/freebsd/zfs/zfs_file_os.c b/module/os/freebsd/zfs/zfs_file_os.c index 9d68499d82ec..b337932b9dcc 100644 --- a/module/os/freebsd/zfs/zfs_file_os.c +++ b/module/os/freebsd/zfs/zfs_file_os.c @@ -50,26 +50,65 @@ int zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) { struct thread *td; - int rc, fd; + struct vnode *vp; + struct file *fp; + struct nameidata nd; + int error; td = curthread; pwd_ensure_dirs(); - /* 12.x doesn't take a const char * */ - rc = kern_openat(td, AT_FDCWD, __DECONST(char *, path), - UIO_SYSSPACE, flags, mode); - if (rc) - return (SET_ERROR(rc)); - fd = td->td_retval[0]; - td->td_retval[0] = 0; - if (fget(curthread, fd, &cap_no_rights, fpp)) - kern_close(td, fd); + + KASSERT((flags & (O_EXEC | O_PATH)) == 0, + ("invalid flags: 0x%x", flags)); + KASSERT((flags & O_ACCMODE) != O_ACCMODE, + ("invalid flags: 0x%x", flags)); + flags = FFLAGS(flags); + + error = falloc_noinstall(td, &fp); + 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); } void zfs_file_close(zfs_file_t *fp) { - fo_close(fp, curthread); + fdrop(fp, curthread); } static int @@ -260,7 +299,7 @@ zfs_file_get(int fd) void zfs_file_put(zfs_file_t *fp) { - fdrop(fp, curthread); + zfs_file_close(fp); } loff_t