Blame SOURCES/glibc-rh1063681.patch

147e83
diff --git glibc-2.17-c758a686/libio/Makefile glibc-2.17-c758a686/libio/Makefile
147e83
index 22dbcae..488ee51 100644
147e83
--- glibc-2.17-c758a686/libio/Makefile
147e83
+++ glibc-2.17-c758a686/libio/Makefile
147e83
@@ -60,7 +60,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
147e83
 	tst-wmemstream1 tst-wmemstream2 \
147e83
 	bug-memstream1 bug-wmemstream1 \
147e83
 	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
147e83
-	tst-fwrite-error
147e83
+	tst-fwrite-error tst-ftell-active-handler
147e83
 ifeq (yes,$(build-shared))
147e83
 # Add test-fopenloc only if shared library is enabled since it depends on
147e83
 # shared localedata objects.
147e83
diff --git glibc-2.17-c758a686/libio/fileops.c glibc-2.17-c758a686/libio/fileops.c
147e83
index a3499be..2e7bc8d 100644
147e83
--- glibc-2.17-c758a686/libio/fileops.c
147e83
+++ glibc-2.17-c758a686/libio/fileops.c
147e83
@@ -929,6 +929,93 @@ _IO_file_sync_mmap (_IO_FILE *fp)
147e83
   return 0;
147e83
 }
147e83
 
147e83
+/* Get the current file offset using a system call.  This is the safest method
147e83
+   to get the current file offset, since we are sure that we get the current
147e83
+   state of the file.  Before the stream handle is activated (by using fread,
147e83
+   fwrite, etc.), an application may alter the state of the file descriptor
147e83
+   underlying it by calling read/write/lseek on it.  Using a cached offset at
147e83
+   this point will result in returning the incorrect value.  Same is the case
147e83
+   when one switches from reading in a+ mode to writing, where the buffer has
147e83
+   not been flushed - the cached offset would reflect the reading position
147e83
+   while the actual write position would be at the end of the file.
147e83
+
147e83
+   do_ftell and do_ftell_wide may resort to using the cached offset in some
147e83
+   special cases instead of calling get_file_offset, but those cases should be
147e83
+   thoroughly described.  */
147e83
+_IO_off64_t
147e83
+get_file_offset (_IO_FILE *fp)
147e83
+{
147e83
+  if ((fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING)
147e83
+    {
147e83
+      struct stat64 st;
147e83
+      bool ret = (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode));
147e83
+      if (ret)
147e83
+	return st.st_size;
147e83
+      else
147e83
+	return EOF;
147e83
+    }
147e83
+  else
147e83
+    return _IO_SYSSEEK (fp, 0, _IO_seek_cur);
147e83
+}
147e83
+
147e83
+
147e83
+/* ftell{,o} implementation.  Don't modify any state of the file pointer while
147e83
+   we try to get the current state of the stream.  */
147e83
+static _IO_off64_t
147e83
+do_ftell (_IO_FILE *fp)
147e83
+{
147e83
+  _IO_off64_t result = 0;
147e83
+  bool use_cached_offset = false;
147e83
+
147e83
+  /* No point looking at unflushed data if we haven't allocated buffers
147e83
+     yet.  */
147e83
+  if (fp->_IO_buf_base != NULL)
147e83
+    {
147e83
+      bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
147e83
+			  || _IO_in_put_mode (fp));
147e83
+
147e83
+      /* Adjust for unflushed data.  */
147e83
+      if (!was_writing)
147e83
+	result -= fp->_IO_read_end - fp->_IO_read_ptr;
147e83
+      else
147e83
+	result += fp->_IO_write_ptr - fp->_IO_read_end;
147e83
+
147e83
+      /* It is safe to use the cached offset when available if there is
147e83
+	 unbuffered data (indicating that the file handle is active) and the
147e83
+	 handle is not for a file open in a+ mode.  The latter condition is
147e83
+	 because there could be a scenario where there is a switch from read
147e83
+	 mode to write mode using an fseek to an arbitrary position.  In this
147e83
+	 case, there would be unbuffered data due to be appended to the end of
147e83
+	 the file, but the offset may not necessarily be the end of the
147e83
+	 file.  It is fine to use the cached offset when the a+ stream is in
147e83
+	 read mode though, since the offset is maintained correctly in that
147e83
+	 case.  Note that this is not a comprehensive set of cases when the
147e83
+	 offset is reliable.  The offset may be reliable even in some cases
147e83
+	 where there is no unflushed input and the handle is active, but it's
147e83
+	 just that we don't have a way to identify that condition reliably.  */
147e83
+      use_cached_offset = (result != 0 && fp->_offset != _IO_pos_BAD
147e83
+			   && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS))
147e83
+			       == (_IO_IS_APPENDING | _IO_NO_READS)
147e83
+			       && was_writing));
147e83
+    }
147e83
+
147e83
+  if (use_cached_offset)
147e83
+    result += fp->_offset;
147e83
+  else
147e83
+    result += get_file_offset (fp);
147e83
+
147e83
+  if (result == EOF)
147e83
+    return result;
147e83
+
147e83
+  if (result < 0)
147e83
+    {
147e83
+      __set_errno (EINVAL);
147e83
+      return EOF;
147e83
+    }
147e83
+
147e83
+  return result;
147e83
+}
147e83
+
147e83
 
