Date: Mon, 18 Jun 2007 21:07:26 +1000 (EST) From: Bruce Evans <brde@optusnet.com.au> To: Tim Kientzle <kientzle@FreeBSD.org> Cc: cvs-src@FreeBSD.org, src-committers@FreeBSD.org, cvs-all@FreeBSD.org, Colin Percival <cperciva@FreeBSD.org> Subject: Re: cvs commit: src/lib/libarchive archive_read_open_fd.c archive_read_open_filename.c Message-ID: <20070618195605.J43008@delplex.bde.org> In-Reply-To: <46760F7A.8020209@freebsd.org> References: <200706180036.l5I0asac023540@repoman.freebsd.org> <4675DFC8.8060108@freebsd.org> <46760F7A.8020209@freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20070618195605.J43008>