diff options
Diffstat (limited to 'libio/strops.c')
-rw-r--r-- | libio/strops.c | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/libio/strops.c b/libio/strops.c index 2ba3704dd2..1bb8a77932 100644 --- a/libio/strops.c +++ b/libio/strops.c @@ -230,6 +230,21 @@ enlarge_userbuf (_IO_FILE *fp, _IO_off64_t offset, int reading) return 0; } +static void +_IO_str_switch_to_get_mode (_IO_FILE *fp) +{ + if (_IO_in_backup (fp)) + fp->_IO_read_base = fp->_IO_backup_base; + else + { + fp->_IO_read_base = fp->_IO_buf_base; + if (fp->_IO_write_ptr > fp->_IO_read_end) + fp->_IO_read_end = fp->_IO_write_ptr; + } + fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_write_ptr; + + fp->_flags &= ~_IO_CURRENTLY_PUTTING; +} _IO_off64_t _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) @@ -239,14 +254,14 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET)) mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT); + bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base + || _IO_in_put_mode (fp)); + if (was_writing) + _IO_str_switch_to_get_mode (fp); + if (mode == 0) { - /* Don't move any pointers. But there is no clear indication what - mode FP is in. Let's guess. */ - if (fp->_IO_file_flags & _IO_NO_WRITES) - new_pos = fp->_IO_read_ptr - fp->_IO_read_base; - else - new_pos = fp->_IO_write_ptr - fp->_IO_write_base; + new_pos = fp->_IO_read_ptr - fp->_IO_read_base; } else { @@ -256,48 +271,62 @@ _IO_str_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) /* Move the get pointer, if requested. */ if (mode & _IOS_INPUT) { + _IO_ssize_t base; switch (dir) { - case _IO_seek_end: - offset += cur_size; + case _IO_seek_set: + base = 0; break; case _IO_seek_cur: - offset += fp->_IO_read_ptr - fp->_IO_read_base; + base = fp->_IO_read_ptr - fp->_IO_read_base; break; - default: /* case _IO_seek_set: */ + default: /* case _IO_seek_end: */ + base = cur_size; break; } - if (offset < 0) - return EOF; - if ((_IO_ssize_t) offset > cur_size - && enlarge_userbuf (fp, offset, 1) != 0) + _IO_ssize_t maxval = SSIZE_MAX - base; + if (offset < -base || offset > maxval) + { + __set_errno (EINVAL); + return EOF; + } + base += offset; + if (base > cur_size + && enlarge_userbuf (fp, base, 1) != 0) return EOF; - fp->_IO_read_ptr = fp->_IO_read_base + offset; + fp->_IO_read_ptr = fp->_IO_read_base + base; fp->_IO_read_end = fp->_IO_read_base + cur_size; - new_pos = offset; + new_pos = base; } /* Move the put pointer, if requested. */ if (mode & _IOS_OUTPUT) { + _IO_ssize_t base; switch (dir) { - case _IO_seek_end: - offset += cur_size; + case _IO_seek_set: + base = 0; break; case _IO_seek_cur: - offset += fp->_IO_write_ptr - fp->_IO_write_base; + base = fp->_IO_write_ptr - fp->_IO_write_base; break; - default: /* case _IO_seek_set: */ + default: /* case _IO_seek_end: */ + base = cur_size; break; } - if (offset < 0) - return EOF; - if ((_IO_ssize_t) offset > cur_size - && enlarge_userbuf (fp, offset, 0) != 0) + _IO_ssize_t maxval = SSIZE_MAX - base; + if (offset < -base || offset > maxval) + { + __set_errno (EINVAL); + return EOF; + } + base += offset; + if (base > cur_size + && enlarge_userbuf (fp, base, 0) != 0) return EOF; - fp->_IO_write_ptr = fp->_IO_write_base + offset; - new_pos = offset; + fp->_IO_write_ptr = fp->_IO_write_base + base; + new_pos = base; } } return new_pos; |