Change offset in fdopen only if setting O_APPEND
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Mon, 17 Mar 2014 13:12:53 +0000 (18:42 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Mon, 17 Mar 2014 15:54:02 +0000 (21:24 +0530)
fdopen should only be allowed to change the offset in the file it
attaches to if it is setting O_APPEND.  If O_APPEND is already set, it
should not change the state of the handle.

ChangeLog
libio/iofdopen.c
libio/tst-ftell-active-handler.c

index eea2ef6..da6a230 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2014-03-17  Siddhesh Poyarekar  <siddhesh@redhat.com>
 
+       * libio/iofdopen.c (_IO_new_fdopen): Seek to end only if
+       setting O_APPEND.
+       * libio/tst-ftell-active-handler.c (do_append_test): Add a
+       test case.
+
        [BZ #16680]
        * libio/fileops.c (_IO_file_open): Seek to end of file but
        don't cache the offset.
index 843a4fa..b36d21d 100644 (file)
@@ -59,6 +59,11 @@ _IO_new_fdopen (fd, mode)
   int i;
   int use_mmap = 0;
 
+  /* Decide whether we modify the offset of the file we attach to and seek to
+     the end of file.  We only do this if the mode is 'a' and if the file
+     descriptor did not have O_APPEND in its flags already.  */
+  bool do_seek = false;
+
   switch (*mode)
     {
     case 'r':
@@ -128,6 +133,7 @@ _IO_new_fdopen (fd, mode)
      */
   if ((posix_mode & O_APPEND) && !(fd_flags & O_APPEND))
     {
+      do_seek = true;
 #ifdef F_SETFL
       if (_IO_fcntl (fd, F_SETFL, fd_flags | O_APPEND) == -1)
 #endif
@@ -167,10 +173,11 @@ _IO_new_fdopen (fd, mode)
   _IO_mask_flags (&new_f->fp.file, read_write,
                  _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
 
-  /* For append mode, set the file offset to the end of the file.  Don't
-     update the offset cache though, since the file handle is not active.  */
-  if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
-      == (_IO_IS_APPENDING | _IO_NO_READS))
+  /* For append mode, set the file offset to the end of the file if we added
+     O_APPEND to the file descriptor flags.  Don't update the offset cache
+     though, since the file handle is not active.  */
+  if (do_seek && ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
+                 == (_IO_IS_APPENDING | _IO_NO_READS)))
     {
       _IO_off64_t new_pos = _IO_SYSSEEK (&new_f->fp.file, 0, _IO_seek_end);
       if (new_pos == _IO_pos_BAD && errno != ESPIPE)
index 40ca58c..e9dc7b3 100644 (file)
@@ -414,6 +414,61 @@ do_append_test (const char *filename)
        }
     }
 
+  /* For fdopen in 'a' mode, the file descriptor should not change if the file
+     is already open with the O_APPEND flag set.  */
+  fd = open (filename, O_WRONLY | O_APPEND, 0);
+  if (fd == -1)
+    {
+      printf ("open(O_APPEND) failed: %m\n");
+      return 1;
+    }
+
+  off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
+  if (seek_ret == -1)
+    {
+      printf ("lseek[O_APPEND][0] failed: %m\n");
+      ret |= 1;
+    }
+
+  fp = fdopen (fd, "a");
+  if (fp == NULL)
+    {
+      printf ("fdopen(O_APPEND) failed: %m\n");
+      close (fd);
+      return 1;
+    }
+
+  off_t new_seek_ret = lseek (fd, 0, SEEK_CUR);
+  if (seek_ret == -1)
+    {
+      printf ("lseek[O_APPEND][1] failed: %m\n");
+      ret |= 1;
+    }
+
+  printf ("\tappend: fdopen (file, \"a\"): O_APPEND: ");
+
+  if (seek_ret != new_seek_ret)
+    {
+      printf ("incorrectly modified file offset to %ld, should be %ld",
+             new_seek_ret, seek_ret);
+      ret |= 1;
+    }
+  else
+    printf ("retained current file offset %ld", seek_ret);
+
+  new_seek_ret = ftello (fp);
+
+  if (seek_ret != new_seek_ret)
+    {
+      printf (", ftello reported incorrect offset %ld, should be %ld\n",
+             new_seek_ret, seek_ret);
+      ret |= 1;
+    }
+  else
+    printf (", ftello reported correct offset %ld\n", seek_ret);
+
+  fclose (fp);
+
   return ret;
 }