summaryrefslogtreecommitdiffstats
path: root/sys/dev/fdc
diff options
context:
space:
mode:
authorjoerg <joerg@FreeBSD.org>1996-11-02 23:31:11 +0000
committerjoerg <joerg@FreeBSD.org>1996-11-02 23:31:11 +0000
commit56a5526f33821f3afcfcdc1ba3384cd40c0bbf8c (patch)
treeb32696e37c44311bb13ce2341fa15ccc85d5b1ce /sys/dev/fdc
parent9434a8b4e5607a49321ce4d08dddc9d505e93723 (diff)
downloadFreeBSD-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.c64
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);
OpenPOWER on IntegriCloud