Possible off-by-one errors in CDROM handling code

David Balazic (david.balazic@uni-mb.si)
Wed, 25 Apr 2001 12:26:50 +0200


There seem to be some off-by-one errors in the cdrom/ide-cd driver,
where CD capacity is handled in the redhat kernel-source-2.4.1-0.1.9
and also in kernel-2.4.3 )

>From the file linux/drivers/ide/ide-cd.c :
(My comments start with '#')

static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity,
struct request_sense *sense)
{
struct {
__u32 lba;
__u32 blocklen;
} capbuf;

int stat;
struct packet_command pc;

memset(&pc, 0, sizeof(pc));
pc.sense = sense;

pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
pc.buffer = (char *)&capbuf;
pc.buflen = sizeof(capbuf);

stat = cdrom_queue_packet_command(drive, &pc);
if (stat == 0)
*capacity = 1 + be32_to_cpu(capbuf.lba); # this seems correct , the CD reports the
# address of the last sector , sectors are counted from 0

return stat;
}

...

in function static int cdrom_read_toc() :

/* Now try to get the total cdrom capacity. */
minor = (drive->select.b.unit) << PARTN_BITS;
dev = MKDEV(HWIF(drive)->major, minor);
stat = cdrom_get_last_written(dev, (long *)&toc->capacity); # this returns the last sector number ,
# should add one to get capacity !!!
if (stat)
stat = cdrom_read_capacity(drive, &toc->capacity, sense); # this returns the correct capacity
if (stat)
toc->capacity = 0x1fffff;

HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
...

-------------------------

from linux/drivers/cdrom/cdrom.c :

/* return the last written block on the CD-R media. this is for the udf
file system. */
int cdrom_get_last_written(kdev_t dev, long *last_written)
{
struct cdrom_device_info *cdi = cdrom_find_device(dev);
struct cdrom_tocentry toc;
disc_information di;
track_information ti;
__u32 last_track;
int ret = -1;

if (!CDROM_CAN(CDC_GENERIC_PACKET))
goto use_toc;

if ((ret = cdrom_get_disc_info(dev, &di)))
goto use_toc;

last_track = (di.last_track_msb << 8) | di.last_track_lsb;
if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
goto use_toc;

/* if this track is blank, try the previous. */
if (ti.blank) {
last_track--;
if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
goto use_toc;
}

/* if last recorded field is valid, return it. */
if (ti.lra_v) {
*last_written = be32_to_cpu(ti.last_rec_address); # seems OK
} else {
/* make it up instead */
*last_written = be32_to_cpu(ti.track_start) + # on a two sector CD ( sectors 0 and 1 ), this returns :
be32_to_cpu(ti.track_size); # 0 + 2 = 2 , instead of 1 !!!
if (ti.free_blocks)
*last_written -= (be32_to_cpu(ti.free_blocks) + 7); # no idea what this does ,
# maybe it fixes the previous error
}
return 0;

/* this is where we end up if the drive either can't do a
GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if
it fails. then we return the toc contents. */
use_toc:
toc.cdte_format = CDROM_MSF;
toc.cdte_track = CDROM_LEADOUT;
if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))
return ret;
sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA);
*last_written = toc.cdte_addr.lba; # seems OK
return 0;
}

-- 
David Balazic
--------------
"Be excellent to each other." - Bill & Ted
- - - - - - - - - - - - - - - - - - - - - -
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/