diff options
Diffstat (limited to 'libio/fileops.c')
-rw-r--r-- | libio/fileops.c | 34 |
1 files changed, 28 insertions, 6 deletions
diff --git a/libio/fileops.c b/libio/fileops.c index 500629564a..2e7bc8dad9 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -964,12 +964,8 @@ get_file_offset (_IO_FILE *fp) static _IO_off64_t do_ftell (_IO_FILE *fp) { - _IO_off64_t result; - - result = get_file_offset (fp); - - if (result == EOF) - return result; + _IO_off64_t result = 0; + bool use_cached_offset = false; /* No point looking at unflushed data if we haven't allocated buffers yet. */ @@ -983,8 +979,34 @@ do_ftell (_IO_FILE *fp) result -= fp->_IO_read_end - fp->_IO_read_ptr; else result += fp->_IO_write_ptr - fp->_IO_read_end; + + /* It is safe to use the cached offset when available if there is + unbuffered data (indicating that the file handle is active) and the + handle is not for a file open in a+ mode. The latter condition is + because there could be a scenario where there is a switch from read + mode to write mode using an fseek to an arbitrary position. In this + case, there would be unbuffered data due to be appended to the end of + the file, but the offset may not necessarily be the end of the + file. It is fine to use the cached offset when the a+ stream is in + read mode though, since the offset is maintained correctly in that + case. Note that this is not a comprehensive set of cases when the + offset is reliable. The offset may be reliable even in some cases + where there is no unflushed input and the handle is active, but it's + just that we don't have a way to identify that condition reliably. */ + use_cached_offset = (result != 0 && fp->_offset != _IO_pos_BAD + && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS)) + == (_IO_IS_APPENDING | _IO_NO_READS) + && was_writing)); } + if (use_cached_offset) + result += fp->_offset; + else + result += get_file_offset (fp); + + if (result == EOF) + return result; + if (result < 0) { __set_errno (EINVAL); |