On 08/09/2011 12:03 PM, John W. Eaton wrote:
On 9-Aug-2011, Eric Blake wrote:
| I'm thinking this may be a bug in gnulib. I also know that glibc 2.14
| fixed one fclose() bug in how fclose() relates to fflush(), but at the
| expense of introducing another, and that gnulib does not yet detect and
| work around that glibc bug. I'm assuming that you were testing on a
| glibc platform, but which version?
Sorry, I should have included this info in my earlier mail.
$ ldd --version
ldd (Debian EGLIBC 2.13-7) 2.13
Definitely a bug in gnulib's ftell replacement, which is kicking in
because of [e]glibc's fflush() bugs :(
An even simpler reproducer (I tested with glibc 2.13, which should be
similar to your eglibc 2.13 at having the fflush bug that triggers the
gnulib replacements):
$ ./gnulib-tool --with-tests --create-testdir --dir=testdir0 \
fclose fseek ftell
$ cd testdir0
$ cat foo.c
#ifdef with_gnulib
# include <config.h>
#endif
#include <stdio.h>
int main(void) {
FILE *f = fopen("bar", "r");
if (!f)
return 1;
long l = ftell(f);
printf("initial tell %ld\n", l);
int i = fseek(f, 0, SEEK_END);
printf("seek %d\n", i);
l = ftell(f);
printf("second tell %ld\n", l);
i = fclose(f);
printf("close %d\n", i);
return 0;
}
$ gcc -o foo1 -Wall -g foo.c
$ gcc -o foo2 -Wall -g foo.c -Dwith_gnulib -Igllib -I. gllib/libgnu.a
$ strace ./foo1
...
write(1, "initial tell 0\n", 15initial tell 0
) = 15
fstat(3, {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
lseek(3, 0, SEEK_SET) = 0
read(3, "hi\n", 3) = 3
write(1, "seek 0\n", 7seek 0
) = 7
write(1, "second tell 3\n", 14second tell 3
) = 14
close(3) = 0
munmap(0x7f3683389000, 4096) = 0
write(1, "close 0\n", 8close 0
) = 8
exit_group(0) = ?
$ strace ./foo2
write(1, "initial tell 0\n", 15initial tell 0
) = 15
lseek(3, 0, SEEK_END) = 3
write(1, "seek 0\n", 7seek 0
) = 7
write(1, "second tell 0\n", 14second tell 0
) = 14
lseek(3, 0, SEEK_CUR) = 3
close(3) = 0
munmap(0x7f7dc89ab000, 4096) = 0
write(1, "close 0\n", 8close 0
) = 8
exit_group(0) = ?
Ouch. Notice how raw glibc converts fseek(f,0,SEEK_END) into
fstat()/lseek(,0,SEEK_SET)/read(,3) - it probes the file size, and sees
that the entire file would fit in the read buffer, so it repositions the
raw fd to 0, does the read (which repositions the raw fd to 3), then
updates the stream position to 3 visible to the ftell().
But the gnulib replacement converts fseek(f,0,SEEK_END) into
lseek(,0,SEEK_END), without populating the read buffer and apparently
without updating the stream position, at which point the ftell() is
hopelessly lost.
I'm hoping to be able to decipher the gnulib code enough to have a patch
later today.
--
Eric Blake ebl...@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org