]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Fix file descriptor position after fclose
authorUlrich Drepper <drepper@gmail.com>
Sat, 14 May 2011 01:08:45 +0000 (02:08 +0100)
committerPetr Baudis <pasky@ucw.cz>
Thu, 26 May 2011 23:28:47 +0000 (01:28 +0200)
fclose should leave the file descriptor position after the last read
or written byte.

(cherry picked from commit fcabc0f8b185f9e0a9289720be5ede6c39b3bf21)

ChangeLog
libio/Makefile
libio/bug-fclose1.c [new file with mode: 0644]
libio/fileops.c

index 50772691bc3be1cb2df0450fa87e10ae30c58fa1..221f8a91188b90a7499172671b3526b3236cf5a7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-05-13  Ulrich Drepper  <drepper@gmail.com>
+
+       [BZ #12724]
+       * libio/fileops.c (_IO_new_file_close_it): Always flush when
+       currently writing and seek to current position when not.
+       * libio/Makefile (tests): Add bug-fclose1.
+       * libio/bug-fclose1.c: New file.
+
 2011-05-12  Ulrich Drepper  <drepper@gmail.com>
 
        [BZ #12511]
index 83b9458dc2b2360bb8ef7d14d71fa1da2cb09523..ec309048411e0394c7534d3c05c4d296a1f4a29b 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 1995-2004,2006,2007,2008,2009 Free Software Foundation, Inc.
+# Copyright (C) 1995-2004,2006-2009,2011 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -58,7 +58,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
        tst-memstream1 tst-memstream2 \
        tst-wmemstream1 tst-wmemstream2 \
        bug-memstream1 bug-wmemstream1 \
-       tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos
+       tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos bug-fclose1
 test-srcs = test-freopen
 
 all: # Make this the default target; it will be defined in Rules.
diff --git a/libio/bug-fclose1.c b/libio/bug-fclose1.c
new file mode 100644 (file)
index 0000000..f1e09f5
--- /dev/null
@@ -0,0 +1,132 @@
+// BZ #12724
+
+static void do_prepare (void);
+#define PREPARE(argc, argv) do_prepare ()
+static int do_test (void);
+#define TEST_FUNCTION do_test()
+#include "../test-skeleton.c"
+
+
+static int fd;
+
+
+static void
+do_prepare (void)
+{
+  fd = create_temp_file ("bug-fclose1.", NULL);
+  if (fd == -1)
+    {
+      printf ("cannot create temporary file: %m\n");
+      exit (1);
+    }
+}
+
+
+static int
+do_test (void)
+{
+  static const char pattern[] = "hello world";
+
+  /* Prepare a seekable file.  */
+  if (write (fd, pattern, sizeof pattern) != sizeof pattern)
+    {
+      printf ("cannot write pattern: %m\n");
+      return 1;
+    }
+  if (lseek (fd, 1, SEEK_SET) != 1)
+    {
+      printf ("cannot seek after write: %m\n");
+      return 1;
+    }
+
+  /* Create an output stream visiting the file; when it is closed, all
+     other file descriptors visiting the file must see the new file
+     position.  */
+  int fd2 = dup (fd);
+  if (fd2 < 0)
+    {
+      printf ("cannot duplicate descriptor for writing: %m\n");
+      return 1;
+    }
+  FILE *f = fdopen (fd2, "w");
+  if (f == NULL)
+    {
+      printf ("first fdopen failed: %m\n");
+      return 1;
+    }
+  if (fputc (pattern[1], f) != pattern[1])
+    {
+      printf ("fputc failed: %m\n");
+      return 1;
+    }
+  if (fclose (f) != 0)
+    {
+      printf ("first fclose failed: %m\n");
+      return 1;
+    }
+  errno = 0;
+  if (lseek (fd2, 0, SEEK_CUR) != -1)
+    {
+      printf ("lseek after fclose after write did not fail\n");
+      return 1;
+    }
+  if (errno != EBADF)
+    {
+      printf ("lseek after fclose after write did not fail with EBADF: %m\n");
+      return 1;
+    }
+  off_t o = lseek (fd, 0, SEEK_CUR);
+  if (o != 2)
+    {
+      printf ("\
+lseek on original descriptor after first fclose returned %ld, expected 2\n",
+             (long int) o);
+      return 1;
+    }
+
+  /* Likewise for an input stream.  */
+  fd2 = dup (fd);
+  if (fd2 < 0)
+     {
+      printf ("cannot duplicate descriptor for reading: %m\n");
+      return 1;
+    }
+  f = fdopen (fd2, "r");
+   if (f == NULL)
+    {
+      printf ("second fdopen failed: %m\n");
+      return 1;
+    }
+   char c = fgetc (f);
+   if (c != pattern[2])
+     {
+       printf ("getc returned %c, expected %c\n", c, pattern[2]);
+       return 1;
+     }
+  if (fclose (f) != 0)
+    {
+      printf ("second fclose failed: %m\n");
+      return 1;
+    }
+  errno = 0;
+  if (lseek (fd2, 0, SEEK_CUR) != -1)
+    {
+      printf ("lseek after fclose after read did not fail\n");
+      return 1;
+    }
+  if (errno != EBADF)
+    {
+      printf ("lseek after fclose after read did not fail with EBADF: %m\n");
+      return 1;
+    }
+  o = lseek (fd, 0, SEEK_CUR);
+  if (o != 3)
+    {
+      printf ("\
+lseek on original descriptor after second fclose returned %ld, expected 3\n",
+             (long int) o);
+      return 1;
+    }
+
+  return 0;
+}
index 1e7ef0754cd5d5cf0b046d825c5416ea91627910..adf4cfaea953d8c69203f1241e4fb6c295cc9b93 100644 (file)
@@ -160,19 +160,25 @@ int
 _IO_new_file_close_it (fp)
      _IO_FILE *fp;
 {
-  int write_status, close_status;
   if (!_IO_file_is_open (fp))
     return EOF;
 
-  if ((fp->_flags & _IO_NO_WRITES) == 0
-      && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
+  int write_status;
+  if (_IO_in_put_mode (fp))
     write_status = _IO_do_flush (fp);
-  else
-    write_status = 0;
+  else if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
+          && !_IO_in_backup (fp))
+    {
+      off64_t o = _IO_SEEKOFF (fp, 0, _IO_seek_cur, 0);
+      if (o == WEOF)
+       write_status = EOF;
+      else
+       write_status = _IO_SYSSEEK (fp, o, SEEK_SET) < 0 ? EOF : 0;
+    }
 
   INTUSE(_IO_unsave_markers) (fp);
 
-  close_status = _IO_SYSCLOSE (fp);
+  int close_status = _IO_SYSCLOSE (fp);
 
   /* Free buffer. */
 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T