147e83
 _IO_off64_t
147e83
 _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
@@ -940,6 +1027,13 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
   _IO_off64_t result;
147e83
   _IO_off64_t delta, new_offset;
147e83
   long count;
147e83
+
147e83
+  /* Short-circuit into a separate function.  We don't want to mix any
147e83
+     functionality and we don't want to touch anything inside the FILE
147e83
+     object. */
147e83
+  if (mode == 0)
147e83
+    return do_ftell (fp);
147e83
+
147e83
   /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
147e83
      offset of the underlying file must be exact.  */
147e83
   int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end
147e83
@@ -948,9 +1042,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
   bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
147e83
 		      || _IO_in_put_mode (fp));
147e83
 
147e83
-  if (mode == 0)
147e83
-    dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
147e83
-
147e83
   /* Flush unwritten characters.
147e83
      (This may do an unneeded write if we seek within the buffer.
147e83
      But to be able to switch to reading, we would need to set
147e83
@@ -958,7 +1049,7 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
      which assumes file_ptr() is eGptr.  Anyway, since we probably
147e83
      end up flushing when we close(), it doesn't make much difference.)
147e83
      FIXME: simulate mem-mapped files. */
147e83
-  else if (was_writing && _IO_switch_to_get_mode (fp))
147e83
+  if (was_writing && _IO_switch_to_get_mode (fp))
147e83
     return EOF;
147e83
 
147e83
   if (fp->_IO_buf_base == NULL)
147e83
@@ -978,30 +1069,10 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
     {
147e83
     case _IO_seek_cur:
147e83
       /* Adjust for read-ahead (bytes is buffer). */
147e83
-      if (mode != 0 || !was_writing)
147e83
-	offset -= fp->_IO_read_end - fp->_IO_read_ptr;
147e83
-      else
147e83
-	{
147e83
-	  /* _IO_read_end coincides with fp._offset, so the actual file position
147e83
-	     is fp._offset - (_IO_read_end - new_write_ptr).  This is fine
147e83
-	     even if fp._offset is not set, since fp->_IO_read_end is then at
147e83
-	     _IO_buf_base and this adjustment is for unbuffered output.  */
147e83
-	  offset -= fp->_IO_read_end - fp->_IO_write_ptr;
147e83
-	}
147e83
+      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
147e83
 
147e83
       if (fp->_offset == _IO_pos_BAD)
147e83
-	{
147e83
-	  if (mode != 0)
147e83
-	    goto dumb;
147e83
-	  else
147e83
-	    {
147e83
-	      result = _IO_SYSSEEK (fp, 0, dir);
147e83
-	      if (result == EOF)
147e83
-		return result;
147e83
-
147e83
-	      fp->_offset = result;
147e83
-	    }
147e83
-	}
147e83
+	goto dumb;
147e83
       /* Make offset absolute, assuming current pointer is file_ptr(). */
147e83
       offset += fp->_offset;
147e83
       if (offset < 0)
147e83
@@ -1028,10 +1099,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
147e83
     }
147e83
   /* At this point, dir==_IO_seek_set. */
147e83
 
147e83
-  /* If we are only interested in the current position we've found it now.  */
147e83
-  if (mode == 0)
147e83
-    return offset;
147e83
-
147e83
   /* If destination is within current buffer, optimize: */
147e83
   if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
147e83
       && !_IO_in_backup (fp))
147e83
diff --git glibc-2.17-c758a686/libio/iofdopen.c glibc-2.17-c758a686/libio/iofdopen.c
147e83
index 066ff19..3f266f7 100644
147e83
--- glibc-2.17-c758a686/libio/iofdopen.c
147e83
+++ glibc-2.17-c758a686/libio/iofdopen.c
147e83
@@ -141,9 +141,6 @@ _IO_new_fdopen (fd, mode)
147e83
 #ifdef _IO_MTSAFE_IO
147e83
   new_f->fp.file._lock = &new_f->lock;
147e83
 #endif
147e83
-  /* Set up initially to use the `maybe_mmap' jump tables rather than using
147e83
-     __fopen_maybe_mmap to do it, because we need them in place before we
147e83
-     call _IO_file_attach or else it will allocate a buffer immediately.  */
147e83
   _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd,
147e83
 #ifdef _G_HAVE_MMAP
147e83
 	       (use_mmap && (read_write & _IO_NO_WRITES))
147e83
@@ -159,13 +156,12 @@ _IO_new_fdopen (fd, mode)
147e83
 #if  !_IO_UNIFIED_JUMPTABLES
147e83
   new_f->fp.vtable = NULL;
147e83
 #endif
147e83
-  if (_IO_file_attach ((_IO_FILE *) &new_f->fp, fd) == NULL)
147e83
-    {
147e83
-      _IO_setb (&new_f->fp.file, NULL, NULL, 0);
147e83
-      _IO_un_link (&new_f->fp);
147e83
-      free (new_f);
147e83
-      return NULL;
147e83
-    }
147e83
+  /* We only need to record the fd because _IO_file_init will have unset the
147e83
+     offset.  It is important to unset the cached offset because the real
147e83
+     offset in the file could change between now and when the handle is
147e83
+     activated and we would then mislead ftell into believing that we have a
147e83
+     valid offset.  */
147e83
+  new_f->fp.file._fileno = fd;
147e83
   new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
