fa3bfd
commit be349d7042de84c3c5157a5c1fbcad580aed33e1
985194
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
fa3bfd
Date:   Thu Dec 4 08:08:37 2014 +0530
985194
985194
    ftell: seek to end only when there are unflushed bytes (BZ #17647)
985194
    
985194
    Currently we seek to end of file if there are unflushed writes or the
985194
    stream is in write mode, to get the current offset for writing in
985194
    append mode, which is the end of file.  The latter case (i.e. stream
985194
    is in write mode, but no unflushed writes) is unnecessary since it
985194
    will only happen when the stream has just been flushed, in which case
985194
    the recorded offset ought to be reliable.
985194
    
985194
    Removing that case lets ftell give the correct offset when it follows
985194
    an ftruncate.  The latter truncates the file, but does not change the
985194
    file position, due to which it is permissible to call ftell without an
985194
    intervening fseek call.
985194
    
985194
    Tested on x86_64 to verify that the added test case fails without the
985194
    patch and succeeds with it, and that there are no additional
985194
    regressions due to it.
985194
    
fa3bfd
            [BZ #17647]
fa3bfd
            * libio/fileops.c (do_ftell): Seek only when there are
fa3bfd
            unflushed writes.
fa3bfd
            * libio/wfileops.c (do_ftell_wide): Likewise.
fa3bfd
            * libio/tst-ftell-active-handler.c (do_ftruncate_test): New
fa3bfd
            test case.
fa3bfd
            (do_one_test): Call it.
985194
12745e
diff --git glibc-2.17-c758a686/libio/fileops.c glibc-2.17-c758a686/libio/fileops.c
985194
index e0d7b76..1fc5719 100644
12745e
--- glibc-2.17-c758a686/libio/fileops.c
12745e
+++ glibc-2.17-c758a686/libio/fileops.c
985194
@@ -943,15 +943,14 @@ do_ftell (_IO_FILE *fp)
985194
      yet.  */
985194
   if (fp->_IO_buf_base != NULL)
985194
     {
985194
-      bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
985194
-			  || _IO_in_put_mode (fp));
985194
+      bool unflushed_writes = (fp->_IO_write_ptr > fp->_IO_write_base);
985194
 
985194
       bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
985194
 
985194
       /* When we have unflushed writes in append mode, seek to the end of the
985194
 	 file and record that offset.  This is the only time we change the file
985194
 	 stream state and it is safe since the file handle is active.  */
985194
-      if (was_writing && append_mode)
985194
+      if (unflushed_writes && append_mode)
985194
 	{
985194
 	  result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
985194
 	  if (result == _IO_pos_BAD)
985194
@@ -961,7 +960,7 @@ do_ftell (_IO_FILE *fp)
985194
 	}
985194
 
985194
       /* Adjust for unflushed data.  */
985194
-      if (!was_writing)
985194
+      if (!unflushed_writes)
985194
 	offset -= fp->_IO_read_end - fp->_IO_read_ptr;
985194
       /* We don't trust _IO_read_end to represent the current file offset when
985194
 	 writing in append mode because the value would have to be shifted to
12745e
diff --git glibc-2.17-c758a686/libio/tst-ftell-active-handler.c glibc-2.17-c758a686/libio/tst-ftell-active-handler.c
985194
index e9dc7b3..9f23c55 100644
12745e
--- glibc-2.17-c758a686/libio/tst-ftell-active-handler.c
12745e
+++ glibc-2.17-c758a686/libio/tst-ftell-active-handler.c
985194
@@ -88,6 +88,95 @@ static size_t file_len;
985194
 typedef int (*fputs_func_t) (const void *data, FILE *fp);
985194
 fputs_func_t fputs_func;
985194
 
985194
+/* This test verifies that the offset reported by ftell is correct after the
985194
+   file is truncated using ftruncate.  ftruncate does not change the file
985194
+   offset on truncation and hence, SEEK_CUR should continue to point to the
985194
+   old offset and not be changed to the new offset.  */
985194
+static int
985194
+do_ftruncate_test (const char *filename)
985194
+{
985194
+  FILE *fp = NULL;
985194
+  int fd;
985194
+  int ret = 0;
985194
+  struct test
985194
+    {
985194
+      const char *mode;
985194
+      int fd_mode;
985194
+    } test_modes[] = {
985194
+	  {"r+", O_RDWR},
985194
+	  {"w", O_WRONLY},
985194
+	  {"w+", O_RDWR},
985194
+	  {"a", O_WRONLY},
985194
+	  {"a+", O_RDWR}
985194
+    };
985194
+
985194
+  for (int j = 0; j < 2; j++)
985194
+    {
985194
+      for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
985194
+	{
985194
+	  int fileret;
985194
+	  printf ("\tftruncate: %s (file, \"%s\"): ",
985194
+		  j == 0 ? "fopen" : "fdopen",
985194
+		  test_modes[i].mode);
985194
+
985194
+	  if (j == 0)
985194
+	    fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
985194
+	  else
985194
+	    fileret = get_handles_fdopen (filename, fd, fp,
985194
+					  test_modes[i].fd_mode,
985194
+					  test_modes[i].mode);
985194
+
985194
+	  if (fileret != 0)
985194
+	    return fileret;
985194
+
985194
+	  /* Write some data.  */
985194
+	  size_t written = fputs_func (data, fp);
985194
+
985194
+	  if (written == EOF)
985194
+	    {
985194
+	      printf ("fputs[1] failed to write data\n");
985194
+	      ret |= 1;
985194
+	    }
985194
+
985194
+	  /* Record the offset.  */
985194
+	  long offset = ftell (fp);
985194
+
985194
+	  /* Flush data to allow switching active handles.  */
985194
+	  if (fflush (fp))
985194
+	    {
985194
+	      printf ("Flush failed: %m\n");
985194
+	      ret |= 1;
985194
+	    }
985194
+
985194
+	  /* Now truncate the file.  */
985194
+	  if (ftruncate (fd, 0) != 0)
985194
+	    {
985194
+	      printf ("Failed to truncate file: %m\n");
985194
+	      ret |= 1;
985194
+	    }
985194
+
985194
+	  /* ftruncate does not change the offset, so there is no need to call
985194
+	     anything to be able to switch active handles.  */
985194
+	  long new_offset = ftell (fp);
985194
+
985194
+	  /* The offset should remain unchanged since ftruncate does not update
985194
+	     it.  */
985194
+	  if (offset != new_offset)
985194
+	    {
985194
+	      printf ("Incorrect offset.  Expected %zu, but got %ld\n",
985194
+		      offset, new_offset);
985194
+
985194
+	      ret |= 1;
985194
+	    }
985194
+	  else
985194
+	    printf ("offset = %ld\n", offset);
985194
+
985194
+	  fclose (fp);
985194
+	}
985194
+    }
985194
+
985194
+  return ret;
985194
+}
985194
 /* Test that ftell output after a rewind is correct.  */
985194
 static int
985194
 do_rewind_test (const char *filename)
985194
@@ -481,6 +570,7 @@ do_one_test (const char *filename)
985194
   ret |= do_write_test (filename);
985194
   ret |= do_append_test (filename);
985194
   ret |= do_rewind_test (filename);
985194
+  ret |= do_ftruncate_test (filename);
985194
 
985194
   return ret;
985194
 }
12745e
diff --git glibc-2.17-c758a686/libio/wfileops.c glibc-2.17-c758a686/libio/wfileops.c
985194
index 6a088b1..71281c1 100644
12745e
--- glibc-2.17-c758a686/libio/wfileops.c
12745e
+++ glibc-2.17-c758a686/libio/wfileops.c
985194
@@ -626,16 +626,15 @@ do_ftell_wide (_IO_FILE *fp)
985194
       const wchar_t *wide_read_base;
985194
       const wchar_t *wide_read_ptr;
985194
       const wchar_t *wide_read_end;
985194
-      bool was_writing = ((fp->_wide_data->_IO_write_ptr
985194
-			   > fp->_wide_data->_IO_write_base)
985194
-			  || _IO_in_put_mode (fp));
985194
+      bool unflushed_writes = (fp->_wide_data->_IO_write_ptr
985194
+			       > fp->_wide_data->_IO_write_base);
985194
 
985194
       bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
985194
 
985194
       /* When we have unflushed writes in append mode, seek to the end of the
985194
 	 file and record that offset.  This is the only time we change the file
985194
 	 stream state and it is safe since the file handle is active.  */
985194
-      if (was_writing && append_mode)
985194
+      if (unflushed_writes && append_mode)
985194
 	{
985194
 	  result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
985194
 	  if (result == _IO_pos_BAD)
985194
@@ -674,7 +673,7 @@ do_ftell_wide (_IO_FILE *fp)
985194
       struct _IO_codecvt *cv = fp->_codecvt;
985194
       int clen = (*cv->__codecvt_do_encoding) (cv);
985194
 
985194
-      if (!was_writing)
985194
+      if (!unflushed_writes)
985194
 	{
985194
 	  if (clen > 0)
985194
 	    {