]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix file descriptor leak on pool import.
authorPawel Jakub Dawidek <pawel@dawidek.net>
Tue, 23 Jan 2024 23:03:48 +0000 (15:03 -0800)
committerGitHub <noreply@github.com>
Tue, 23 Jan 2024 23:03:48 +0000 (15:03 -0800)
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

module/os/freebsd/zfs/zfs_file_os.c

index 9d68499d82ec932de6a2272fd20864299a0e3234..b337932b9dccc33725cbb89246d00c36bbddb9c2 100644 (file)
@@ -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