Subject: lseek(2) & read(2) on raw cd(4) device
To: None <tech-kern@netbsd.org>
From: Sean Doran <smd@ebone.net>
List: tech-kern
Date: 08/24/2000 20:58:43
There is some oddity with respect to lseek/read on raw cd devices.

First problem:  lseek(... SEEK_END) always returns 0
Second problem: lseek(...) to a position well beyond the
                 physical size of the CD works
Third problem:  a read(...) if the pointer is beyond the physical
                 size of the CD causes subsequent lseeks and reads
                 to fail with EIO
Fourth problem: read(...) if the pointer is beyond the
                 physical size of the CD causes
                 unpredictable errors (EIO or EINVAL)
                 depending on:
            cd0(ahc0:1:0):  Check Condition on CDB: 0x08 04 84 b8 01 00
                SENSE KEY:  Media Error
               INFO FIELD:  296120
                 ASC/ASCQ:  L-EC Uncorrectable Error
versus
            cd1(ahc0:2:0):  DEFERRED ERROR, key = 0x5
            cd1(ahc0:2:0):  Check Condition on CDB: 0x08 12 9c 24 04 00
                SENSE KEY:  Illegal Request
               INFO FIELD:  1218960
                 ASC/ASCQ:  Logical Block Address Out of Range

(The first gives EIO, the second EINVAL).

Is this buggy, or should it just be documented?  -:)

Incidentally, the fourth problem causes me grief reading
archives with pax/tar/etc when the error is deferred (which is not always).

This is using a standard ISO 9660 CD in a SCSI CD ROM
drive, nothing special at all.  I get similar results
using a SCSI DVD RAM drive (cd1).

Basically, lseek is never checked against the size of the
medium in any way (e.g., against the fake disklabel) and
thus we can lseek anywhere as long as we don't try to
read.   When we try to read, we get an error.

The program below was a quick hack to test return results.
First, we just do a bunch of lseeks.  Note the '0' return from lseek(..., 2).

sean# ./tstcd -noread
lseek(3, 0, 0) -> 0
lseek(3, 2048, 0) -> 2048
lseek(3, 0, 2) -> 0
lseek(3, 606445568, 0) -> 606445568
lseek(3, 0, 1) -> 606445568
lseek(3, 4096, 1) -> 606449664
lseek(3, 0, 1) -> 606449664
lseek(3, 1212891136, 0) -> 1212891136
lseek(3, 0, 1) -> 1212891136
lseek(3, 0, 2) -> 0
lseek(3, 0, 1) -> 0
lseek(3, 8192, 0) -> 8192
lseek(3, 0, 1) -> 8192

Now we try to read 2048 bytes after each lseek.  
Note that we get EIO even after lseek(3, 8192, 0).

sean# ./tstcd
lseek(3, 0, 0) -> 0
block read OK
lseek(3, 2048, 0) -> 2048
block read OK
lseek(3, 0, 2) -> 0
block read OK
lseek(3, 606445568, 0) -> 606445568
block read OK
lseek(3, 0, 1) -> 606447616
block read OK
lseek(3, 4096, 1) -> 606453760
errno = 0  read failed: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error
errno = 5 doing lseek: Input/output error

        Sean.