Possible off-by-one errors in CDROM handling code

From: David Balazic (david.balazic@uni-mb.si)
Date: Wed Apr 25 2001 - 05:26:50 EST


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/



This archive was generated by hypermail 2b29 : Mon Apr 30 2001 - 21:00:13 EST