This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Plea for clarification on bz #12724
- From: Csaba Henk <csaba at lowlife dot hu>
- To: libc-alpha at sourceware dot org
- Date: Wed, 27 Jul 2011 18:31:11 +0300
- Subject: Plea for clarification on bz #12724
Hi,
Bugzilla entry #12724 proposes a (by now committed) change
to fclose(3) behavior based on the following citation from
POSIX.1-2008:
"If the file is not already at EOF, and the file is one capable
of seeking, the file offset of the underlying open file
description shall be adjusted so that the next operation
on the open file description deals with the byte after the
last one read from or written to the stream being closed."
There seems to be an ambiguous corner case, in which case
the behavior adopted is not necessarily wrong, but seems
to be not thought over carefully (see below on the coverage
of the test programs).
The text speaks of "_the_ last one [byte] read from or written to
the stream". That's a definite article, thus it presupposes existence.
So, as I interpret, no definitive statement is here about the case
when there was no read or write done on the stream during its lifetime.
- Is my interpretation correct?
- If not, what rationale is there to make a choice?
- If yes, what practical arguments are there for chosen behavior?
To give some background why I'm asking it:
- Solaris, the OS which is quoted in the bug report as a system
which is compliant in this regard, chooses the other behavior
and does not position the file descriptor on close if no I/O
was done on the stream. This can be demonstrated by the following
modified version of the test program of the bug report, which
does not do I/O on the stream (but repositions a duplicate fd).
The program asserts that the duplicate fd will be positioned back
to the stream's fd's position upon fclose().
With glibc >= 2.14, the program completes peacefully, but on Solaris
the assertion is violated (the original test program, and the one
included in the related commit [libio/bug-fclose1.c of
glibc-2.13-161-gfcabc0f] do complete on both platforms, as they are
not covering this case).
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#define NAME "test-fclose.t"
int
main (void)
{
const char buf[] = "hello world";
int fd;
int fd2;
FILE *f;
/* Prepare a seekable file. */
fd = open (NAME, O_RDWR | O_CREAT | O_TRUNC, 0600);
assert (0 <= fd);
assert (write (fd, buf, sizeof buf) == sizeof buf);
assert (lseek (fd, 1, SEEK_SET) == 1);
/* Create an output stream visiting the file; when it is closed, all
other file descriptors visiting the file must see the new file
position. */
fd2 = dup (fd);
assert (0 <= fd2);
f = fdopen (fd2, "w");
assert (f);
assert(lseek(fd, 4, SEEK_SET) == 4);
assert (fclose (f) == 0);
errno = 0;
assert (lseek (fd2, 0, SEEK_CUR) == -1);
assert (errno == EBADF);
assert (lseek (fd, 0, SEEK_CUR) == 1);
/* Likewise for an input stream. */
fd2 = dup (fd);
assert (0 <= fd2);
f = fdopen (fd2, "r");
assert (f);
assert(lseek(fd, 4, SEEK_SET) == 4);
assert (fclose (f) == 0);
errno = 0;
assert (lseek (fd2, 0, SEEK_CUR) == -1);
assert (errno == EBADF);
assert (lseek (fd, 0, SEEK_CUR) == 1);
/* Clean up. */
assert (remove (NAME) == 0);
return 0;
}
- Ruby 1.8.7 build fails with glibc >= 2.14 due to the
chosen no-I/O-fclose behavior, see
http://redmine.ruby-lang.org/issues/5108
They keep around a duplicated file object
to ensure that the original stdio (which might be
redirected during the program run) can be restored.
Even if that part of the code is not used, at GC time
the duplicate is destroyed, implying an fclose() and
an unexpected repositioning of stdout.
By no means I want to defend this rather hacky practice,
which can be regarded as historical cruft, however, it's
a case which demonstrates the choice in this regard
can break things.
Regards,
Csaba