Subject: lib/8359: ANSI fread() and fwrite() interactions aren't sufficiently documented
To: None <gnats-bugs@gnats.netbsd.org>
From: None <cgd@netbsd.org>
List: netbsd-bugs
Date: 09/09/1999 13:11:10
>Number:         8359
>Category:       lib
>Synopsis:       ANSI fread() and fwrite() interactions aren't sufficiently documented
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          doc-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Sep  9 12:35:01 1999
>Last-Modified:
>Originator:     Chris Demetriou
>Organization:
>Release:        NetBSD 1.4.1
>Environment:
irrelevant.  NetBSD 1.4.1 (manual pages)
>Description:
ANSI specifies that fread() and fwrite() apparently can't be intermingled
in a program in what i'd describe as the naive, or straightforward, way:

For instance, the program:

#include <stdio.h>
#include <assert.h>

main(int argc, char *argv[])
{
        FILE *f;
        int rv, v[4];

        f = fopen(argv[1], "r+");
        assert(f != NULL);

        rv = fseek(f, 16, SEEK_SET);
        assert (rv != -1);

        printf("%ld\n", ftell(f));

        rv = fread(v, sizeof v, 1, f);
        assert (rv != -1);

        printf("%ld\n", ftell(f));

        v[0] = 0;
        rv = fwrite(&v[0], sizeof v[0], 1, f);

        printf("%ld\n", ftell(f));
}

is apparently _NOT_ correct ANSI C, because in order to write
at the naively expected location (the location of the file pointer
after the read), you need to seek first.

Other systems, e.g. linux, do what is expected, but I understand that
our behaviour is allowed by the relevant standards.  However, this
limitation is not documented in our manual pages, or in K&R, or in
any place obvious to a "naive" C programmer such as myself.

>How-To-Repeat:
take the program above, compile it on linux.  run it like:

4 [zaphod1] tmp % dd if=/dev/zero of=bar bs=1024 count=1
1+0 records in
1+0 records out
5 [zaphod1] tmp % ./a.out bar
16
32
36

Note that the written data went to the expected place.

then compile and run it on NetBSD, and note that the output is:

50 [bacon] tmp % dd if=/dev/zero of=bar bs=1024 count=1
1+0 records in
1+0 records out
1024 bytes transferred in 1 secs (1024 bytes/sec)
51 [bacon] tmp % ./a.out bar
16
32
1028

and in fact the file's been _extended_ with the data that
was written rather than having a few bytes rewritten in place.

Look at K&R (ed. 2) and our manual pages for explanation of the
difference, which is, uh, non-intuitive.  Find none.  Consult your
favorite C gurus, and have them explain it to you.  Wonder how
you (and your boss, who's the person who presented you the
non-functional code to begin with) have never managed to trip
this issue before...
>Fix:
Update the manual pages to explain why this obvious fread()/fwrite()
sequence is not correct.

If ambitious, and the efficiency cost of actually making the code
work as expected isn't significant, fix the manual page _and_ fix
the code so that it works like sane people would expect.  8-)
It'd be interesting to know how many systems produce weird-but-allowed
results like we do...
>Audit-Trail:
>Unformatted: