aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Myers <josmyers@redhat.com>2024-09-04 16:32:21 +0000
committerJoseph Myers <josmyers@redhat.com>2024-09-04 16:32:21 +0000
commited4bb289cf739f537deb735eaa01be531df084b9 (patch)
tree4f72104640ca6f2e3edb79b676401fe2749233d4
parentae4d44b1d501421ad9a3af95279b8f4d1546f1ce (diff)
downloadglibc-ed4bb289cf739f537deb735eaa01be531df084b9.zip
glibc-ed4bb289cf739f537deb735eaa01be531df084b9.tar.gz
glibc-ed4bb289cf739f537deb735eaa01be531df084b9.tar.bz2
Add more thorough tests of freopen
freopen is rather minimally tested in libio/tst-freopen and libio/test-freopen. Add some more thorough tests, covering different cases for change of mode in particular. The tests are run for both freopen and freopen64 (given that those functions have two separate copies of much of the code, so any bug fix directly in the freopen code would probably need applying in both places). Note that there are two parts of the tests disabled because of bugs discovered through running the tests, with bug numbers given in comments. I expect to address those separately. The tests also don't cover changes to cancellation ("c" in mode); I think that will better be handled through a separate test. Also to handle separately: testing on stdin / stdout / stderr; documenting lack of support for streams opened with popen / fmemopen / open_memstream / fopencookie; maybe also a chroot test without /proc; maybe also more thorough tests for large file handling on 32-bit systems (freopen64). Tested for x86_64.
-rw-r--r--stdio-common/Makefile16
-rw-r--r--stdio-common/tst-freopen2-main.c526
-rw-r--r--stdio-common/tst-freopen2.c3
-rw-r--r--stdio-common/tst-freopen3-main.c90
-rw-r--r--stdio-common/tst-freopen3.c2
-rw-r--r--stdio-common/tst-freopen64-2.c3
-rw-r--r--stdio-common/tst-freopen64-3.c2
7 files changed, 642 insertions, 0 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 948d960..89871d0 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -216,6 +216,10 @@ tests := \
tst-fmemopen4 \
tst-fphex \
tst-fphex-wide \
+ tst-freopen2 \
+ tst-freopen3 \
+ tst-freopen64-2 \
+ tst-freopen64-3 \
tst-fseek \
tst-fwrite \
tst-getline \
@@ -315,6 +319,8 @@ tests-special += \
ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += \
+ $(objpfx)tst-freopen2-mem.out \
+ $(objpfx)tst-freopen64-2-mem.out \
$(objpfx)tst-getline-enomem-mem.out \
$(objpfx)tst-getline-mem.out \
$(objpfx)tst-printf-bz18872-mem.out \
@@ -326,6 +332,10 @@ tests-special += \
# tests-special
generated += \
+ tst-freopen2-mem.out \
+ tst-freopen2.mtrace \
+ tst-freopen64-2-mem.out \
+ tst-freopen64-2.mtrace \
tst-getline-enomem-mem.out \
tst-getline-enomem.mtrace \
tst-getline-mem.out \
@@ -445,6 +455,12 @@ tst-getline-ENV = \
tst-getline-enomem-ENV = \
MALLOC_TRACE=$(objpfx)tst-getline-enomem.mtrace \
LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen2-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen2.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen64-2-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen64-2.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
diff --git a/stdio-common/tst-freopen2-main.c b/stdio-common/tst-freopen2-main.c
new file mode 100644
index 0000000..22b21af
--- /dev/null
+++ b/stdio-common/tst-freopen2-main.c
@@ -0,0 +1,526 @@
+/* Test freopen.
+ Copyright (C) 2024 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
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+#define START_TEST(DESC) \
+ do \
+ { \
+ fds = support_descriptors_list (); \
+ verbose_printf (DESC); \
+ } \
+ while (0)
+
+#define END_TEST \
+ do \
+ { \
+ support_descriptors_check (fds); \
+ support_descriptors_free (fds); \
+ } \
+ while (0)
+
+int
+do_test (void)
+{
+ mtrace ();
+ struct support_descriptors *fds;
+ char *temp_dir = support_create_temp_directory ("tst-freopen2");
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ char *file2 = xasprintf ("%s/file2", temp_dir);
+ support_write_file_string (file2, "file2");
+ add_temp_file (file2);
+ char *file3 = xasprintf ("%s/file3", temp_dir);
+ char *file4 = xasprintf ("%s/file4", temp_dir);
+ char *file1a = xasprintf ("%s/file1a", temp_dir);
+ FILE *fp;
+ int ret;
+ wint_t wc;
+
+ /* Test each pair of old and new modes from r w a. */
+
+ START_TEST ("Testing r -> r\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file2");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> w\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("File2new", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file1, "file1");
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new");
+ END_TEST;
+
+ START_TEST ("Testing r -> a\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("3", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new3");
+ END_TEST;
+
+ START_TEST ("Testing w -> r\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "File2new3");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing w -> w\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("next", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file1, "");
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next");
+ END_TEST;
+
+ START_TEST ("Testing w -> a\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("4", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next4");
+ END_TEST;
+
+ START_TEST ("Testing a -> r\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "next4");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing a -> w\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("another", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another");
+ END_TEST;
+
+ START_TEST ("Testing a -> a\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("5", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another5");
+ END_TEST;
+
+ /* Test for file originally opened with fopen64. */
+ START_TEST ("Testing fopen64 a -> a\n");
+ fp = fopen64 (file1, "a");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("64", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another564");
+ END_TEST;
+
+ /* Test calling freopen more than once on the same FILE *. */
+
+ START_TEST ("Testing r -> w -> r\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("freopen-twice", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice");
+ END_TEST;
+
+ START_TEST ("Testing r -> w -> r (exactly one freopen64)\n");
+ fp = xfopen (file1, "r");
+ fp = OTHER_FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("freopen-twice64", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice64");
+ END_TEST;
+
+ /* Test changing to/from b (binary, no-op). */
+
+ START_TEST ("Testing rb -> r\n");
+ fp = xfopen (file1, "rb");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> rb\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "rb", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from + (read-and-write). */
+
+ START_TEST ("Testing r -> w+\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("latest", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_FILE_STRING (fp, "latest");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latest");
+ END_TEST;
+
+ START_TEST ("Testing w -> a+\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "a+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("suffix", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_FILE_STRING (fp, "latestsuffix");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latestsuffix");
+ END_TEST;
+
+ START_TEST ("Testing a -> r+\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "latestsuffix");
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ ret = fputs ("new", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "newestsuffix");
+ END_TEST;
+
+ START_TEST ("Testing r+ -> w\n");
+ fp = xfopen (file1, "r+");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("plusto", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ TEST_COMPARE (fgetc (fp), EOF);
+ TEST_COMPARE (errno, EBADF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plusto");
+ END_TEST;
+
+ START_TEST ("Testing w+ -> a\n");
+ fp = xfopen (file1, "w+");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("more", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ TEST_COMPARE (fgetc (fp), EOF);
+ TEST_COMPARE (errno, EBADF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore");
+ END_TEST;
+
+ START_TEST ("Testing a+ -> r\n");
+ fp = xfopen (file1, "a+");
+ fp = FREOPEN (file2, "rr", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ ret = fputs ("2", fp);
+ TEST_COMPARE (ret, EOF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore");
+ END_TEST;
+
+ /* Test changing to/from e (FD_CLOEXEC). */
+
+ START_TEST ("Testing re -> r\n");
+ fp = xfopen (file1, "re");
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+#if 0 /* Fails to clear FD_CLOEXEC (bug 32134). */
+ TEST_COMPARE (ret & FD_CLOEXEC, 0);
+#endif
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> re\n");
+ fp = xfopen (file1, "r");
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, 0);
+ fp = FREOPEN (file2, "re", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from m (mmap) (a no-op as far as testing
+ semantics is concerned). */
+
+ START_TEST ("Testing rm -> r\n");
+ fp = xfopen (file1, "rm");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> rm\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "rm", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from x (O_EXCL). */
+
+ START_TEST ("Testing wx -> w\n");
+ fp = xfopen (file3, "wx");
+ add_temp_file (file3);
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("wxtow", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "wxtow");
+ END_TEST;
+
+ START_TEST ("Testing w -> wx (file does not exist)\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file4, "wx", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ add_temp_file (file4);
+ ret = fputs ("wtowx", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file4, "wtowx");
+ END_TEST;
+
+ /* Test with ,ccs=CHARSET. */
+
+ START_TEST ("testing w,ccs=utf-8 -> r\n");
+ fp = xfopen (file1, "w,ccs=utf-8");
+ ret = fputws (L"\xc0\xc1", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "wxtow");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing w,ccs=iso-8859-1 -> r,ccs=utf-8\n");
+ fp = xfopen (file2, "w,ccs=iso-8859-1");
+ ret = fputws (L"\xc0\xc1", fp);
+ TEST_VERIFY (ret >= 0);
+#if 0 /* Doesn't work (bug 23675). */
+ fp = FREOPEN (file1, "r,ccs=utf-8", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+#else /* Works instead. */
+ xfclose (fp);
+ fp = xfopen (file1, "r,ccs=utf-8");
+#endif
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, (wint_t) 0xc0);
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, (wint_t) 0xc1);
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, WEOF);
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing r,ccs=utf-8 -> r\n");
+ fp = xfopen (file1, "r,ccs=utf-8");
+ fp = FREOPEN (file1, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test that errors closing the old file are ignored. */
+
+ START_TEST ("testing errors closing old file ignored\n");
+ fp = xfopen ("/dev/full", "w");
+ fputc ('x', fp);
+ fp = FREOPEN (file1, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test that error / EOF state from the old file are cleared. */
+
+ START_TEST ("testing error state from old file cleared\n");
+ fp = xfopen ("/dev/full", "w");
+ fputc ('x', fp);
+ fflush (fp);
+ TEST_VERIFY (ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing EOF state from old file cleared\n");
+ fp = xfopen ("/dev/null", "r");
+ fgetc (fp);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (feof (fp));
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, same mode (should flush content and reset
+ file offset). */
+
+ START_TEST ("testing freopen with NULL, same mode\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("same mode", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "same mode");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, different mode. */
+
+ START_TEST ("testing freopen with NULL, different mode\n");
+ fp = xfopen (file1, "w");
+ ret = fputs ("different mode", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (NULL, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "different mode");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, renamed file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened. */
+
+ START_TEST ("testing freopen with NULL, renamed file\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("file has been renamed", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = rename (file1, file1a);
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file has been renamed");
+ xfclose (fp);
+ ret = rename (file1a, file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ /* Test freopen with NULL, deleted file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened. */
+
+ START_TEST ("testing freopen with NULL, deleted file\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("file has now been deleted", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file has now been deleted");
+ xfclose (fp);
+ /* Recreate the file so it is present when expected for temporary
+ file deletion. */
+ support_write_file_string (file1, "file1");
+ END_TEST;
+
+ free (temp_dir);
+ free (file1);
+ free (file2);
+ free (file3);
+ free (file4);
+ free (file1a);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen2.c b/stdio-common/tst-freopen2.c
new file mode 100644
index 0000000..11ec7a9
--- /dev/null
+++ b/stdio-common/tst-freopen2.c
@@ -0,0 +1,3 @@
+#define FREOPEN freopen
+#define OTHER_FREOPEN freopen64
+#include <tst-freopen2-main.c>
diff --git a/stdio-common/tst-freopen3-main.c b/stdio-common/tst-freopen3-main.c
new file mode 100644
index 0000000..5107e1f
--- /dev/null
+++ b/stdio-common/tst-freopen3-main.c
@@ -0,0 +1,90 @@
+/* Test freopen failure.
+ Copyright (C) 2024 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
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+#define START_TEST(DESC) \
+ do \
+ { \
+ fds = support_descriptors_list (); \
+ verbose_printf (DESC); \
+ } \
+ while (0)
+
+#define END_TEST \
+ do \
+ { \
+ support_descriptors_check (fds); \
+ support_descriptors_free (fds); \
+ } \
+ while (0)
+
+int
+do_test (void)
+{
+ struct support_descriptors *fds;
+ char *temp_dir = support_create_temp_directory ("tst-freopen3");
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ char *file2 = xasprintf ("%s/file2", temp_dir);
+ support_write_file_string (file2, "file2");
+ add_temp_file (file2);
+ char *file_nodir = xasprintf ("%s/nodir/file", temp_dir);
+ FILE *fp;
+ int ret;
+ int fd;
+
+ START_TEST ("Testing w -> wx (file exists)\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "wx", fp);
+ TEST_VERIFY (fp == NULL);
+ END_TEST;
+
+ /* Test old file is closed even when opening the new file fails. */
+
+ START_TEST ("testing r -> r (opening new file fails)\n");
+ fp = xfopen (file1, "r");
+ fd = fileno (fp);
+ fp = FREOPEN (file_nodir, "r", fp);
+ TEST_VERIFY (fp == NULL);
+ errno = 0;
+ ret = fcntl (fd, F_GETFL);
+ TEST_COMPARE (ret, -1);
+ TEST_COMPARE (errno, EBADF);
+ END_TEST;
+
+ free (temp_dir);
+ free (file1);
+ free (file2);
+ free (file_nodir);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen3.c b/stdio-common/tst-freopen3.c
new file mode 100644
index 0000000..5041b6b
--- /dev/null
+++ b/stdio-common/tst-freopen3.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen
+#include <tst-freopen3-main.c>
diff --git a/stdio-common/tst-freopen64-2.c b/stdio-common/tst-freopen64-2.c
new file mode 100644
index 0000000..88fdc64
--- /dev/null
+++ b/stdio-common/tst-freopen64-2.c
@@ -0,0 +1,3 @@
+#define FREOPEN freopen64
+#define OTHER_FREOPEN freopen
+#include <tst-freopen2-main.c>
diff --git a/stdio-common/tst-freopen64-3.c b/stdio-common/tst-freopen64-3.c
new file mode 100644
index 0000000..b91b6d2
--- /dev/null
+++ b/stdio-common/tst-freopen64-3.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen64
+#include <tst-freopen3-main.c>