diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2016-11-26 14:53:29 -0800 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2016-11-26 14:54:38 -0800 |
commit | b947d0524d64b5a139282fd48caa7a866e20513c (patch) | |
tree | 4719c46b1cefe2f15e1f1fd23fac8d09c091f112 /lib/freopen.c | |
parent | 0a2d091427da29e9d956b108801cdfdc27713a09 (diff) | |
download | gnulib-b947d0524d64b5a139282fd48caa7a866e20513c.tar.gz |
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.
Diffstat (limited to 'lib/freopen.c')
-rw-r--r-- | lib/freopen.c | 44 |
1 files changed, 34 insertions, 10 deletions
diff --git a/lib/freopen.c b/lib/freopen.c index 4cf7528e67..6855214adc 100644 --- a/lib/freopen.c +++ b/lib/freopen.c @@ -19,12 +19,12 @@ /* 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; } |