olga / rpms / glibc

Forked from rpms/glibc 5 years ago
Clone

Blame SOURCES/glibc-rh1156331.patch

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