Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Apr 2004 16:26:47 +1000 (EST)
From:      Bruce Evans <bde@zeta.org.au>
To:        Guido Laubner <Guido.Laubner@gmx.de>
Cc:        freebsd-gnats-submit@freebsd.org
Subject:   Re: kern/65786: Incorrect fifo semantics
Message-ID:  <20040420152430.J1413@gamplex.bde.org>
In-Reply-To: <200404192029.i3JKTWGJ000679@www.freebsd.org>
References:  <200404192029.i3JKTWGJ000679@www.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 19 Apr 2004, Guido Laubner wrote:

> >Description:
> I found a strange behaviour of dd. Digging into it I believe it's a
> problem of the fifo created by mkfifo(2).
> It looks like the fifo allows (and reports correct return codes) for
> lseek(2), but when reading form the fifo after the lseek advancing the "read cursor", reading starts from position 0.
> Comparing this with a pipe, the pipe behaves correctly :
> It allows for lseek and reading starts at the position given in the lseek(2) syscall.
> Assuming the pipe is correct, the fifo is not implemented correctly in that is does not position the read "cursor" for the next read properly.
> If my assumption is wrong (so the pipe semantics would not hold for
> a fifo) the lseek syscall to a fifo should return an error.

lseek() on fifos shall return an error.  From POSIX.1-2001-draft7:

% 23980            The lseek( ) function shall fail if:
% ...
% 23986            [ESPIPE]             The fildes argument is associated with a pipe, FIFO, or socket.

This was fixed in 1996 in rev.1.52 of vfs_syscalls.c and associated changes,
but was broken in 2003 in rev.1.319 of vfs_syscalls.c and associated changes.

% Index: vfs_syscalls.c
% ===================================================================
% RCS file: /home/ncvs/src/sys/kern/vfs_syscalls.c,v
% retrieving revision 1.318
% retrieving revision 1.319
% diff -u -2 -r1.318 -r1.319
% --- vfs_syscalls.c	11 Jun 2003 00:56:59 -0000	1.318
% +++ vfs_syscalls.c	18 Jun 2003 19:53:59 -0000	1.319
% ...
% @@ -1342,5 +1342,5 @@
%  	if ((error = fget(td, uap->fd, &fp)) != 0)
%  		return (error);
% -	if (fp->f_type != DTYPE_VNODE) {
% +	if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) {
%  		fdrop(fp, td);
%  		return (ESPIPE);

The bug in rev.1.319 is that fifos use the same f_ops as regular files,
so they cannot be distinguished from regular files using f_ops.

Rev.1.52 worked by putting their classification in f_type and using it
here and in a couple of other places.

Fifos are still classified by f_type, but the correct fix is not as
simple as changing the above to:

	if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE) ||
	    fp->f_type != DTYPE_VNODE) {
		...

since several places would need to be changed similarly and then the
changes in rev.1.352 would increase the number of special cases a
lot instead of reducing it a little.

lseek() is also broken for device files (seeking on ttys and /dev/null
bogusly succeeds), but this is an old bug.  Some devices are seekable,
so lseek() must work for them, but there is no way for indivual devices
to indicate if they support seeking so lseek() must works for all
devices.

Breaking lseek() of course confuses any program that expects lseek()
to actually work if it returns successfully, and are used on files
where it is broken.  Such program/usage combinations are apparently
surprisingly rare.  The problem only showed up here in a fifo test
program.  dd knows not to trust lseek() on devices only.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040420152430.J1413>