147e83
 
147e83
   _IO_mask_flags (&new_f->fp.file, read_write,
147e83
diff --git glibc-2.17-c758a686/libio/iofwide.c glibc-2.17-c758a686/libio/iofwide.c
147e83
index 5cff632..64187e4 100644
147e83
--- glibc-2.17-c758a686/libio/iofwide.c
147e83
+++ glibc-2.17-c758a686/libio/iofwide.c
147e83
@@ -199,12 +199,6 @@ _IO_fwide (fp, mode)
147e83
 
147e83
       /* From now on use the wide character callback functions.  */
147e83
       ((struct _IO_FILE_plus *) fp)->vtable = fp->_wide_data->_wide_vtable;
147e83
-
147e83
-      /* One last twist: we get the current stream position.  The wide
147e83
-	 char streams have much more problems with not knowing the
147e83
-	 current position and so we should disable the optimization
147e83
-	 which allows the functions without knowing the position.  */
147e83
-      fp->_offset = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
147e83
     }
147e83
 
147e83
   /* Set the mode now.  */
147e83
diff --git glibc-2.17-c758a686/libio/libioP.h glibc-2.17-c758a686/libio/libioP.h
147e83
index 4ca723c..8a7b85b 100644
147e83
--- glibc-2.17-c758a686/libio/libioP.h
147e83
+++ glibc-2.17-c758a686/libio/libioP.h
147e83
@@ -397,6 +397,7 @@ extern void _IO_wdoallocbuf (_IO_FILE *) __THROW;
147e83
 libc_hidden_proto (_IO_wdoallocbuf)
147e83
 extern void _IO_unsave_wmarkers (_IO_FILE *) __THROW;
147e83
 extern unsigned _IO_adjust_wcolumn (unsigned, const wchar_t *, int) __THROW;
147e83
+extern _IO_off64_t get_file_offset (_IO_FILE *fp);
147e83
 
147e83
 /* Marker-related function. */
147e83
 
