]> Savannah Git Hosting - gnulib.git/commitdiff
freopen: work around glibc bug with closed fd
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 26 Nov 2016 22:53:29 +0000 (14:53 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 26 Nov 2016 22:54:38 +0000 (14:54 -0800)
Work around glibc bug#15589, where freopen mishandles the case
where stdin etc. are already closed.
* doc/posix-functions/freopen.texi (freopen): Document the bug.
* lib/freopen.c (_GL_ALREADY_INCLUDING_STDIO_H): Define this
instead of __need_FILE, as the latter does not work with glibc.
Include <fcntl.h>, for open flags.
(rpl_freopen): Work around glibc bug.
* m4/freopen.m4 (gl_FUNC_FREOPEN): Check for bug.
* modules/freopen (Depends-on): Add fcntl-h.
* tests/test-freopen.c (main): Test for bug.

ChangeLog
doc/posix-functions/freopen.texi
lib/freopen.c
m4/freopen.m4
modules/freopen
tests/test-freopen.c

index 26eb3d5da2d56b3afc5c347a222486c24a8080f3..a7e03f227f8e9de32fec79c07648391e9f4e02bf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2016-11-26  Paul Eggert  <eggert@cs.ucla.edu>
+
+       freopen: work around glibc bug with closed fd
+       Work around glibc bug#15589, where freopen mishandles the case
+       where stdin etc. are already closed.
+       * doc/posix-functions/freopen.texi (freopen): Document the bug.
+       * lib/freopen.c (_GL_ALREADY_INCLUDING_STDIO_H): Define this
+       instead of __need_FILE, as the latter does not work with glibc.
+       Include <fcntl.h>, for open flags.
+       (rpl_freopen): Work around glibc bug.
+       * m4/freopen.m4 (gl_FUNC_FREOPEN): Check for bug.
+       * modules/freopen (Depends-on): Add fcntl-h.
+       * tests/test-freopen.c (main): Test for bug.
+
 2016-11-25  Paul Eggert  <eggert@cs.ucla.edu>
 
        fnmatch: fix typo introduced on 2016-08-17
index d14c3e1e0501949240208e48a75136539c49b9d3..f79a05aefc4d97c32bf34192eafec616b9bc62ac 100644 (file)
@@ -9,6 +9,10 @@ Gnulib module: freopen
 Portability problems fixed by Gnulib:
 @itemize
 @item
+On some platforms, if @code{stream} does not already have an open
+file descriptor, @code{freopen} returns the stream without opening
+the file: glibc 2.24.
+@item
 On platforms where @code{off_t} is a 32-bit type, @code{freopen} may not work
 correctly with files larger than 2 GB.  (Cf. @code{AC_SYS_LARGEFILE}.)
 @item
index 4cf7528e6794a68ce400f6e02e6c5477730fbd05..6855214adc0f23dc97c9cc0e1e12b109a737ec71 100644 (file)
 /* If the user's config.h happens to include <stdio.h>, let it include only
    the system's <stdio.h> here, so that orig_freopen doesn't recurse to
    rpl_freopen.  */
-#define __need_FILE
+#define _GL_ALREADY_INCLUDING_STDIO_H
 #include <config.h>
 
 /* Get the original definition of freopen.  It might be defined as a macro.  */
 #include <stdio.h>
-#undef __need_FILE
+#undef _GL_ALREADY_INCLUDING_STDIO_H
 
 #include <errno.h>
 
@@ -39,29 +39,53 @@ orig_freopen (const char *filename, const char *mode, FILE *stream)
    this include because of the preliminary #include <stdio.h> above.  */
 #include "stdio.h"
 
+#include <fcntl.h>
 #include <string.h>
 
 FILE *
 rpl_freopen (const char *filename, const char *mode, FILE *stream)
 {
   FILE *result;
-
 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
-  if (filename != NULL && strcmp (filename, "/dev/null") == 0)
-    filename = "NUL";
+  char const *null_device = "NUL";
+  if (filename && strcmp (filename, "/dev/null") == 0)
+    filename = null_device;
+#else
+  char const *null_device = "/dev/null";
 #endif
 
-  /* Clear errno to check the success of freopen() with it */
+#ifdef __KLIBC__
   errno = 0;
+#endif
 
   result = orig_freopen (filename, mode, stream);
 
+  if (!result)
+    {
 #ifdef __KLIBC__
-  /* On OS/2 kLIBC, freopen() returns NULL even if it is successful
-     if filename is NULL. */
-  if (!filename && !result && !errno)
-    result = stream;
+      /* On OS/2 kLIBC, freopen returns NULL even if it is successful
+         if filename is NULL. */
+      if (!filename && !errno)
+        result = stream;
 #endif
+    }
+  else if (filename)
+    {
+      int fd = fileno (result);
+      if (dup2 (fd, fd) < 0 && errno == EBADF)
+        {
+          int nullfd = open (null_device, O_RDONLY | O_CLOEXEC);
+          int err = 0;
+          if (nullfd != fd)
+            {
+              if (dup2 (nullfd, fd) < 0)
+                err = 1;
+              close (nullfd);
+            }
+          if (!err)
+            result = orig_freopen (filename, mode, result);
+        }
+    }
 
   return result;
 }
index 8d8e124a7d6a6ebd69dc8e5d295cbe3b37dc4062..727eb086a1bae77bbe55af85e2362f408ce2971a 100644 (file)
@@ -1,4 +1,4 @@
-# freopen.m4 serial 5
+# freopen.m4 serial 6
 dnl Copyright (C) 2007-2016 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -12,6 +12,27 @@ AC_DEFUN([gl_FUNC_FREOPEN],
     mingw* | pw* | os2*)
       REPLACE_FREOPEN=1
       ;;
+    *)
+      AC_CACHE_CHECK([whether freopen works on closed fds],
+        [gl_cv_func_freopen_works_on_closed],
+        [AC_RUN_IFELSE(
+           [AC_LANG_PROGRAM(
+              [[#include <stdio.h>
+                #include <unistd.h>
+              ]],
+              [[close (0);
+                return !(freopen ("/dev/null", "r", stdin)
+                         && getchar () == EOF
+                         && !ferror (stdin) && feof (stdin));]])],
+           [gl_cv_func_freopen_works_on_closed=yes],
+           [gl_cv_func_freopen_works_on_closed=no],
+           [case $host_os in
+              *gnu*) gl_cv_func_freopen_works_on_closed="guessing no" ;;
+              *)     gl_cv_func_freopen_works_on_closed="guessing yes";;
+            esac])])
+      case $gl_cv_func_freopen_works_on_closed in
+        *no) REPLACE_FREOPEN=1;;
+      esac
   esac
 ])
 
index 958cdbb279e70099c0d471966d3a1be28e3d08b9..adb4bbcbc571543a7b75de56e2be654628684877 100644 (file)
@@ -6,6 +6,7 @@ lib/freopen.c
 m4/freopen.m4
 
 Depends-on:
+fcntl-h        [test $REPLACE_FREOPEN = 1]
 stdio
 largefile
 
index 80453bfd5051debf3b657cbae3a3591197111f3d..849d7d1214e90f1bcb2a6c564602dfe9c7665f4e 100644 (file)
@@ -33,7 +33,11 @@ main ()
 {
   const char *filename = "test-freopen.txt";
 
+  close (STDIN_FILENO);
   ASSERT (freopen ("/dev/null", "r", stdin) != NULL);
+  ASSERT (getchar () == EOF);
+  ASSERT (!ferror (stdin));
+  ASSERT (feof (stdin));
 
 #if 0 /* freopen (NULL, ...) is unsupported on most platforms.  */
   /* Test that freopen() sets errno if someone else closes the stream