[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