Bug 8518 - freopen() in append mode followed by ftell() gives strange results
Summary: freopen() in append mode followed by ftell() gives strange results
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 1998-10-31 13:20 UTC by Arjan.deVet
Modified: 2001-06-05 19:00 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Arjan.deVet 1998-10-31 13:20:00 UTC
During testing INN 2.2beta I discovered a possible 'bug' in the
freopen() call when used with "a" (append mode). The first ftell()
call always give 0 (irrespective of the current file length) and
subsequent writes to the file ftell() does not tell the current
write position in the file but the number of bytes written.

Fix: Fix provided by Steinar Haugh, sthaug@nethelp.no:



See further "Possible bug in freopen()?" thread on hackers@freebsd.org.--u0k6xoLkXPjkJE1lLRQSrzOOxKH8y64KPJNEWs0huHwVGMLR
Content-Type: text/plain; name="file.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="file.diff"

*** lib/libc/stdio/freopen.c.orig       Tue May 30 07:41:43 1995
--- lib/libc/stdio/freopen.c    Sat Oct 31 10:06:08 1998
***************
*** 151,155 ****
--- 151,166 ----
        fp->_write = __swrite;
        fp->_seek = __sseek;
        fp->_close = __sclose;
+ 
+       /*
+        * When opening in append mode, even though we use O_APPEND,
+        * we need to seek to the end so that ftell() gets the right
+        * answer.  If the user then alters the seek pointer, or
+        * the file extends, this will fail, but there is not much
+        * we can do about this.  (We could set __SAPP and check in
+        * fseek and ftell.)
+        */
+       if (oflags & O_APPEND)
+               (void) __sseek((void *)fp, (fpos_t)0, SEEK_END);
        return (fp);
  }
How-To-Repeat: #include <stdio.h>
#include <unistd.h>

main () {
    FILE *f, *g;
    long i;

    g = fopen("/tmp/test", "a");
    f = freopen("/tmp/test", "a", g);
    i = ftell(f);
    printf("%d\n", i);
    fprintf(f, "test");
    i = ftell(f);
    printf("%d\n", i);
    fclose(f);
}

Create empty /tmp/test file and run the program three times. Each
time it will print 0 and 4. BSD/OS prints 0 4, 0 8 and 0 12. Solaris
and Linux print 0 4, 4 8 and 8 12.
Comment 1 Bruce Evans 1998-11-10 08:25:11 UTC
>During testing INN 2.2beta I discovered a possible 'bug' in the
>freopen() call when used with "a" (append mode). The first ftell()
>call always give 0 (irrespective of the current file length) and
>subsequent writes to the file ftell() does not tell the current
>write position in the file but the number of bytes written.

This seems to be only a ``bug''.  POSIX.1-1990 says:

"If the stream is opened in append mode or if the O_APPEND flag is
set as a consequence of dealing with other handles on the file, the
result of ftell() on that stream is unspecified".

>>How-To-Repeat:
>#include <stdio.h>
>#include <unistd.h>
>
>main () {
>    FILE *f, *g;
>    long i;
>
>    g = fopen("/tmp/test", "a");
>    f = freopen("/tmp/test", "a", g);
>    i = ftell(f);
>    printf("%d\n", i);
>    fprintf(f, "test");
>    i = ftell(f);
>    printf("%d\n", i);
>    fclose(f);
>}
>
>Create empty /tmp/test file and run the program three times. Each
>time it will print 0 and 4. BSD/OS prints 0 4, 0 8 and 0 12. Solaris
>and Linux print 0 4, 4 8 and 8 12.

The BSD/OS behaviour is the least surprising.  The initial offsets
of zero may even be required (if no other process writes to the file).
POSIX.1 specifies fopen() to just use open(), and freopen() to do much
the same thing as fopen().  Therefore, the initial offsets at the file
descriptor level are zero, and for "a+" mode, an initial read would
start at the beginning of the file.  Only the fuzzy specification of
permits the initial offsets at the stream level to be nonzero.

Bruce
Comment 2 Arjan.deVet 1998-11-15 14:03:00 UTC
Bruce Evans:

>>Create empty /tmp/test file and run the program three times. Each
>>time it will print 0 and 4. BSD/OS prints 0 4, 0 8 and 0 12. Solaris
>>and Linux print 0 4, 4 8 and 8 12.
>
>The BSD/OS behaviour is the least surprising.  The initial offsets
>of zero may even be required (if no other process writes to the file).
>POSIX.1 specifies fopen() to just use open(), and freopen() to do much
>the same thing as fopen().  Therefore, the initial offsets at the file
>descriptor level are zero, and for "a+" mode, an initial read would
>start at the beginning of the file.  Only the fuzzy specification of
>permits the initial offsets at the stream level to be nonzero.

But let's not look at the initial offset now but the offsets after writing 4
bytes. FreeBSD reports here three times '4' which looks completely wrong in
my opinion: when you have "testtest" in a file and you append "test" ftell
should say 12 isn't it?

Arjan

-- 
Arjan de Vet, Eindhoven, The Netherlands              <Arjan.deVet@adv.iae.nl>
URL: http://www.iae.nl/users/devet/           for PGP key: finger devet@iae.nl
Comment 3 Poul-Henning Kamp freebsd_committer freebsd_triage 2001-06-05 18:57:12 UTC
State Changed
From-To: open->closed

You can never trust *seek() to tell you where the next write 
will happen on a file opened in append mode, because another 
process might sneak in between your *seek() and write() calls. 

One could possibly use *seek() to discover the offset after the 
write completes and set the offset, it would then be possible to 
retroactively compute the location of the data in the file by 
calling write() first and then *seek(), subtracting the number of 
bytes written.