olga / rpms / glibc

Forked from rpms/glibc 5 years ago
Clone

Blame SOURCES/glibc-rh1063681.patch

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