diff options
author | joerg <joerg@FreeBSD.org> | 1996-11-02 23:31:11 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 1996-11-02 23:31:11 +0000 |
commit | 56a5526f33821f3afcfcdc1ba3384cd40c0bbf8c (patch) | |
tree | b32696e37c44311bb13ce2341fa15ccc85d5b1ce /sys/dev/fdc | |
parent | 9434a8b4e5607a49321ce4d08dddc9d505e93723 (diff) | |
download | FreeBSD-src-56a5526f33821f3afcfcdc1ba3384cd40c0bbf8c.zip FreeBSD-src-56a5526f33821f3afcfcdc1ba3384cd40c0bbf8c.tar.gz |
Fix the broken EOF handling in the floppy driver. The most obvious
appearance of this bug was the malfunctioning -M option in GNU tar (it
worked only by explicitly specifying -L).
Reviewed by: bde, and partially corrected accoring to his comments
Candidate for 2.2, IMHO even for 2.1.6.
Diffstat (limited to 'sys/dev/fdc')
-rw-r--r-- | sys/dev/fdc/fdc.c | 64 |
1 files changed, 36 insertions, 28 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c index 631157d..5076b88 100644 --- a/sys/dev/fdc/fdc.c +++ b/sys/dev/fdc/fdc.c @@ -43,7 +43,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.91 1996/07/23 21:51:33 phk Exp $ + * $Id: fd.c,v 1.92 1996/09/06 23:07:18 phk Exp $ * */ @@ -84,8 +84,6 @@ #include <sys/devfsext.h> #endif -#define b_cylin b_resid /* XXX now spelled b_cylinder elsewhere */ - /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX @@ -1013,7 +1011,7 @@ fdclose(dev_t dev, int flags, int mode, struct proc *p) void fdstrategy(struct buf *bp) { - long nblocks, blknum; + unsigned nblocks, blknum, cando; int s; fdcu_t fdcu; fdu_t fdu; @@ -1060,18 +1058,30 @@ fdstrategy(struct buf *bp) /* * Set up block calculations. */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + if (bp->b_blkno > 20000000) { + /* + * Reject unreasonably high block number, prevent the + * multiplication below from overflowing. + */ + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; + bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; + if (blknum <= nblocks) { + cando = (nblocks - blknum) * fdblk; + bp->b_resid = bp->b_bcount - cando; + if (cando == 0) + goto bad; /* not actually bad but EOF */ } else { - bp->b_error = ENOSPC; + bp->b_error = EINVAL; bp->b_flags |= B_ERROR; + goto bad; } - goto bad; } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); bp->b_pblkno = bp->b_blkno; s = splbio(); tqdisksort(&fdc->head, bp); @@ -1195,7 +1205,7 @@ static int fdstate(fdcu_t fdcu, fdc_p fdc) { int read, format, head, sec = 0, sectrac, st0, cyl, st3; - unsigned long blknum; + unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; @@ -1228,8 +1238,16 @@ fdstate(fdcu_t fdcu, fdc_p fdc) } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; - if(format) + if(format) { finfo = (struct fd_formb *)bp->b_un.b_addr; + fd->skip = (char *)&(finfo->fd_formb_cylno(0)) + - (char *)finfo; + } + if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { + blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk + + fd->skip/fdblk; + b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); + } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); @@ -1270,13 +1288,13 @@ fdstate(fdcu_t fdcu, fdc_p fdc) fdc->state = DOSEEK; break; case DOSEEK: - if (bp->b_cylin == fd->track) + if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } if (fd_cmd(fdcu, 3, NE7CMD_SEEK, - fd->fdsu, bp->b_cylin * fd->ft->steptrac, + fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* @@ -1298,7 +1316,7 @@ fdstate(fdcu_t fdcu, fdc_p fdc) /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { - int descyl = bp->b_cylin * fd->ft->steptrac; + int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, @@ -1362,14 +1380,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc) } } - fd->track = bp->b_cylin; - if(format) - fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - - (char *)finfo; + fd->track = b_cylinder; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk - + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; @@ -1487,20 +1500,15 @@ fdstate(fdcu_t fdcu, fdc_p fdc) } /* All OK */ fd->skip += fdblk; - if (!format && fd->skip < bp->b_bcount) + if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk - + fd->skip/fdblk; - bp->b_cylin = - (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; - bp->b_resid = 0; TAILQ_REMOVE(&fdc->head, bp, b_act); biodone(bp); fdc->fd = (fd_p) 0; @@ -1654,7 +1662,7 @@ retrier(fdcu) } bp->b_flags |= B_ERROR; bp->b_error = EIO; - bp->b_resid = bp->b_bcount - fdc->fd->skip; + bp->b_resid += bp->b_bcount - fdc->fd->skip; TAILQ_REMOVE(&fdc->head, bp, b_act); fdc->fd->skip = 0; biodone(bp); |