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