Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/msdosfs/msdosfs_vnops.c
| Show First 20 Lines • Show All 514 Lines • ▼ Show 20 Lines | #endif | ||||
| return (deupdat(dep, 0)); | return (deupdat(dep, 0)); | ||||
| } | } | ||||
| static int | static int | ||||
| msdosfs_read(struct vop_read_args *ap) | msdosfs_read(struct vop_read_args *ap) | ||||
| { | { | ||||
| int error = 0; | int error = 0; | ||||
| int blsize; | int blsize; | ||||
| int isadir; | |||||
| ssize_t orig_resid; | ssize_t orig_resid; | ||||
| u_int n; | u_int n; | ||||
| u_long diff; | u_long diff; | ||||
| u_long on; | u_long on; | ||||
| daddr_t lbn; | daddr_t lbn; | ||||
| daddr_t rablock; | daddr_t rablock; | ||||
| int rasize; | int rasize; | ||||
| int seqcount; | int seqcount; | ||||
| struct buf *bp; | struct buf *bp; | ||||
| struct vnode *vp = ap->a_vp; | struct vnode *vp = ap->a_vp; | ||||
| struct denode *dep = VTODE(vp); | struct denode *dep = VTODE(vp); | ||||
| struct msdosfsmount *pmp = dep->de_pmp; | struct msdosfsmount *pmp = dep->de_pmp; | ||||
| struct uio *uio = ap->a_uio; | 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. | * If they didn't ask for any data, then we are done. | ||||
| */ | */ | ||||
| orig_resid = uio->uio_resid; | orig_resid = uio->uio_resid; | ||||
| if (orig_resid == 0) | if (orig_resid == 0) | ||||
| return (0); | return (0); | ||||
| /* | /* | ||||
| * The caller is supposed to ensure that | * The caller is supposed to ensure that | ||||
| * uio->uio_offset >= 0 and uio->uio_resid >= 0. | * uio->uio_offset >= 0 and uio->uio_resid >= 0. | ||||
| * We don't need to check for large offsets as in ffs because | * We don't need to check for large offsets as in ffs because | ||||
| * dep->de_FileSize <= MSDOSFS_FILESIZE_MAX < OFF_MAX, so large | * dep->de_FileSize <= MSDOSFS_FILESIZE_MAX < OFF_MAX, so large | ||||
| * offsets cannot cause overflow even in theory. | * offsets cannot cause overflow even in theory. | ||||
| */ | */ | ||||
| seqcount = ap->a_ioflag >> IO_SEQSHIFT; | seqcount = ap->a_ioflag >> IO_SEQSHIFT; | ||||
| isadir = dep->de_Attributes & ATTR_DIRECTORY; | |||||
kib: I think it is still worth keep this check somehow, either as KASSERT, or just as `return… | |||||
Done Inline ActionsGood point- I'm leaning towards replacing it with return (EISDIR). If the situation is volatile enough that we end up here after having checked back up in vn_io_fault, I'm not sure panicking has much utility from a debugging perspective and we're not not necessarily in a state that otherwise warrants it. kevans: Good point- I'm leaning towards replacing it with `return (EISDIR)`. If the situation is… | |||||
| do { | do { | ||||
| if (uio->uio_offset >= dep->de_FileSize) | if (uio->uio_offset >= dep->de_FileSize) | ||||
| break; | break; | ||||
| lbn = de_cluster(pmp, uio->uio_offset); | lbn = de_cluster(pmp, uio->uio_offset); | ||||
| rablock = lbn + 1; | rablock = lbn + 1; | ||||
| blsize = pmp->pm_bpcluster; | blsize = pmp->pm_bpcluster; | ||||
| on = uio->uio_offset & pmp->pm_crbomask; | on = uio->uio_offset & pmp->pm_crbomask; | ||||
| /* | if (de_cn2off(pmp, rablock) >= dep->de_FileSize) { | ||||
| * 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) { | |||||
| error = bread(vp, lbn, blsize, NOCRED, &bp); | error = bread(vp, lbn, blsize, NOCRED, &bp); | ||||
| } else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) { | } else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) { | ||||
| error = cluster_read(vp, dep->de_FileSize, lbn, blsize, | error = cluster_read(vp, dep->de_FileSize, lbn, blsize, | ||||
| NOCRED, on + uio->uio_resid, seqcount, 0, &bp); | NOCRED, on + uio->uio_resid, seqcount, 0, &bp); | ||||
| } else if (seqcount > 1) { | } else if (seqcount > 1) { | ||||
| rasize = blsize; | rasize = blsize; | ||||
| error = breadn(vp, lbn, | error = breadn(vp, lbn, | ||||
| blsize, &rablock, &rasize, 1, NOCRED, &bp); | blsize, &rablock, &rasize, 1, NOCRED, &bp); | ||||
| Show All 10 Lines | do { | ||||
| if (diff < n) | if (diff < n) | ||||
| n = diff; | n = diff; | ||||
| diff = blsize - bp->b_resid; | diff = blsize - bp->b_resid; | ||||
| if (diff < n) | if (diff < n) | ||||
| n = diff; | n = diff; | ||||
| error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio); | error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio); | ||||
| brelse(bp); | brelse(bp); | ||||
| } while (error == 0 && uio->uio_resid > 0 && n != 0); | } 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) | (vp->v_mount->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0) | ||||
| dep->de_flag |= DE_ACCESS; | dep->de_flag |= DE_ACCESS; | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| * Write data to a file or directory. | * Write data to a file or directory. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 1,354 Lines • Show Last 20 Lines | |||||
I think it is still worth keep this check somehow, either as KASSERT, or just as return (EISDIR). After all, msdosfs is often read from untrusted sources or even synthesized on fly.
There of course will be a redundancy with the upper-level check that in principle looked at the same data, but for msdosfs it is better to recheck.