Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 11 Nov 1997 16:59:08 +1100
From:      Bruce Evans <bde@zeta.org.au>
To:        freebsd-bugs@hub.freebsd.org, tege@nada.kth.se
Subject:   Re: bin/4961: Problems with fseek and fprints
Message-ID:  <199711110559.QAA20598@godzilla.zeta.org.au>

next in thread | raw e-mail | index | archive | help
>   >1) fseek is ignored on files that were fdopen'ed in append mode.
> 
>   This is not a bug.  ANSI C says that writes are only allowed at the end
>   of file when opening in append mode (e.g. any write has an implicit

It would be a bug to always completely ignore fseek on such files,
since it sometimes has side effects if it succeeds (e.g., at least under
POSIX, it must flush any buffered output and synchronise with with the
underlying fd.  Flushing must set file timestamps and these can be peeked
at outside of stdio, so flushing can't be delayed).

>   seek to end preceding it).  Open with "r+" if you want to write
>   somewhere other than the end of file.

This won't work if the file descriptor is open in O_APPEND mode.  fdopen
should not and does not clear to O_APPEND flag, so it is impossible to
write somewhere other than the end of a file using stdio.

> (Does ANSI say anything about this?  I thought POSIX was what spec'd these
> functions?)

ANSI permits fseek to fail if the request cannot be satisfied.  Under
POSIX, all offsets can be lseeked to although only EOF can be written to,
so it isn't reasonable to reject requests because of the offset, and in
fact fseek does whatever the FILE's _seek function does.

fseek is just useless in the O_APPEND case, at least if the _seek
function is lseek.  You can get the same effect using fflush.  fseek is
more useless than lseek, since lseek(fd, off, SEEK_EOF) is useful for
determining the current file, but the corresponding `fseek(fp, off,
SEEK_EOF); ftell(fp);' is not, at least in POSIX, since the result of
ftell() is unspecified for the O_APPEND case :-(.  FreeBSD stdio actually
depends on this loophole to be POSIXly correct: after `fseek(fp, 0L,
SEEK_SET); putc('!', fp);', on a nonempty file, ftell() reports the
bogus offset of 1.  The fseek() sets the underlying file offset to 0
and the putc() advances it to previous_file_size+1 by writing at EOF,
and ftell() apparently doesn't bother to synchronise with the file system.

> I checked several other Unices and, and they behaved as I want.  If
> FreeBSD's behaviour is right, there is no obvious way of closing say stdout
> and reopen what was associated to it and then keep writing to a specified
> position.  "w" truncates the file and "r+" cannot be done on stdout since
> the underlying file descriptor disallows reads.

You'll have to use fcntl() to change the O_APPEND flag at suitable times.

There are some other problems with freopen() on files that are actually
file descriptors.  The files must be kept open to prevent their fd's
from going away, but ANSI specifies closing of the old file.  Closing
the file sometimes has side effects.  NIST PCTS detects brokenness here
by noticing that side effects for named pipes don't occur.

>   >2) The second fprintf below sets errno even while no error really
>   >   happens.
> 
>   errno is not set on a 2.2.2-RELEASE system; on a 3.0-CURRENT system,
>   errno is indeed set to ENOENT.  Interestingly enough, it's the first
>   fprintf that's setting errno.  This is definitely worth looking into;
>   I have no clue what fprintf might be doing that would cause a ENOENT.

As discussed already, checking errno is usually the wrong way to
detect errors, and the ENOENT may be for malloc attempting to open
/etc/malloc.conf.

Bruce



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