147e83
diff --git glibc-2.17-c758a686/libio/tst-ftell-active-handler.c glibc-2.17-c758a686/libio/tst-ftell-active-handler.c
147e83
new file mode 100644
147e83
index 0000000..175e904
147e83
--- /dev/null
147e83
+++ glibc-2.17-c758a686/libio/tst-ftell-active-handler.c
147e83
@@ -0,0 +1,384 @@
147e83
+/* Verify that ftell returns the correct value at various points before and
147e83
+   after the handler on which it is called becomes active.
147e83
+   Copyright (C) 2014 Free Software Foundation, Inc.
147e83
+   This file is part of the GNU C Library.
147e83
+
147e83
+   The GNU C Library is free software; you can redistribute it and/or
147e83
+   modify it under the terms of the GNU Lesser General Public
147e83
+   License as published by the Free Software Foundation; either
147e83
+   version 2.1 of the License, or (at your option) any later version.
147e83
+
147e83
+   The GNU C Library is distributed in the hope that it will be useful,
147e83
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
147e83
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
147e83
+   Lesser General Public License for more details.
147e83
+
147e83
+   You should have received a copy of the GNU Lesser General Public
147e83
+   License along with the GNU C Library; if not, see
147e83
+   <http://www.gnu.org/licenses/>.  */
147e83
+
147e83
+#include <stdio.h>
147e83
+#include <stdlib.h>
147e83
+#include <string.h>
147e83
+#include <errno.h>
147e83
+#include <unistd.h>
147e83
+#include <fcntl.h>
147e83
+#include <locale.h>
147e83
+#include <wchar.h>
147e83
+
147e83
+static int do_test (void);
147e83
+
147e83
+#define TEST_FUNCTION do_test ()
147e83
+#include "../test-skeleton.c"
147e83
+
147e83
+#define get_handles_fdopen(filename, fd, fp, fd_mode, mode) \
147e83
+({									      \
147e83
+  int ret = 0;								      \
147e83
+  (fd) = open ((filename), (fd_mode), 0);				      \
147e83
+  if ((fd) == -1)							      \
147e83
+    {									      \
147e83
+      printf ("open failed: %m\n");					      \
147e83
+      ret = 1;								      \
147e83
+    }									      \
147e83
+  else									      \
147e83
+    {									      \
147e83
+      (fp) = fdopen ((fd), (mode));					      \
147e83
+      if ((fp) == NULL)							      \
147e83
+        {								      \
147e83
+          printf ("fdopen failed: %m\n");				      \
147e83
+          close (fd);							      \
147e83
+          ret = 1;							      \
147e83
+        }								      \
147e83
+    }									      \
147e83
+  ret;									      \
147e83
+})
147e83
+
147e83
+#define get_handles_fopen(filename, fd, fp, mode) \
147e83
+({									      \
147e83
+  int ret = 0;								      \
147e83
+  (fp) = fopen ((filename), (mode));					      \
147e83
+  if ((fp) == NULL)							      \
147e83
+    {									      \
147e83
+      printf ("fopen failed: %m\n");					      \
147e83
+      ret = 1;								      \
147e83
+    }									      \
147e83
+  else									      \
147e83
+    {									      \
147e83
+      (fd) = fileno (fp);						      \
147e83
+      if ((fd) == -1)							      \
147e83
+        {								      \
147e83
+	  printf ("fileno failed: %m\n");				      \
147e83
+	  ret = 1;							      \
147e83
+	}								      \
147e83
+    }									      \
147e83
+  ret;									      \
147e83
+})
147e83
+
147e83
+/* data points to either char_data or wide_data, depending on whether we're
147e83
+   testing regular file mode or wide mode respectively.  Similarly,
147e83
+   fputs_func points to either fputs or fputws.  data_len keeps track of the
147e83
+   length of the current data and file_len maintains the current file
147e83
+   length.  */
147e83
+static const void *data;
147e83
+static const char *char_data = "abcdef";
147e83
+static const wchar_t *wide_data = L"abcdef";
147e83
+static size_t data_len;
147e83
+static size_t file_len;
147e83
+
147e83
+typedef int (*fputs_func_t) (const void *data, FILE *fp);
147e83
+fputs_func_t fputs_func;
147e83
+
147e83
+/* Test that the value of ftell is not cached when the stream handle is not
147e83
+   active.  */
147e83
+static int
147e83
+do_ftell_test (const char *filename)
147e83
+{
147e83
+  int ret = 0;
147e83
+  struct test
147e83
+    {
147e83
+      const char *mode;
147e83
+      int fd_mode;
147e83
+      size_t old_off;
147e83
+      size_t new_off;
147e83
+    } test_modes[] = {
147e83
+	  /* In w, w+ and r+ modes, the file position should be at the
147e83
+	     beginning of the file.  After the write, the offset should be
147e83
+	     updated to data_len.  */
147e83
+	  {"w", O_WRONLY, 0, data_len},
147e83
+	  {"w+", O_RDWR, 0, data_len},
147e83
+	  {"r+", O_RDWR, 0, data_len},
147e83
+	  /* For 'a' and 'a+' modes, the initial file position should be the
147e83
+	     current end of file. After the write, the offset has data_len
147e83
+	     added to the old value.  */
147e83
+	  {"a", O_WRONLY, data_len, 2 * data_len},
147e83
+	  {"a+", O_RDWR, 2 * data_len, 3 * data_len},
147e83
+    };
147e83
+  for (int j = 0; j < 2; j++)
147e83
+    {
147e83
+      for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
147e83
+	{
147e83
+	  FILE *fp;
147e83
+	  int fd;
147e83
+	  printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
147e83
+		  test_modes[i].mode);
147e83
+
147e83
+	  if (j == 0)
147e83
+	    ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
147e83
+				      test_modes[i].mode);
147e83
+	  else
147e83
+	    ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
147e83
+
147e83
+	  if (ret != 0)
147e83
+	    return ret;
147e83
+
147e83
+	  long off = ftell (fp);
147e83
+	  if (off != test_modes[i].old_off)
147e83
+	    {
147e83
+	      printf ("Incorrect old offset.  Expected %zu but got %ld, ",
147e83
+		      test_modes[i].old_off, off);
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+	  else
147e83
+	    printf ("old offset = %ld, ", off);
147e83
+
147e83
+	  /* The effect of this write on the offset should be seen in the ftell
147e83
+	     call that follows it.  */
147e83
+	  int ret = write (fd, data, data_len);
147e83
+	  off = ftell (fp);
147e83
+
147e83
+	  if (off != test_modes[i].new_off)
147e83
+	    {
147e83
+	      printf ("Incorrect new offset.  Expected %zu but got %ld\n",
147e83
+		      test_modes[i].old_off, off);
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+	  else
147e83
+	    printf ("new offset = %ld\n", off);
147e83
+
147e83
+	  fclose (fp);
147e83
+	}
147e83
+    }
147e83
+
147e83
+  return ret;
147e83
+}
147e83
+
147e83
+/* This test opens the file for writing, moves the file offset of the
147e83
+   underlying file, writes out data and then checks if ftell trips on it.  */
147e83
+static int
147e83
+do_write_test (const char *filename)
147e83
+{
147e83
+  FILE *fp = NULL;
147e83
+  int fd;
147e83
+  int ret = 0;
147e83
+  struct test
147e83
+    {
147e83
+      const char *mode;
147e83
+      int fd_mode;
147e83
+    } test_modes[] = {
147e83
+	  {"w", O_WRONLY},
147e83
+	  {"w+", O_RDWR},
147e83
+	  {"r+", O_RDWR}
147e83
+    };
147e83
+
147e83
+  for (int j = 0; j < 2; j++)
147e83
+    {
147e83
+      for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
147e83
+	{
147e83
+	  printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
147e83
+		  test_modes[i].mode);
147e83
+
147e83
+	  if (j == 0)
147e83
+	    ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
147e83
+	  else
147e83
+	    ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
147e83
+				      test_modes[i].mode);
147e83
+
147e83
+	  if (ret != 0)
147e83
+	    return ret;
147e83
+
147e83
+	  /* Move offset to just before the end of the file.  */
147e83
+	  off_t ret = lseek (fd, file_len - 1, SEEK_SET);
147e83
+	  if (ret == -1)
147e83
+	    {
147e83
+	      printf ("lseek failed: %m\n");
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+
147e83
+	  /* Write some data.  */
147e83
+	  size_t written = fputs_func (data, fp);
147e83
+
147e83
+	  if (written == EOF)
147e83
+	    {
147e83
+	      printf ("fputs[1] failed to write data\n");
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+
147e83
+	  /* Verify that the offset points to the end of the file.  The length
147e83
+	     of the file would be the original length + the length of data
147e83
+	     written to it - the amount by which we moved the offset using
147e83
+	     lseek.  */
147e83
+	  long offset = ftell (fp);
147e83
+	  file_len = file_len - 1 + data_len;
147e83
+
147e83
+	  if (offset != file_len)
147e83
+	    {
147e83
+	      printf ("Incorrect offset.  Expected %zu, but got %ld\n",
147e83
+		      file_len, offset);
147e83
+
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+
147e83
+	  printf ("offset = %ld\n", offset);
147e83
+	  fclose (fp);
147e83
+        }
147e83
+    }
147e83
+
147e83
+  return ret;
147e83
+}
147e83
+
147e83
+/* This test opens a file in append mode, writes some data, and then verifies
147e83
+   that ftell does not trip over it.  */
147e83
+static int
147e83
+do_append_test (const char *filename)
147e83
+{
147e83
+  FILE *fp = NULL;
147e83
+  int ret = 0;
147e83
+  int fd;
147e83
+
147e83
+  struct test
147e83
+    {
147e83
+      const char *mode;
147e83
+      int fd_mode;
147e83
+    } test_modes[] = {
147e83
+	  {"a", O_WRONLY},
147e83
+	  {"a+", O_RDWR}
147e83
+    };
147e83
+
147e83
+  for (int j = 0; j < 2; j++)
147e83
+    {
147e83
+      for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
147e83
+	{
147e83
+	  printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
147e83
+		  test_modes[i].mode);
147e83
+
147e83
+	  if (j == 0)
147e83
+	    ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
147e83
+	  else
147e83
+	    ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
147e83
+				      test_modes[i].mode);
147e83
+
147e83
+	  if (ret != 0)
147e83
+	    return ret;
147e83
+
147e83
+	  /* Write some data.  */
147e83
+	  size_t written = fputs_func (data, fp);
147e83
+
147e83
+	  if (written == EOF)
147e83
+	    {
147e83
+	      printf ("fputs[1] failed to write all data\n");
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+
147e83
+	  /* Verify that the offset points to the end of the file.  The file
147e83
+	     len is maintained by adding data_len each time to reflect the data
147e83
+	     written to it.  */
147e83
+	  long offset = ftell (fp);
147e83
+	  file_len += data_len;
147e83
+
147e83
+	  if (offset != file_len)
147e83
+	    {
147e83
+	      printf ("Incorrect offset.  Expected %zu, but got %ld\n",
147e83
+		      file_len, offset);
147e83
+
147e83
+	      ret |= 1;
147e83
+	    }
147e83
+
147e83
+	  printf ("offset = %ld\n", offset);
147e83
+	  fclose (fp);
147e83
+	}
147e83
+    }
147e83
+
147e83
+  return ret;
147e83
+}
147e83
+
147e83
+static int
147e83
+do_one_test (const char *filename)
147e83
+{
147e83
+  int ret = 0;
147e83
+
147e83
+  ret |= do_ftell_test (filename);
147e83
+  ret |= do_write_test (filename);
147e83
+  ret |= do_append_test (filename);
147e83
+
147e83
+  return ret;
147e83
+}
147e83
+
147e83
+/* Run a set of tests for ftell for regular files and wide mode files.  */
147e83
+static int
147e83
+do_test (void)
147e83
+{
147e83
+  int ret = 0;
147e83
+  FILE *fp = NULL;
147e83
+  char *filename;
147e83
+  size_t written;
147e83
+  int fd = create_temp_file ("tst-active-handler-tmp.", &filename);
147e83
+
147e83
+  if (fd == -1)
147e83
+    {
147e83
+      printf ("create_temp_file: %m\n");
147e83
+      return 1;
147e83
+    }
147e83
+
147e83
+  fp = fdopen (fd, "w");
147e83
+  if (fp == NULL)
147e83
+    {
147e83
+      printf ("fdopen[0]: %m\n");
147e83
+      close (fd);
147e83
+      return 1;
147e83
+    }
147e83
+
147e83
+  data = char_data;
147e83
+  data_len = strlen (char_data);
147e83
+  file_len = strlen (char_data);
147e83
+  written = fputs (data, fp);
147e83
+
147e83
+  if (written == EOF)
147e83
+    {
147e83
+      printf ("fputs[1] failed to write data\n");
147e83
+      ret = 1;
147e83
+    }
147e83
+
147e83
+  fclose (fp);
147e83
+  if (ret)
147e83
+    return ret;
147e83
+
147e83
+  /* Tests for regular files.  */
147e83
+  puts ("Regular mode:");
147e83
+  fputs_func = (fputs_func_t) fputs;
147e83
+  data = char_data;
147e83
+  data_len = strlen (char_data);
147e83
+  ret |= do_one_test (filename);
147e83
+
147e83
+  /* Truncate the file before repeating the tests in wide mode.  */
147e83
+  fp = fopen (filename, "w");
147e83
+  if (fp == NULL)
147e83
+    {
147e83
+      printf ("fopen failed %m\n");
147e83
+      return 1;
147e83
+    }
147e83
+  fclose (fp);
147e83
+
147e83
+  /* Tests for wide files.  */
147e83
+  puts ("Wide mode:");
147e83
+  if (setlocale (LC_ALL, "en_US.UTF-8") == NULL)
147e83
+    {
147e83
+      printf ("Cannot set en_US.UTF-8 locale.\n");
147e83
+      return 1;
147e83
+    }
147e83
+  fputs_func = (fputs_func_t) fputws;
147e83
+  data = wide_data;
147e83
+  data_len = wcslen (wide_data);
147e83
+  ret |= do_one_test (filename);
147e83
+
147e83
+  return ret;
147e83
+}
147e83
diff --git glibc-2.17-c758a686/libio/wfileops.c glibc-2.17-c758a686/libio/wfileops.c
147e83
index 9cebe77..8b2e108 100644
147e83
--- glibc-2.17-c758a686/libio/wfileops.c
147e83
+++ glibc-2.17-c758a686/libio/wfileops.c
147e83
@@ -596,29 +596,25 @@ done:
147e83
   return 0;
147e83
 }
147e83
 
147e83
-_IO_off64_t
147e83
-_IO_wfile_seekoff (fp, offset, dir, mode)
147e83
-     _IO_FILE *fp;
147e83
-     _IO_off64_t offset;
147e83
-     int dir;
147e83
-     int mode;
147e83
+/* ftell{,o} implementation for wide mode.  Don't modify any state of the file
147e83
+   pointer while we try to get the current state of the stream.  */
147e83
+static _IO_off64_t
147e83
+do_ftell_wide (_IO_FILE *fp)
147e83
 {
147e83
-  _IO_off64_t result;
147e83
-  _IO_off64_t delta, new_offset;
147e83
-  long int count;
147e83
-  /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
147e83
-     offset of the underlying file must be exact.  */
147e83
-  int must_be_exact = ((fp->_wide_data->_IO_read_base
147e83
-			== fp->_wide_data->_IO_read_end)
147e83
-		       && (fp->_wide_data->_IO_write_base
147e83
-			   == fp->_wide_data->_IO_write_ptr));
147e83
+  _IO_off64_t result, offset = 0;
147e83
+  bool use_cached_offset = false;
147e83
 
147e83
-  bool was_writing = ((fp->_wide_data->_IO_write_ptr
147e83
-		       > fp->_wide_data->_IO_write_base)
147e83
-		      || _IO_in_put_mode (fp));
147e83
-
147e83
-  if (mode == 0)
147e83
+  /* No point looking for offsets in the buffer if it hasn't even been
147e83
+     allocated.  */
147e83
+  if (fp->_wide_data->_IO_buf_base != NULL)
147e83
     {
147e83
+      const wchar_t *wide_read_base;
147e83
+      const wchar_t *wide_read_ptr;
147e83
+      const wchar_t *wide_read_end;
147e83
+      bool was_writing = ((fp->_wide_data->_IO_write_ptr
147e83
+			   > fp->_wide_data->_IO_write_base)
147e83
+			  || _IO_in_put_mode (fp));
147e83
+
147e83
       /* XXX For wide stream with backup store it is not very
147e83
 	 reasonable to determine the offset.  The pushed-back
147e83
 	 character might require a state change and we need not be
147e83
@@ -633,14 +629,142 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
147e83
 	      return -1;
147e83
 	    }
147e83
 
147e83
-	  /* There is no more data in the backup buffer.  We can
147e83
-	     switch back.  */
147e83
-	  _IO_switch_to_main_wget_area (fp);
147e83
+	  /* Nothing in the backup store, so note the backed up pointers
147e83
+	     without changing the state.  */
147e83
+	  wide_read_base = fp->_wide_data->_IO_save_base;
147e83
+	  wide_read_ptr = wide_read_base;
147e83
+	  wide_read_end = fp->_wide_data->_IO_save_end;
147e83
+	}
147e83
+      else
147e83
+	{
147e83
+	  wide_read_base = fp->_wide_data->_IO_read_base;
147e83
+	  wide_read_ptr = fp->_wide_data->_IO_read_ptr;
147e83
+	  wide_read_end = fp->_wide_data->_IO_read_end;
147e83
+	}
147e83
+
147e83
+      struct _IO_codecvt *cv = fp->_codecvt;
147e83
+      int clen = (*cv->__codecvt_do_encoding) (cv);
147e83
+
147e83
+      if (!was_writing)
147e83
+	{
147e83
+	  if (clen > 0)
147e83
+	    {
147e83
+	      offset -= (wide_read_end - wide_read_ptr) * clen;
147e83
+	      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
147e83
+	    }
147e83
+	  else
147e83
+	    {
147e83
+	      int nread;
147e83
+
147e83
+	      size_t delta = wide_read_ptr - wide_read_base;
147e83
+	      __mbstate_t state = fp->_wide_data->_IO_last_state;
147e83
+	      nread = (*cv->__codecvt_do_length) (cv, &state,
147e83
+						  fp->_IO_read_base,
147e83
+						  fp->_IO_read_end, delta);
147e83
+	      offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
147e83
+	    }
147e83
+	}
147e83
+      else
147e83
+	{
147e83
+	  if (clen > 0)
147e83
+	    offset += (fp->_wide_data->_IO_write_ptr
147e83
+		       - fp->_wide_data->_IO_write_base) * clen;
147e83
+	  else
147e83
+	    {
147e83
+	      size_t delta = (fp->_wide_data->_IO_write_ptr
147e83
+			      - fp->_wide_data->_IO_write_base);
147e83
+
147e83
+	      /* Allocate enough space for the conversion.  */
147e83
+	      size_t outsize = delta * sizeof (wchar_t);
147e83
+	      char *out = malloc (outsize);
147e83
+	      char *outstop = out;
147e83
+	      const wchar_t *in = fp->_wide_data->_IO_write_base;
147e83
+
147e83
+	      enum __codecvt_result status;
147e83
+
147e83
+	      __mbstate_t state = fp->_wide_data->_IO_last_state;
147e83
+	      status = (*cv->__codecvt_do_out) (cv, &state,
147e83
+						in, in + delta, &in,
147e83
+						out, out + outsize, &outstop);
147e83
+
147e83
+	      /* We don't check for __codecvt_partial because it can be
147e83
+		 returned on one of two conditions: either the output
147e83
+		 buffer is full or the input sequence is incomplete.  We
147e83
+		 take care to allocate enough buffer and our input
147e83
+		 sequences must be complete since they are accepted as
147e83
+		 wchar_t; if not, then that is an error.  */
147e83
+	      if (__glibc_unlikely (status != __codecvt_ok))
147e83
+		return WEOF;
147e83
+
147e83
+	      offset += outstop - out;
147e83
+	    }
147e83
+
147e83
+	  /* _IO_read_end coincides with fp._offset, so the actual file
147e83
+	     position is fp._offset - (_IO_read_end - new_write_ptr).  */
147e83
+	  offset -= fp->_IO_read_end - fp->_IO_write_ptr;
147e83
 	}
147e83
 
147e83
-      dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
147e83
+      /* It is safe to use the cached offset when available if there is
147e83
+	 unbuffered data (indicating that the file handle is active) and
147e83
+	 the handle is not for a file open in a+ mode.  The latter
147e83
+	 condition is because there could be a scenario where there is a
147e83
+	 switch from read mode to write mode using an fseek to an arbitrary
147e83
+	 position.  In this case, there would be unbuffered data due to be
147e83
+	 appended to the end of the file, but the offset may not
147e83
+	 necessarily be the end of the file.  It is fine to use the cached
147e83
+	 offset when the a+ stream is in read mode though, since the offset
147e83
+	 is maintained correctly in that case.  Note that this is not a
147e83
+	 comprehensive set of cases when the offset is reliable.  The
147e83
+	 offset may be reliable even in some cases where there is no
147e83
+	 unflushed input and the handle is active, but it's just that we
147e83
+	 don't have a way to identify that condition reliably.  */
147e83
+      use_cached_offset = (offset != 0 && fp->_offset != _IO_pos_BAD
147e83
+			   && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS))
147e83
+			       == (_IO_IS_APPENDING | _IO_NO_READS)
147e83
+			       && was_writing));
147e83
     }
147e83
 
147e83
+  if (use_cached_offset)
147e83
+    result = fp->_offset;
147e83
+  else
147e83
+    result = get_file_offset (fp);
147e83
+
147e83
+  if (result == EOF)
147e83
+    return result;
147e83
+
147e83
+  result += offset;
147e83
+
147e83
+  return result;
147e83
+}
147e83
+
147e83
+_IO_off64_t
147e83
+_IO_wfile_seekoff (fp, offset, dir, mode)
147e83
+     _IO_FILE *fp;
147e83
+     _IO_off64_t offset;
147e83
+     int dir;
147e83
+     int mode;
147e83
+{
147e83
+  _IO_off64_t result;
147e83
+  _IO_off64_t delta, new_offset;
147e83
+  long int count;
147e83
+
147e83
+  /* Short-circuit into a separate function.  We don't want to mix any
147e83
+     functionality and we don't want to touch anything inside the FILE
147e83
+     object. */
147e83
+  if (mode == 0)
147e83
+    return do_ftell_wide (fp);
147e83
+
147e83
+  /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
147e83
+     offset of the underlying file must be exact.  */
147e83
+  int must_be_exact = ((fp->_wide_data->_IO_read_base
147e83
+			== fp->_wide_data->_IO_read_end)
147e83
+		       && (fp->_wide_data->_IO_write_base
147e83
+			   == fp->_wide_data->_IO_write_ptr));
147e83
+
147e83
+  bool was_writing = ((fp->_wide_data->_IO_write_ptr
147e83
+		       > fp->_wide_data->_IO_write_base)
147e83
+		      || _IO_in_put_mode (fp));
147e83
+
147e83
   /* Flush unwritten characters.
147e83
      (This may do an unneeded write if we seek within the buffer.
147e83
      But to be able to switch to reading, we would need to set
147e83
@@ -648,7 +772,7 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
147e83
      which assumes file_ptr() is eGptr.  Anyway, since we probably
147e83
      end up flushing when we close(), it doesn't make much difference.)
147e83
      FIXME: simulate mem-mapped files. */
147e83
-  else if (was_writing && _IO_switch_to_wget_mode (fp))
147e83
+  if (was_writing && _IO_switch_to_wget_mode (fp))
147e83
     return WEOF;
147e83
 
147e83
   if (fp->_wide_data->_IO_buf_base == NULL)
147e83
@@ -693,7 +817,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
147e83
 	    {
147e83
 	      int nread;
147e83
 
147e83
-	    flushed:
147e83
 	      delta = (fp->_wide_data->_IO_read_ptr
147e83
 		       - fp->_wide_data->_IO_read_base);
147e83
 	      fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
147e83
@@ -706,80 +829,9 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
147e83
 	      offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
147e83
 	    }
147e83
 	}
