aboutsummaryrefslogtreecommitdiff
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.zip
glibc-2482ae433a4249495859343ae1fba408300f2c2e.tar.gz
glibc-2482ae433a4249495859343ae1fba408300f2c2e.tar.bz2
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 cf68dbf..204cfea 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;
}