On Sun, 17 Jun 2007, Tim Kientzle wrote:

Colin Percival wrote:
Tim Kientzle wrote:

 I fear I'll have to avoid seeks ... tape drives on
 FreeBSD seem to return garbage from lseek().

Is there any reason why the tape drivers can't be fixed?

lseek(2) succeeds for almost all file types except pipes.  It just
changes the file offset.  The underlying file "driver" is not
consulted except to check (f_ops->fo_flags & DFLAG_SEEKABLE).
A single fileops flag like this cannot handle any variations
between files under one fileops, and using one gives bugs like
the following:
- the (non-)seekability of fifos was broken for several years
  by not having a fileops struct in fifofs.  The generic fileops
  for "vnodes" was misused, and that fileops of course has to have
  DFLAG_SEEKABLE set so that lseek() succeeds on regular files, so
  lseek() succeded bogusly on fifos.
- devfs's fileops is used for all device files.  It of course has
  to have DFLAG_SEEKABLE set so that lseek() succeeds on disks.
  Thus lseek() succeeds bogusly for amlmost all device files except
  disks, including tapes.

The success of lseek() doesn't mean that i/o at the resulting offset
is possible or even that the resulting offset is considered when
doing i/o.  Not considering the resulting offset is the only thing
that is clearly a bug here.  Among device files, disks are approximately
the only subtype where the offset is even considered.  This works in
some cases as follows:
- physio(9) passes the offset down
- physio() also sets b_blkno for compatibility.  This has always been
  broken, since physio() blindly discards any residual bytes if the
  ofset is not a multiple of DEV_BSIZE.  This used to do bad things
  in disk drivers when userland seeks to an offset that is not a
  multiple of DEV_BSIZE.
- Now, most disks are handled by geom.  geom ignores b_blkno and
  converts the offset to a block nmber itself.  It returns EINVAL at
  i/o time if the offset is not a multiple of the sector size.  This
  is not the only way that an offset can be invalid.  geom returns the
  wrong error EIO for offsets beyond the end of the media.
Tape devices should probably work similarly -- reject offsets that they
can't handle at i/o time.  This might be all offsets except 0.

dd(1) has some messes to handle this problem.  It depends on tape
devices setting D_TAPE in their cdevsw.  At least ast and sa seem to
do this correctly.  It never trusts lseek() to determine seekability
of devices, but depends on disk devices setting D_DISK and memory
devices setting D_MEM in their cdevsw to determine seekability.  It
and other things really want a per-file D_SEEKABLE, but that is not
available, especially in userland across all OS's.  The FreeBSD ioctl
to read the cdevsw flags is also very unportable.  4.4BSD dd refuses to
seek on what it thinks are cdevs, pipes and tapes.  It uses the
MTIOCGET ioctl to attempt to determine if a devices is a tape.

The basic problem is that an lseek() call to a tape
drive returns success exactly as if it worked.
Daniel O'Connor recently sent me the following
ktrace from bsdtar doing a tar -t of an uncompressed
tar archive from his Tandberg TS400 connected to an
Adaptec 29160 controller:

 5378 bsdtar   CALL  lseek(0x3,0,0,0x1)
 5378 bsdtar   RET   lseek 51200/0xc800
 5378 bsdtar   CALL  lseek(0x3,0,0x2f800,0x1)
 5378 bsdtar   RET   lseek 245760/0x3c000

Note that the second call returns a new file position
that's exactly 0x2f800 bytes beyond the former file
position, even though nothing has actually happened.

Because lseek() doesn't go anywhere near the device driver.
I think it is correct for lseek() to have no physical effect
until i/o time.

I think any of the following would be reasonable behaviors:
* lseek() could return ESPIPE ("illegal seek")
* lseek() could return an unchanged file offset
  (indicating that the file position was unchanged by
   the attempted seek).
* lseek() could return ENOTSUP ("unsupported operation")
As I said though, I just don't know that code well
enough to propose a fix.

lseek(9) is trivial compared with making all drivers (or even one)
actually understand seeking :-).

Another minor problem is that top-level code blindly advances the
file offset after successful i/o, although the offset may be
meaningless.  Subsequent lseek(... SEEK_CUR) then show the bogus
offset.

Bruce
_______________________________________________
cvs-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/cvs-all
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to