about summary refs log tree commit diff
path: root/libio/fileops.c
diff options
context:
space:
mode:
authorSiddhesh Poyarekar <siddhesh@redhat.com>2014-05-27 13:54:19 +0530
committerSiddhesh Poyarekar <siddhesh@redhat.com>2014-05-27 13:54:19 +0530
commit2482ae433a4249495859343ae1fba408300f2c2e (patch)
tree9fdaad4e75acb1672022be1c49862ee726134bd9 /libio/fileops.c
parentbab900166e8b5f0f4081c5cf1afa0cd33b123714 (diff)
downloadglibc-2482ae433a4249495859343ae1fba408300f2c2e.tar.gz
glibc-2482ae433a4249495859343ae1fba408300f2c2e.tar.xz
glibc-2482ae433a4249495859343ae1fba408300f2c2e.zip
Fix offset computation for append+ mode on switching from read (BZ #16724)
The offset computation in write mode uses the fact that _IO_read_end
is kept in sync with the external file offset.  This however is not
true when O_APPEND is in effect since switching to write mode ought to
send the external file offset to the end of file without making the
necessary adjustment to _IO_read_end.

Hence in append mode, offset computation when writing should only
consider the effect of unflushed writes, i.e. from _IO_write_base to
_IO_write_ptr.

The wiki has a detailed document that describes the rationale for
offsets returned by ftell in various conditions:

https://sourceware.org/glibc/wiki/File%20offsets%20in%20a%20stdio%20stream%20and%20ftell
Diffstat (limited to 'libio/fileops.c')
-rw-r--r--libio/fileops.c12
1 files changed, 11 insertions, 1 deletions
diff --git a/libio/fileops.c b/libio/fileops.c
index cf68dbfe52..204cfeaa35 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -91,7 +91,9 @@ extern struct __gconv_trans_data __libio_translit attribute_hidden;
 
    The position in the buffer that corresponds to the position
    in external file system is normally _IO_read_end, except in putback
-   mode, when it is _IO_save_end.
+   mode, when it is _IO_save_end and also when the file is in append mode,
+   since switching from read to write mode automatically sends the position in
+   the external file system to the end of file.
    If the field _fb._offset is >= 0, it gives the offset in
    the file as a whole corresponding to eGptr(). (?)
 
@@ -966,6 +968,14 @@ do_ftell (_IO_FILE *fp)
       /* Adjust for unflushed data.  */
       if (!was_writing)
 	offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+      /* We don't trust _IO_read_end to represent the current file offset when
+	 writing in append mode because the value would have to be shifted to
+	 the end of the file during a flush.  Use the write base instead, along
+	 with the new offset we got above when we did a seek to the end of the
+	 file.  */
+      else if (append_mode)
+	offset += fp->_IO_write_ptr - fp->_IO_write_base;
+      /* For all other modes, _IO_read_end represents the file offset.  */
       else
 	offset += fp->_IO_write_ptr - fp->_IO_read_end;
     }