diff --git a/ChangeLog b/ChangeLog index 26eb3d5..a7e03f2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2016-11-26 Paul Eggert + + 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 , 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 fnmatch: fix typo introduced on 2016-08-17 diff --git a/doc/posix-functions/freopen.texi b/doc/posix-functions/freopen.texi index d14c3e1..f79a05a 100644 --- a/doc/posix-functions/freopen.texi +++ b/doc/posix-functions/freopen.texi @@ -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. address@hidden 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 diff --git a/lib/freopen.c b/lib/freopen.c index 4cf7528..229c1d9 100644 --- a/lib/freopen.c +++ b/lib/freopen.c @@ -19,12 +19,12 @@ /* If the user's config.h happens to include , let it include only the system's here, so that orig_freopen doesn't recurse to rpl_freopen. */ -#define __need_FILE +#define _GL_ALREADY_INCLUDING_STDIO_H #include /* Get the original definition of freopen. It might be defined as a macro. */ #include -#undef __need_FILE +#undef _GL_ALREADY_INCLUDING_STDIO_H #include @@ -39,29 +39,54 @@ orig_freopen (const char *filename, const char *mode, FILE *stream) this include because of the preliminary #include above. */ #include "stdio.h" +#include #include +#include 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; } diff --git a/m4/freopen.m4 b/m4/freopen.m4 index 8d8e124..727eb08 100644 --- a/m4/freopen.m4 +++ b/m4/freopen.m4 @@ -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 + #include + ]], + [[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 ]) diff --git a/modules/freopen b/modules/freopen index 958cdbb..adb4bbc 100644 --- a/modules/freopen +++ b/modules/freopen @@ -6,6 +6,7 @@ lib/freopen.c m4/freopen.m4 Depends-on: +fcntl-h [test $REPLACE_FREOPEN = 1] stdio largefile diff --git a/tests/test-freopen.c b/tests/test-freopen.c index 80453bf..849d7d1 100644 --- a/tests/test-freopen.c +++ b/tests/test-freopen.c @@ -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