[glibc] Sync with upstream master and Resolve: #1069559
Siddhesh Poyarekar
siddhesh at fedoraproject.org
Tue Feb 25 09:52:48 UTC 2014
commit 52f69e7bd59408115a3d3909f8a12561e92bb7a2
Author: Siddhesh Poyarekar <siddhesh at redhat.com>
Date: Tue Feb 25 15:22:53 2014 +0530
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(-)
---
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 <siddhesh at redhat.com>
+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
++ <http://www.gnu.org/licenses/>. */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <locale.h>
++#include <wchar.h>
++
++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 <siddhesh at redhat.com>
+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 <siddhesh at redhat.com> - 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 <carlos at redhat.com> - 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
More information about the scm-commits
mailing list