Index: lib/libc/sys/read.2 =================================================================== --- lib/libc/sys/read.2 +++ lib/libc/sys/read.2 @@ -28,7 +28,7 @@ .\" @(#)read.2 8.4 (Berkeley) 2/26/94 .\" $FreeBSD$ .\" -.Dd March 30, 2020 +.Dd May 13, 2020 .Dt READ 2 .Os .Sh NAME @@ -199,9 +199,10 @@ The file was marked for non-blocking I/O, and no data were ready to be read. .It Bq Er EISDIR -The file descriptor is associated with a directory residing -on a file system that does not allow regular read operations on -directories (e.g.\& NFS). +The file descriptor is associated with a directory. +The +.Xr readdir 3 +function should be used instead. .It Bq Er EOPNOTSUPP The file descriptor is associated with a file system and file type that do not allow regular read operations on it. Index: sys/fs/ext2fs/ext2_vnops.c =================================================================== --- sys/fs/ext2fs/ext2_vnops.c +++ sys/fs/ext2fs/ext2_vnops.c @@ -2073,7 +2073,7 @@ if (vp->v_type == VLNK) { if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen) panic("%s: short symlink", "ext2_read"); - } else if (vp->v_type != VREG && vp->v_type != VDIR) + } else if (vp->v_type != VREG) panic("%s: type %d", "ext2_read", vp->v_type); #endif orig_resid = uio->uio_resid; Index: sys/fs/msdosfs/msdosfs_vnops.c =================================================================== --- sys/fs/msdosfs/msdosfs_vnops.c +++ sys/fs/msdosfs/msdosfs_vnops.c @@ -520,7 +520,6 @@ { int error = 0; int blsize; - int isadir; ssize_t orig_resid; u_int n; u_long diff; @@ -535,6 +534,18 @@ struct msdosfsmount *pmp = dep->de_pmp; struct uio *uio = ap->a_uio; + /* + * This check duplicates a similar check on the vnode that occurs up in + * the VFS layer, but it's perhaps necessary. msdosfs, in particular, + * are often read from untrusted sources or synthesized on the fly, so + * it does not hurt to ensure that the metadata here is consistent with + * our assumption having entered msdosfs_read. This may not be + * exceptional, so we reject it with an appropriate error rather than + * asserting it or panicking. + */ + if ((dep->de_Attributes & ATTR_DIRECTORY) != 0) + return (EISDIR); + /* * If they didn't ask for any data, then we are done. */ @@ -552,7 +563,6 @@ seqcount = ap->a_ioflag >> IO_SEQSHIFT; - isadir = dep->de_Attributes & ATTR_DIRECTORY; do { if (uio->uio_offset >= dep->de_FileSize) break; @@ -560,21 +570,7 @@ rablock = lbn + 1; blsize = pmp->pm_bpcluster; on = uio->uio_offset & pmp->pm_crbomask; - /* - * If we are operating on a directory file then be sure to - * do i/o with the vnode for the filesystem instead of the - * vnode for the directory. - */ - if (isadir) { - /* convert cluster # to block # */ - error = pcbmap(dep, lbn, &lbn, 0, &blsize); - if (error == E2BIG) { - error = EINVAL; - break; - } else if (error) - break; - error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp); - } else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) { + if (de_cn2off(pmp, rablock) >= dep->de_FileSize) { error = bread(vp, lbn, blsize, NOCRED, &bp); } else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) { error = cluster_read(vp, dep->de_FileSize, lbn, blsize, @@ -601,7 +597,7 @@ error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); - if (!isadir && (error == 0 || uio->uio_resid != orig_resid) && + if ((error == 0 || uio->uio_resid != orig_resid) && (vp->v_mount->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0) dep->de_flag |= DE_ACCESS; return (error); Index: sys/fs/smbfs/smbfs_vnops.c =================================================================== --- sys/fs/smbfs/smbfs_vnops.c +++ sys/fs/smbfs/smbfs_vnops.c @@ -502,7 +502,7 @@ struct uio *uio = ap->a_uio; SMBVDEBUG("\n"); - if (vp->v_type != VREG && vp->v_type != VDIR) + if (vp->v_type != VREG) return EPERM; return smbfs_readvnode(vp, uio, ap->a_cred); } Index: sys/kern/vfs_vnops.c =================================================================== --- sys/kern/vfs_vnops.c +++ sys/kern/vfs_vnops.c @@ -1160,6 +1160,15 @@ doio = uio->uio_rw == UIO_READ ? vn_read : vn_write; vp = fp->f_vnode; + + /* + * Checked here as the entry for fo_read calls. We could restrict this + * to uio->uio_rw == UIO_READ, but attempts to open() a dirfd writable + * will be rejected with EISDIR in vn_open_vnode(). Thus, we won't + * see it. + */ + if (vp->v_type == VDIR) + return (EISDIR); foffset_lock_uio(fp, uio, flags); if (do_vn_io_fault(vp, uio)) { args.kind = VN_IO_FAULT_FOP; Index: sys/ufs/ffs/ffs_vnops.c =================================================================== --- sys/ufs/ffs/ffs_vnops.c +++ sys/ufs/ffs/ffs_vnops.c @@ -566,7 +566,7 @@ if (vp->v_type == VLNK) { if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen) panic("ffs_read: short symlink"); - } else if (vp->v_type != VREG && vp->v_type != VDIR) + } else if (vp->v_type != VREG) panic("ffs_read: type %d", vp->v_type); #endif orig_resid = uio->uio_resid;