147e83
-      else
147e83
-	{
147e83
-	  char *new_write_ptr = fp->_IO_write_ptr;
147e83
-
147e83
-	  if (clen > 0)
147e83
-	    offset += (fp->_wide_data->_IO_write_ptr
147e83
-		       - fp->_wide_data->_IO_write_base) / clen;
147e83
-	  else
147e83
-	    {
147e83
-	      enum __codecvt_result status = __codecvt_ok;
147e83
-	      delta = (fp->_wide_data->_IO_write_ptr
147e83
-		       - fp->_wide_data->_IO_write_base);
147e83
-	      const wchar_t *write_base = fp->_wide_data->_IO_write_base;
147e83
-
147e83
-	      /* FIXME: This actually ends up in two iterations of conversion,
147e83
-		 one here and the next when the buffer actually gets flushed.
147e83
-		 It may be possible to optimize this in future so that
147e83
-		 wdo_write identifies already converted content and does not
147e83
-		 redo it.  In any case, this is much better than having to
147e83
-		 flush buffers for every ftell.  */
147e83
-	      do
147e83
-		{
147e83
-		  /* There is not enough space in the buffer to do the entire
147e83
-		     conversion, so there is no point trying to avoid the
147e83
-		     buffer flush.  Just do it and go back to how it was with
147e83
-		     the read mode.  */
147e83
-		  if (status == __codecvt_partial
147e83
-		      || (delta > 0 && new_write_ptr == fp->_IO_buf_end))
147e83
-		    {
147e83
-		      if (_IO_switch_to_wget_mode (fp))
147e83
-			return WEOF;
147e83
-		      goto flushed;
147e83
-		    }
147e83
-
147e83
-		  const wchar_t *new_wbase = fp->_wide_data->_IO_write_base;
147e83
-		  fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
147e83
-		  status = (*cv->__codecvt_do_out) (cv,
147e83
-						    &fp->_wide_data->_IO_state,
147e83
-						    write_base,
147e83
-						    write_base + delta,
147e83
-						    &new_wbase,
147e83
-						    new_write_ptr,
147e83
-						    fp->_IO_buf_end,
147e83
-						    &new_write_ptr);
147e83
-
147e83
-		  delta -= new_wbase - write_base;
147e83
-
147e83
-		  /* If there was an error, then return WEOF.
147e83
-		     TODO: set buffer state.  */
147e83
-		  if (__builtin_expect (status == __codecvt_error, 0))
147e83
-		      return WEOF;
147e83
-		}
147e83
-	      while (delta > 0);
147e83
-	    }
147e83
-
147e83
-	  /* _IO_read_end coincides with fp._offset, so the actual file position
147e83
-	     is fp._offset - (_IO_read_end - new_write_ptr).  This is fine
147e83
-	     even if fp._offset is not set, since fp->_IO_read_end is then at
147e83
-	     _IO_buf_base and this adjustment is for unbuffered output.  */
147e83
-	  offset -= fp->_IO_read_end - new_write_ptr;
147e83
-	}
147e83
 
147e83
       if (fp->_offset == _IO_pos_BAD)
147e83
-	{
147e83
-	  if (mode != 0)
147e83
-	    goto dumb;
147e83
-	  else
147e83
-	    {
147e83
-	      result = _IO_SYSSEEK (fp, 0, dir);
147e83
-	      if (result == EOF)
147e83
-		return result;
147e83
-	      fp->_offset = result;
147e83
-	    }
147e83
-	}
147e83
+	goto dumb;
147e83
 
147e83
       /* Make offset absolute, assuming current pointer is file_ptr(). */
147e83
       offset += fp->_offset;
147e83
@@ -802,10 +854,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
147e83
     }
147e83
   /* At this point, dir==_IO_seek_set. */
147e83
 
147e83
-  /* If we are only interested in the current position we've found it now.  */
147e83
-  if (mode == 0)
147e83
-    return offset;
147e83
-
147e83
   /* If destination is within current buffer, optimize: */
147e83
   if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
147e83
       && !_IO_in_backup (fp))