+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
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
/* 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>
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;
}
-# 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,
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
])
m4/freopen.m4
Depends-on:
+fcntl-h [test $REPLACE_FREOPEN = 1]
stdio
largefile
{
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