bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: fseek failing without fflush?


From: Eric Blake
Subject: Re: fseek failing without fflush?
Date: Tue, 09 Aug 2011 12:16:12 -0600
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.18) Gecko/20110621 Fedora/3.1.11-1.fc14 Lightning/1.0b3pre Mnenhy/0.8.3 Thunderbird/3.1.11

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   address@hidden    +1-801-349-2682
Libvirt virtualization library http://libvirt.org



reply via email to

[Prev in Thread] Current Thread [Next in Thread]