From 52f69e7bd59408115a3d3909f8a12561e92bb7a2 Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar Date: Tue, 25 Feb 2014 15:22:53 +0530 Subject: [PATCH] Sync with upstream master and Resolve: #1069559 --- glibc-rh1069559-1.patch | 836 ++++++++++++++++++++++++++++++++++++++++ glibc-rh1069559-2.patch | 183 +++++++++ glibc.spec | 14 +- sources | 2 +- 4 files changed, 1032 insertions(+), 3 deletions(-) create mode 100644 glibc-rh1069559-1.patch create mode 100644 glibc-rh1069559-2.patch diff --git a/glibc-rh1069559-1.patch b/glibc-rh1069559-1.patch new file mode 100644 index 0000000..9c098c2 --- /dev/null +++ b/glibc-rh1069559-1.patch @@ -0,0 +1,836 @@ +commit 03425ba6ab17b87b2c64f0d7b47fb0b86add9993 +Author: Siddhesh Poyarekar +Date: Thu Feb 20 15:41:18 2014 +0530 + + Separate ftell from fseek logic and avoid modifying FILE data (#16532) + + ftell semantics are distinct from fseek(SEEK_CUR) especially when it + is called on a file handler that is not yet active. Due to this + caveat, much care needs to be taken while modifying the handler data + and hence, this first iteration on separating out ftell focusses on + maintaining handler data integrity at all times while it figures out + the current stream offset. The result is that it makes a syscall for + every offset request. + + There is scope for optimizing this by caching offsets when we know + that the handler is active. A simple way to find out is when the + buffers have data. It is not so simple to find this out when the + buffer is empty without adding some kind of flag. + +diff --git a/libio/Makefile b/libio/Makefile +index 8c333ce..8ccd80f 100644 +--- a/libio/Makefile ++++ b/libio/Makefile +@@ -60,7 +60,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ + tst-wmemstream1 tst-wmemstream2 \ + bug-memstream1 bug-wmemstream1 \ + tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \ +- tst-fwrite-error tst-ftell-partial-wide ++ tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler + ifeq (yes,$(build-shared)) + # Add test-fopenloc only if shared library is enabled since it depends on + # shared localedata objects. +diff --git a/libio/fileops.c b/libio/fileops.c +index a3499be..a177302 100644 +--- a/libio/fileops.c ++++ b/libio/fileops.c +@@ -931,6 +931,59 @@ _IO_file_sync_mmap (_IO_FILE *fp) + + + _IO_off64_t ++get_file_offset (_IO_FILE *fp) ++{ ++ if ((fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING) ++ { ++ struct stat64 st; ++ bool ret = (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode)); ++ if (ret) ++ return st.st_size; ++ else ++ return EOF; ++ } ++ else ++ return _IO_SYSSEEK (fp, 0, _IO_seek_cur); ++} ++ ++ ++/* ftell{,o} implementation. Don't modify any state of the file pointer while ++ we try to get the current state of the stream. */ ++static _IO_off64_t ++do_ftell (_IO_FILE *fp) ++{ ++ _IO_off64_t result; ++ ++ result = get_file_offset (fp); ++ ++ if (result == EOF) ++ return result; ++ ++ /* No point looking at unflushed data if we haven't allocated buffers ++ yet. */ ++ if (fp->_IO_buf_base != NULL) ++ { ++ bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base ++ || _IO_in_put_mode (fp)); ++ ++ /* Adjust for unflushed data. */ ++ if (!was_writing) ++ result -= fp->_IO_read_end - fp->_IO_read_ptr; ++ else ++ result += fp->_IO_write_ptr - fp->_IO_read_end; ++ } ++ ++ if (result < 0) ++ { ++ __set_errno (EINVAL); ++ return EOF; ++ } ++ ++ return result; ++} ++ ++ ++_IO_off64_t + _IO_new_file_seekoff (fp, offset, dir, mode) + _IO_FILE *fp; + _IO_off64_t offset; +@@ -940,6 +993,13 @@ _IO_new_file_seekoff (fp, offset, dir, mode) + _IO_off64_t result; + _IO_off64_t delta, new_offset; + long count; ++ ++ /* Short-circuit into a separate function. We don't want to mix any ++ functionality and we don't want to touch anything inside the FILE ++ object. */ ++ if (mode == 0) ++ return do_ftell (fp); ++ + /* POSIX.1 8.2.3.7 says that after a call the fflush() the file + offset of the underlying file must be exact. */ + int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end +@@ -948,9 +1008,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode) + bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base + || _IO_in_put_mode (fp)); + +- if (mode == 0) +- dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */ +- + /* Flush unwritten characters. + (This may do an unneeded write if we seek within the buffer. + But to be able to switch to reading, we would need to set +@@ -958,7 +1015,7 @@ _IO_new_file_seekoff (fp, offset, dir, mode) + which assumes file_ptr() is eGptr. Anyway, since we probably + end up flushing when we close(), it doesn't make much difference.) + FIXME: simulate mem-mapped files. */ +- else if (was_writing && _IO_switch_to_get_mode (fp)) ++ if (was_writing && _IO_switch_to_get_mode (fp)) + return EOF; + + if (fp->_IO_buf_base == NULL) +@@ -978,30 +1035,10 @@ _IO_new_file_seekoff (fp, offset, dir, mode) + { + case _IO_seek_cur: + /* Adjust for read-ahead (bytes is buffer). */ +- if (mode != 0 || !was_writing) +- offset -= fp->_IO_read_end - fp->_IO_read_ptr; +- else +- { +- /* _IO_read_end coincides with fp._offset, so the actual file position +- is fp._offset - (_IO_read_end - new_write_ptr). This is fine +- even if fp._offset is not set, since fp->_IO_read_end is then at +- _IO_buf_base and this adjustment is for unbuffered output. */ +- offset -= fp->_IO_read_end - fp->_IO_write_ptr; +- } ++ offset -= fp->_IO_read_end - fp->_IO_read_ptr; + + if (fp->_offset == _IO_pos_BAD) +- { +- if (mode != 0) +- goto dumb; +- else +- { +- result = _IO_SYSSEEK (fp, 0, dir); +- if (result == EOF) +- return result; +- +- fp->_offset = result; +- } +- } ++ goto dumb; + /* Make offset absolute, assuming current pointer is file_ptr(). */ + offset += fp->_offset; + if (offset < 0) +@@ -1028,10 +1065,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode) + } + /* At this point, dir==_IO_seek_set. */ + +- /* If we are only interested in the current position we've found it now. */ +- if (mode == 0) +- return offset; +- + /* If destination is within current buffer, optimize: */ + if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL + && !_IO_in_backup (fp)) +diff --git a/libio/libioP.h b/libio/libioP.h +index 4ca723c..8a7b85b 100644 +--- a/libio/libioP.h ++++ b/libio/libioP.h +@@ -397,6 +397,7 @@ extern void _IO_wdoallocbuf (_IO_FILE *) __THROW; + libc_hidden_proto (_IO_wdoallocbuf) + extern void _IO_unsave_wmarkers (_IO_FILE *) __THROW; + extern unsigned _IO_adjust_wcolumn (unsigned, const wchar_t *, int) __THROW; ++extern _IO_off64_t get_file_offset (_IO_FILE *fp); + + /* Marker-related function. */ + +diff --git a/libio/tst-ftell-active-handler.c b/libio/tst-ftell-active-handler.c +new file mode 100644 +index 0000000..aac2923 +--- /dev/null ++++ b/libio/tst-ftell-active-handler.c +@@ -0,0 +1,366 @@ ++/* Verify that ftell returns the correct value at various points before and ++ after the handler on which it is called becomes active. ++ Copyright (C) 2014 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int do_test (void); ++ ++#define TEST_FUNCTION do_test () ++#include "../test-skeleton.c" ++ ++#define get_handles_fdopen(filename, fd, fp, fd_mode, mode) \ ++({ \ ++ int ret = 0; \ ++ (fd) = open ((filename), (fd_mode), 0); \ ++ if ((fd) == -1) \ ++ { \ ++ printf ("open failed: %m\n"); \ ++ ret = 1; \ ++ } \ ++ else \ ++ { \ ++ (fp) = fdopen ((fd), (mode)); \ ++ if ((fp) == NULL) \ ++ { \ ++ printf ("fdopen failed: %m\n"); \ ++ close (fd); \ ++ ret = 1; \ ++ } \ ++ } \ ++ ret; \ ++}) ++ ++#define get_handles_fopen(filename, fd, fp, mode) \ ++({ \ ++ int ret = 0; \ ++ (fp) = fopen ((filename), (mode)); \ ++ if ((fp) == NULL) \ ++ { \ ++ printf ("fopen failed: %m\n"); \ ++ ret = 1; \ ++ } \ ++ else \ ++ { \ ++ (fd) = fileno (fp); \ ++ if ((fd) == -1) \ ++ { \ ++ printf ("fileno failed: %m\n"); \ ++ ret = 1; \ ++ } \ ++ } \ ++ ret; \ ++}) ++ ++static const void *data; ++static const char *char_data = "abcdef"; ++static const wchar_t *wide_data = L"abcdef"; ++static size_t data_len; ++/* Maintain the current file length for validation. */ ++static size_t file_len; ++ ++typedef int (*fputs_func_t) (const void *data, FILE *fp); ++fputs_func_t fputs_func; ++ ++/* Test that the value of ftell is not cached when the stream handle is not ++ active. */ ++static int ++do_ftell_test (const char *filename) ++{ ++ int ret = 0; ++ struct test ++ { ++ const char *mode; ++ int fd_mode; ++ size_t old_off; ++ size_t new_off; ++ } test_modes[] = { ++ {"w", O_WRONLY, 0, data_len}, ++ {"w+", O_RDWR, 0, data_len}, ++ {"r+", O_RDWR, 0, data_len}, ++ {"a", O_WRONLY, data_len, 2 * data_len}, ++ {"a+", O_RDWR, 2 * data_len, 3 * data_len}, ++ }; ++ for (int j = 0; j < 2; j++) ++ { ++ for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++) ++ { ++ FILE *fp; ++ int fd; ++ printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen", ++ test_modes[i].mode); ++ ++ if (j == 0) ++ ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode, ++ test_modes[i].mode); ++ else ++ ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode); ++ ++ if (ret != 0) ++ return 1; ++ ++ long off = ftell (fp); ++ if (off != test_modes[i].old_off) ++ { ++ printf ("Incorrect old offset. Expected %zu but got %ld, ", ++ test_modes[i].old_off, off); ++ ret |= 1; ++ } ++ else ++ printf ("old offset = %ld, ", off); ++ ++ int ret = write (fd, data, data_len); ++ off = ftell (fp); ++ ++ if (off != test_modes[i].new_off) ++ { ++ printf ("Incorrect new offset. Expected %zu but got %ld\n", ++ test_modes[i].old_off, off); ++ ret |= 1; ++ } ++ else ++ printf ("new offset = %ld\n", off); ++ ++ fclose (fp); ++ } ++ } ++ ++ return ret; ++} ++ ++/* This test opens the file for writing, moves the file offset of the ++ underlying file, writes out data and then checks if ftell trips on it. */ ++static int ++do_write_test (const char *filename) ++{ ++ FILE *fp = NULL; ++ int fd; ++ int ret = 0; ++ struct test ++ { ++ const char *mode; ++ int fd_mode; ++ } test_modes[] = { ++ {"w", O_WRONLY}, ++ {"w+", O_RDWR}, ++ {"r+", O_RDWR} ++ }; ++ ++ for (int j = 0; j < 2; j++) ++ { ++ for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++) ++ { ++ printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen", ++ test_modes[i].mode); ++ ++ if (j == 0) ++ ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode); ++ else ++ ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode, ++ test_modes[i].mode); ++ ++ if (ret != 0) ++ return ret; ++ ++ /* Move offset to just before the end of the file. */ ++ off_t ret = lseek (fd, file_len - 1, SEEK_SET); ++ if (ret == -1) ++ { ++ printf ("lseek failed: %m\n"); ++ ret |= 1; ++ } ++ ++ /* Write some data. */ ++ size_t written = fputs_func (data, fp); ++ ++ if (written == EOF) ++ { ++ printf ("fputs[1] failed to write data\n"); ++ ret |= 1; ++ } ++ ++ /* Verify that the offset points to the end of the file. */ ++ long offset = ftell (fp); ++ file_len = file_len - 1 + data_len; ++ ++ if (offset != file_len) ++ { ++ printf ("Incorrect offset. Expected %zu, but got %ld\n", ++ file_len, offset); ++ ++ ret |= 1; ++ } ++ ++ printf ("offset = %ld\n", offset); ++ fclose (fp); ++ } ++ } ++ ++ return ret; ++} ++ ++/* This test opens a file in append mode, writes some data, and then verifies ++ that ftell does not trip over it. */ ++static int ++do_append_test (const char *filename) ++{ ++ FILE *fp = NULL; ++ int ret = 0; ++ int fd; ++ ++ struct test ++ { ++ const char *mode; ++ int fd_mode; ++ } test_modes[] = { ++ {"a", O_WRONLY}, ++ {"a+", O_RDWR} ++ }; ++ ++ for (int j = 0; j < 2; j++) ++ { ++ for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++) ++ { ++ printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen", ++ test_modes[i].mode); ++ ++ if (j == 0) ++ ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode); ++ else ++ ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode, ++ test_modes[i].mode); ++ ++ if (ret != 0) ++ return ret; ++ ++ /* Write some data. */ ++ size_t written = fputs_func (data, fp); ++ ++ if (written == EOF) ++ { ++ printf ("fputs[1] failed to write all data\n"); ++ ret |= 1; ++ } ++ ++ /* Verify that the offset points to the end of the file. */ ++ long offset = ftell (fp); ++ file_len += data_len; ++ ++ if (offset != file_len) ++ { ++ printf ("Incorrect offset. Expected %zu, but got %ld\n", ++ file_len, offset); ++ ++ ret |= 1; ++ } ++ ++ printf ("offset = %ld\n", offset); ++ fclose (fp); ++ } ++ } ++ ++ return ret; ++} ++ ++static int ++do_one_test (const char *filename) ++{ ++ int ret = 0; ++ ++ ret |= do_ftell_test (filename); ++ ret |= do_write_test (filename); ++ ret |= do_append_test (filename); ++ ++ return ret; ++} ++ ++static int ++do_test (void) ++{ ++ int ret = 0; ++ FILE *fp = NULL; ++ char *filename; ++ size_t written; ++ int fd = create_temp_file ("tst-active-handler-tmp.", &filename); ++ ++ if (fd == -1) ++ { ++ printf ("create_temp_file: %m\n"); ++ return 1; ++ } ++ ++ fp = fdopen (fd, "w"); ++ if (fp == NULL) ++ { ++ printf ("fdopen[0]: %m\n"); ++ close (fd); ++ return 1; ++ } ++ ++ data = char_data; ++ data_len = strlen (char_data); ++ file_len = strlen (char_data); ++ written = fputs (data, fp); ++ ++ if (written == EOF) ++ { ++ printf ("fputs[1] failed to write data\n"); ++ ret = 1; ++ } ++ ++ fclose (fp); ++ if (ret) ++ return ret; ++ ++ /* Tests for regular files. */ ++ puts ("Regular mode:"); ++ fputs_func = (fputs_func_t) fputs; ++ data = char_data; ++ data_len = strlen (char_data); ++ ret |= do_one_test (filename); ++ ++ /* Truncate the file before repeating the tests in wide mode. */ ++ fp = fopen (filename, "w"); ++ if (fp == NULL) ++ { ++ printf ("fopen failed %m\n"); ++ return 1; ++ } ++ fclose (fp); ++ ++ /* Tests for wide files. */ ++ puts ("Wide mode:"); ++ if (setlocale (LC_ALL, "en_US.UTF-8") == NULL) ++ { ++ printf ("Cannot set en_US.UTF-8 locale.\n"); ++ return 1; ++ } ++ fputs_func = (fputs_func_t) fputws; ++ data = wide_data; ++ data_len = wcslen (wide_data); ++ ret |= do_one_test (filename); ++ ++ return ret; ++} +diff --git a/libio/wfileops.c b/libio/wfileops.c +index 9cebe77..eda7828 100644 +--- a/libio/wfileops.c ++++ b/libio/wfileops.c +@@ -596,29 +596,22 @@ done: + return 0; + } + +-_IO_off64_t +-_IO_wfile_seekoff (fp, offset, dir, mode) +- _IO_FILE *fp; +- _IO_off64_t offset; +- int dir; +- int mode; ++static _IO_off64_t ++do_ftell_wide (_IO_FILE *fp) + { +- _IO_off64_t result; +- _IO_off64_t delta, new_offset; +- long int count; +- /* POSIX.1 8.2.3.7 says that after a call the fflush() the file +- offset of the underlying file must be exact. */ +- int must_be_exact = ((fp->_wide_data->_IO_read_base +- == fp->_wide_data->_IO_read_end) +- && (fp->_wide_data->_IO_write_base +- == fp->_wide_data->_IO_write_ptr)); ++ _IO_off64_t result, offset = 0; + +- bool was_writing = ((fp->_wide_data->_IO_write_ptr +- > fp->_wide_data->_IO_write_base) +- || _IO_in_put_mode (fp)); +- +- if (mode == 0) ++ /* No point looking for offsets in the buffer if it hasn't even been ++ allocated. */ ++ if (fp->_wide_data->_IO_buf_base != NULL) + { ++ const wchar_t *wide_read_base; ++ const wchar_t *wide_read_ptr; ++ const wchar_t *wide_read_end; ++ bool was_writing = ((fp->_wide_data->_IO_write_ptr ++ > fp->_wide_data->_IO_write_base) ++ || _IO_in_put_mode (fp)); ++ + /* XXX For wide stream with backup store it is not very + reasonable to determine the offset. The pushed-back + character might require a state change and we need not be +@@ -633,14 +626,117 @@ _IO_wfile_seekoff (fp, offset, dir, mode) + return -1; + } + +- /* There is no more data in the backup buffer. We can +- switch back. */ +- _IO_switch_to_main_wget_area (fp); ++ /* Nothing in the backup store, so note the backed up pointers ++ without changing the state. */ ++ wide_read_base = fp->_wide_data->_IO_save_base; ++ wide_read_ptr = wide_read_base; ++ wide_read_end = fp->_wide_data->_IO_save_end; ++ } ++ else ++ { ++ wide_read_base = fp->_wide_data->_IO_read_base; ++ wide_read_ptr = fp->_wide_data->_IO_read_ptr; ++ wide_read_end = fp->_wide_data->_IO_read_end; + } + +- dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */ ++ struct _IO_codecvt *cv = fp->_codecvt; ++ int clen = (*cv->__codecvt_do_encoding) (cv); ++ ++ if (!was_writing) ++ { ++ if (clen > 0) ++ { ++ offset -= (wide_read_end - wide_read_ptr) * clen; ++ offset -= fp->_IO_read_end - fp->_IO_read_ptr; ++ } ++ else ++ { ++ int nread; ++ ++ size_t delta = wide_read_ptr - wide_read_base; ++ __mbstate_t state = fp->_wide_data->_IO_last_state; ++ nread = (*cv->__codecvt_do_length) (cv, &state, ++ fp->_IO_read_base, ++ fp->_IO_read_end, delta); ++ offset -= fp->_IO_read_end - fp->_IO_read_base - nread; ++ } ++ } ++ else ++ { ++ if (clen > 0) ++ offset += (fp->_wide_data->_IO_write_ptr ++ - fp->_wide_data->_IO_write_base) * clen; ++ else ++ { ++ size_t delta = (fp->_wide_data->_IO_write_ptr ++ - fp->_wide_data->_IO_write_base); ++ ++ /* Allocate enough space for the conversion. */ ++ size_t outsize = delta * sizeof (wchar_t); ++ char *out = malloc (outsize); ++ char *outstop = out; ++ const wchar_t *in = fp->_wide_data->_IO_write_base; ++ ++ enum __codecvt_result status; ++ ++ __mbstate_t state = fp->_wide_data->_IO_last_state; ++ status = (*cv->__codecvt_do_out) (cv, &state, ++ in, in + delta, &in, ++ out, out + outsize, &outstop); ++ ++ /* We don't check for __codecvt_partial because it can be ++ returned on one of two conditions: either the output ++ buffer is full or the input sequence is incomplete. We ++ take care to allocate enough buffer and our input ++ sequences must be complete since they are accepted as ++ wchar_t; if not, then that is an error. */ ++ if (__glibc_unlikely (status != __codecvt_ok)) ++ return WEOF; ++ ++ offset += outstop - out; ++ } ++ ++ /* _IO_read_end coincides with fp._offset, so the actual file position ++ is fp._offset - (_IO_read_end - new_write_ptr). */ ++ offset -= fp->_IO_read_end - fp->_IO_write_ptr; ++ } + } + ++ result = get_file_offset (fp); ++ ++ if (result == EOF) ++ return result; ++ ++ result += offset; ++ ++ return result; ++} ++ ++_IO_off64_t ++_IO_wfile_seekoff (fp, offset, dir, mode) ++ _IO_FILE *fp; ++ _IO_off64_t offset; ++ int dir; ++ int mode; ++{ ++ _IO_off64_t result; ++ _IO_off64_t delta, new_offset; ++ long int count; ++ ++ if (mode == 0) ++ return do_ftell_wide (fp); ++ ++ /* POSIX.1 8.2.3.7 says that after a call the fflush() the file ++ offset of the underlying file must be exact. */ ++ int must_be_exact = ((fp->_wide_data->_IO_read_base ++ == fp->_wide_data->_IO_read_end) ++ && (fp->_wide_data->_IO_write_base ++ == fp->_wide_data->_IO_write_ptr)); ++ ++ bool was_writing = ((fp->_wide_data->_IO_write_ptr ++ > fp->_wide_data->_IO_write_base) ++ || _IO_in_put_mode (fp)); ++ + /* Flush unwritten characters. + (This may do an unneeded write if we seek within the buffer. + But to be able to switch to reading, we would need to set +@@ -648,7 +744,7 @@ _IO_wfile_seekoff (fp, offset, dir, mode) + which assumes file_ptr() is eGptr. Anyway, since we probably + end up flushing when we close(), it doesn't make much difference.) + FIXME: simulate mem-mapped files. */ +- else if (was_writing && _IO_switch_to_wget_mode (fp)) ++ if (was_writing && _IO_switch_to_wget_mode (fp)) + return WEOF; + + if (fp->_wide_data->_IO_buf_base == NULL) +@@ -693,7 +789,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode) + { + int nread; + +- flushed: + delta = (fp->_wide_data->_IO_read_ptr + - fp->_wide_data->_IO_read_base); + fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state; +@@ -706,80 +801,9 @@ _IO_wfile_seekoff (fp, offset, dir, mode) + offset -= fp->_IO_read_end - fp->_IO_read_base - nread; + } + } +- else +- { +- char *new_write_ptr = fp->_IO_write_ptr; +- +- if (clen > 0) +- offset += (fp->_wide_data->_IO_write_ptr +- - fp->_wide_data->_IO_write_base) / clen; +- else +- { +- enum __codecvt_result status = __codecvt_ok; +- delta = (fp->_wide_data->_IO_write_ptr +- - fp->_wide_data->_IO_write_base); +- const wchar_t *write_base = fp->_wide_data->_IO_write_base; +- +- /* FIXME: This actually ends up in two iterations of conversion, +- one here and the next when the buffer actually gets flushed. +- It may be possible to optimize this in future so that +- wdo_write identifies already converted content and does not +- redo it. In any case, this is much better than having to +- flush buffers for every ftell. */ +- do +- { +- /* There is not enough space in the buffer to do the entire +- conversion, so there is no point trying to avoid the +- buffer flush. Just do it and go back to how it was with +- the read mode. */ +- if (status == __codecvt_partial +- || (delta > 0 && new_write_ptr == fp->_IO_buf_end)) +- { +- if (_IO_switch_to_wget_mode (fp)) +- return WEOF; +- goto flushed; +- } +- +- const wchar_t *new_wbase = fp->_wide_data->_IO_write_base; +- fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state; +- status = (*cv->__codecvt_do_out) (cv, +- &fp->_wide_data->_IO_state, +- write_base, +- write_base + delta, +- &new_wbase, +- new_write_ptr, +- fp->_IO_buf_end, +- &new_write_ptr); +- +- delta -= new_wbase - write_base; +- +- /* If there was an error, then return WEOF. +- TODO: set buffer state. */ +- if (__glibc_unlikely (status == __codecvt_error)) +- return WEOF; +- } +- while (delta > 0); +- } +- +- /* _IO_read_end coincides with fp._offset, so the actual file position +- is fp._offset - (_IO_read_end - new_write_ptr). This is fine +- even if fp._offset is not set, since fp->_IO_read_end is then at +- _IO_buf_base and this adjustment is for unbuffered output. */ +- offset -= fp->_IO_read_end - new_write_ptr; +- } + + if (fp->_offset == _IO_pos_BAD) +- { +- if (mode != 0) +- goto dumb; +- else +- { +- result = _IO_SYSSEEK (fp, 0, dir); +- if (result == EOF) +- return result; +- fp->_offset = result; +- } +- } ++ goto dumb; + + /* Make offset absolute, assuming current pointer is file_ptr(). */ + offset += fp->_offset; +@@ -802,10 +826,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode) + } + /* At this point, dir==_IO_seek_set. */ + +- /* If we are only interested in the current position we've found it now. */ +- if (mode == 0) +- return offset; +- + /* If destination is within current buffer, optimize: */ + if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL + && !_IO_in_backup (fp)) diff --git a/glibc-rh1069559-2.patch b/glibc-rh1069559-2.patch new file mode 100644 index 0000000..b74e2f1 --- /dev/null +++ b/glibc-rh1069559-2.patch @@ -0,0 +1,183 @@ +commit 4a68e8ded4fef2b00fbfc6baf1e79e46389871ca +Author: Siddhesh Poyarekar +Date: Thu Feb 20 15:55:16 2014 +0530 + + Use cached offset in ftell when reliable + + The cached offset is reliable to use in ftell when the stream handle + is active. We can consider a stream as being active when there is + unbuffered data. However, even in this case, we can use the cached + offset only when the stream is not being written to in a+ mode, + because this case may have unbuffered data and a stale offset; the + previous read could have sent it off somewhere other than the end of + the file. + + There were a couple of adjustments necessary to get this to work. + Firstly, fdopen now ceases to use _IO_attach_fd because it sets the + offset cache to the current file position. This is not correct + because there could be changes to the file descriptor before the + stream handle is activated, which would not get reflected. + + A similar offset caching action is done in _IO_fwide, claiming that + wide streams have 'problems' with the file offsets. There don't seem + to be any obvious problems with not having the offset cache available, + other than that it will have to be queried in a subsequent + read/write/seek. I have removed this as well. + + The testsuite passes successfully with these changes on x86_64. + +diff --git a/libio/fileops.c b/libio/fileops.c +index a177302..c44a5da 100644 +--- a/libio/fileops.c ++++ b/libio/fileops.c +@@ -952,12 +952,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. */ +@@ -971,8 +967,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 buffered 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); +diff --git a/libio/iofdopen.c b/libio/iofdopen.c +index 066ff19..4525f0f 100644 +--- a/libio/iofdopen.c ++++ b/libio/iofdopen.c +@@ -141,9 +141,6 @@ _IO_new_fdopen (fd, mode) + #ifdef _IO_MTSAFE_IO + new_f->fp.file._lock = &new_f->lock; + #endif +- /* Set up initially to use the `maybe_mmap' jump tables rather than using +- __fopen_maybe_mmap to do it, because we need them in place before we +- call _IO_file_attach or else it will allocate a buffer immediately. */ + _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, + #ifdef _G_HAVE_MMAP + (use_mmap && (read_write & _IO_NO_WRITES)) +@@ -159,13 +156,10 @@ _IO_new_fdopen (fd, mode) + #if !_IO_UNIFIED_JUMPTABLES + new_f->fp.vtable = NULL; + #endif +- if (_IO_file_attach ((_IO_FILE *) &new_f->fp, fd) == NULL) +- { +- _IO_setb (&new_f->fp.file, NULL, NULL, 0); +- _IO_un_link (&new_f->fp); +- free (new_f); +- return NULL; +- } ++ /* We only record the fd because _IO_file_init will have unset the offset. ++ We don't need to get the current offset in the file now since it could ++ change between now and when the handle is activated. */ ++ new_f->fp.file._fileno = fd; + new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE; + + _IO_mask_flags (&new_f->fp.file, read_write, +diff --git a/libio/iofwide.c b/libio/iofwide.c +index 5cff632..64187e4 100644 +--- a/libio/iofwide.c ++++ b/libio/iofwide.c +@@ -199,12 +199,6 @@ _IO_fwide (fp, mode) + + /* From now on use the wide character callback functions. */ + ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable; +- +- /* One last twist: we get the current stream position. The wide +- char streams have much more problems with not knowing the +- current position and so we should disable the optimization +- which allows the functions without knowing the position. */ +- fp->_offset = _IO_SYSSEEK (fp, 0, _IO_seek_cur); + } + + /* Set the mode now. */ +diff --git a/libio/wfileops.c b/libio/wfileops.c +index eda7828..651f5ce 100644 +--- a/libio/wfileops.c ++++ b/libio/wfileops.c +@@ -600,6 +600,7 @@ static _IO_off64_t + do_ftell_wide (_IO_FILE *fp) + { + _IO_off64_t result, offset = 0; ++ bool use_cached_offset = false; + + /* No point looking for offsets in the buffer if it hasn't even been + allocated. */ +@@ -696,13 +697,36 @@ do_ftell_wide (_IO_FILE *fp) + offset += outstop - out; + } + +- /* _IO_read_end coincides with fp._offset, so the actual file position +- is fp._offset - (_IO_read_end - new_write_ptr). */ ++ /* _IO_read_end coincides with fp._offset, so the actual file ++ position is fp._offset - (_IO_read_end - new_write_ptr). */ + offset -= fp->_IO_read_end - fp->_IO_write_ptr; ++ + } ++ ++ /* 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 ++ buffered 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 = (offset != 0 && fp->_offset != _IO_pos_BAD ++ && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS)) ++ == (_IO_IS_APPENDING | _IO_NO_READS) ++ && was_writing)); + } + +- result = get_file_offset (fp); ++ if (use_cached_offset) ++ result = fp->_offset; ++ else ++ result = get_file_offset (fp); + + if (result == EOF) + return result; diff --git a/glibc.spec b/glibc.spec index 6636ed9..13823c9 100644 --- a/glibc.spec +++ b/glibc.spec @@ -1,6 +1,6 @@ -%define glibcsrcdir glibc-2.19-58-ga4fb786 +%define glibcsrcdir glibc-2.19-76-g3ea0f74 %define glibcversion 2.19.90 -%define glibcrelease 2%{?dist} +%define glibcrelease 3%{?dist} # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -211,6 +211,10 @@ Patch2027: %{name}-rh819430.patch # Fix nscd to use permission names not constants. Patch2028: %{name}-rh1025126.patch +# Separate ftell logic from fseek +Patch2029: %{name}-rh1069559-1.patch +Patch2030: %{name}-rh1069559-2.patch + ############################################################################## # End of glibc patches. ############################################################################## @@ -535,6 +539,8 @@ package or when debugging this package. %patch0044 -p1 %patch0046 -p1 %patch2028 -p1 +%patch2029 -p1 +%patch2030 -p1 ############################################################################## # %%prep - Additional prep required... @@ -1621,6 +1627,10 @@ rm -f *.filelist* %endif %changelog +* Tue Feb 25 2014 Siddhesh Poyarekar - 2.19.90-3 +- Sync with upstream master. +- Separate ftell from fseek logic and avoid modifying FILE data (#1069559). + * Mon Feb 24 2014 Carlos O'Donell - 2.19.90-2 - Fix build-locale-archive failure to open default template. diff --git a/sources b/sources index f2ebbfe..dbacb61 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -5f636f8001d1397fa6e233a1009df6c1 glibc-2.19-58-ga4fb786.tar.gz +109ec1d0fa215e5a93e9202786166ccd glibc-2.19-76-g3ea0f74.tar.gz