diff --git a/glibc-RHEL-46737-1.patch b/glibc-RHEL-46737-1.patch new file mode 100644 index 0000000..8281a0e --- /dev/null +++ b/glibc-RHEL-46737-1.patch @@ -0,0 +1,124 @@ +From 81e74c8676479811601b5894d72bb3d7e05f68dd Mon Sep 17 00:00:00 2001 +From: DJ Delorie +Date: Fri, 14 Mar 2025 16:08:12 -0400 +Subject: add ptmx support to test-container + +Conflicts: + support/Makefile (context, line numbers) + support/test-container.c (line numbers) + +diff --git a/support/Makefile b/support/Makefile +index dfe8e547f6..d41278eeab 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -324,6 +324,7 @@ tests = \ + tst-support_format_dns_packet \ + tst-support_fuse \ + tst-support-open-dev-null-range \ ++ tst-support-openpty \ + tst-support-process_state \ + tst-support_quote_blob \ + tst-support_quote_blob_wide \ +@@ -340,6 +341,10 @@ tests = \ + tst-xsigstack \ + # tests + ++tests-container = \ ++ tst-support-openpty-c \ ++ # tests-container ++ + ifeq ($(run-built-tests),yes) + tests-special = \ + $(objpfx)tst-support_record_failure-2.out +diff --git a/support/test-container.c b/support/test-container.c +index 79d3189e2f..a641250079 100644 +--- a/support/test-container.c ++++ b/support/test-container.c +@@ -1149,6 +1149,9 @@ main (int argc, char **argv) + devmount (new_root_path, "null"); + devmount (new_root_path, "zero"); + devmount (new_root_path, "urandom"); ++#ifdef __linux__ ++ devmount (new_root_path, "ptmx"); ++#endif + + /* We're done with the "old" root, switch to the new one. */ + if (chroot (new_root_path) < 0) +@@ -1214,6 +1217,14 @@ main (int argc, char **argv) + + maybe_xmkdir ("/tmp", 0755); + ++#ifdef __linux__ ++ maybe_xmkdir ("/dev/pts", 0777); ++ if (mount ("/dev/pts", "/dev/pts", "devpts", 0, "newinstance,ptmxmode=0666,mode=0666") < 0) ++ FAIL_EXIT1 ("can't mount /dev/pts: %m\n"); ++ if (mount ("/dev/pts/ptmx", "/dev/ptmx", "", MS_BIND | MS_REC, NULL) < 0) ++ FAIL_EXIT1 ("can't mount /dev/ptmx\n"); ++#endif ++ + if (require_pidns) + { + /* Now that we're pid 1 (effectively "root") we can mount /proc */ +diff --git a/support/tst-support-openpty-c.c b/support/tst-support-openpty-c.c +new file mode 100644 +index 0000000000..0a6a428fc3 +--- /dev/null ++++ b/support/tst-support-openpty-c.c +@@ -0,0 +1,2 @@ ++/* Same test, but in a test-container. */ ++#include "tst-support-openpty.c" +diff --git a/support/tst-support-openpty.c b/support/tst-support-openpty.c +new file mode 100644 +index 0000000000..1222d7018f +--- /dev/null ++++ b/support/tst-support-openpty.c +@@ -0,0 +1,49 @@ ++/* Basic test for support_openpty support in test-container. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* Note: the purpose of this test isn't to test if ptys function ++ correctly, but only to verify that test-container's support for ++ them is correct. The many checks in support_openpty.c are ++ sufficient for this. */ ++ ++int ++do_test (void) ++{ ++ int outer, inner; ++ char *name; ++ struct termios term; ++ struct winsize win; ++ ++ cfmakeraw (&term); ++ win.ws_row = 24; ++ win.ws_col = 80; ++ ++ support_openpty (&outer, &inner, &name, &term, &win); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-46737-2.patch b/glibc-RHEL-46737-2.patch new file mode 100644 index 0000000..9e96eb7 --- /dev/null +++ b/glibc-RHEL-46737-2.patch @@ -0,0 +1,1092 @@ +From 95b780c1d0549678c0a244c6e2112ec97edf0839 Mon Sep 17 00:00:00 2001 +From: DJ Delorie +Date: Fri, 14 Mar 2025 16:08:35 -0400 +Subject: stdio: Add more setvbuf tests + + +Conflicts: + stdio-common/Makefile (context, line numbers) + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index 31f40cf57c..3fd33b836d 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -304,6 +304,11 @@ endif + endif + endif + ++tests-internal = \ ++ tst-setvbuf2 \ ++ tst-setvbuf2-ind \ ++ # tests-internal ++ + test-srcs = \ + $(xprintf-srcs) \ + $(xscanf-srcs) \ +@@ -348,6 +353,11 @@ tests-special += \ + $(objpfx)tst-vfprintf-width-prec-mem.out \ + # tests-special + ++tests-container += \ ++ tst-setvbuf2 \ ++ tst-setvbuf2-ind ++ # tests-container ++ + generated += \ + $(foreach f,$(xprintf-stems),$(f)-mem.out) \ + $(foreach f,$(xprintf-stems),$(f).mtrace) \ +@@ -602,6 +612,10 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvb + cmp $^ > $@; \ + $(evaluate-test) + ++CFLAGS-tst-setvbuf2.c += -DIND_PROC=\"$(objpfx)tst-setvbuf2-ind\" ++$(objpfx)tst-setvbuf2-ind : $(objpfx)tst-setvbuf2-ind.o ++$(objpfx)tst-setvbuf2.out: $(objpfx)tst-setvbuf2-ind ++ + $(objpfx)tst-printf-round: $(libm) + $(objpfx)tst-scanf-round: $(libm) + +diff --git a/stdio-common/tst-setvbuf2-ind.c b/stdio-common/tst-setvbuf2-ind.c +new file mode 100644 +index 0000000000..fda2942c24 +--- /dev/null ++++ b/stdio-common/tst-setvbuf2-ind.c +@@ -0,0 +1,2 @@ ++#define INDEPENDENT_PART 1 ++#include "tst-setvbuf2.c" +diff --git a/stdio-common/tst-setvbuf2.c b/stdio-common/tst-setvbuf2.c +new file mode 100644 +index 0000000000..6cc83355f3 +--- /dev/null ++++ b/stdio-common/tst-setvbuf2.c +@@ -0,0 +1,1030 @@ ++/* Test setvbuf under various conditions. ++ Copyright (C) 2025 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 ++ . */ ++ ++/* This file is used twice, once as the test itself (where do_test ++ is defined) and once as a subprocess we spawn to test stdin et all ++ (where main is defined). INDEPENDENT_PART is defined for the ++ latter. ++ ++ Note also that the purpose of this test is to test setvbuf, not the ++ underlying buffering code. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Dear future developer: If you are reading this, you are likely ++ trying to change or understand this test. In that case, these ++ debug/dump macros will be helpful. */ ++#if 0 ++# define debug printf ("\033[3%dm%s:%d\033[0m\n", \ ++ (__LINE__ % 6) + 1, __FUNCTION__, __LINE__); ++ ++static void ++dumpfp (FILE *fp) ++{ ++ char f[10], *p=f; ++ ++ if (fp->_flags & _IO_UNBUFFERED) ++ *p++ = 'N'; ++ if (fp->_flags & _IO_LINE_BUF) ++ *p++ = 'L'; ++ if (p == f) ++ *p++ = 'B'; ++ *p = 0; ++ ++ printf ("FILE %p flags %s" ++ " read %p \033[%dm%+ld \033[%dm%+ld\033[0m" ++ " write %p \033[%dm%+ld \033[%dm%+ld\033[0m %ld" ++ " buf %p \033[%dm%+ld\033[0m sz %ld pend %ld\n", ++ fp, f, ++ ++ fp->_IO_read_base, ++ fp->_IO_read_ptr == fp->_IO_read_base ? 33 : 32, ++ fp->_IO_read_ptr - fp->_IO_read_base, ++ fp->_IO_read_end == fp->_IO_read_base ? 33 : 36, ++ fp->_IO_read_end - fp->_IO_read_base, ++ ++ fp->_IO_write_base, ++ fp->_IO_write_ptr == fp->_IO_write_base ? 33 : 32, ++ fp->_IO_write_ptr - fp->_IO_write_base, ++ fp->_IO_write_end == fp->_IO_write_base ? 33 : 36, ++ fp->_IO_write_end - fp->_IO_write_base, ++ fp->_IO_write_end - fp->_IO_write_base, ++ ++ fp->_IO_buf_base, ++ fp->_IO_buf_end == fp->_IO_buf_base ? 33 : 35, ++ fp->_IO_buf_end - fp->_IO_buf_base, ++ __fbufsize (fp), __fpending (fp) ++ ); ++} ++#else ++# define debug ++# define dumpfp(FP) ++#endif ++ ++#ifndef INDEPENDENT_PART ++/* st_blksize value for that file, or BUFSIZ if out of range. */ ++static int blksize = BUFSIZ; ++#endif ++ ++/* Our test buffer. */ ++#define TEST_BUFSIZE 42 ++static int bufsize = TEST_BUFSIZE < BUFSIZ ? TEST_BUFSIZE : BUFSIZ; ++static char *buffer; ++ ++/* Test data, both written to that file and used as an in-memory ++ stream. */ ++char test_data[2 * BUFSIZ]; ++ ++#define TEST_STRING "abcdef\n" ++ ++enum test_source_case ++ { ++ test_source_file, ++ test_source_pipe, ++ test_source_fifo, ++ test_source_pseudo_terminal, ++ test_source_dev_null, ++ test_source_count, ++ }; ++ ++static const char *const test_source_name[test_source_count] = ++ { ++ "regular file", ++ "pipe", ++ "fifo", ++ "pseudo_terminal", ++ "dev_null" ++ }; ++ ++enum test_stream_case ++ { ++ test_stream_stdin, ++ test_stream_stdout, ++ test_stream_stderr, ++ test_stream_fopen_r, ++ test_stream_fdopen_r, ++ test_stream_fopen_w, ++ test_stream_fdopen_w, ++ test_stream_count ++ }; ++ ++static bool test_stream_reads[test_stream_count] = ++ { ++ true, ++ false, ++ false, ++ true, ++ true, ++ false, ++ false ++ }; ++ ++static const char *const test_stream_name[test_stream_count] = ++ { ++ "stdin", ++ "stdout", ++ "stderr", ++ "fopen (read)", ++ "fdopen (read)", ++ "fopen (write)", ++ "fdopen (write)" ++ }; ++ ++enum test_config_case ++ { ++ test_config_none, ++ test_config_unbuffered, ++ test_config_line, ++ test_config_fully, ++ test_config_count ++ }; ++ ++static const char *const test_config_name[test_config_count] = ++ { ++ "no change", ++ "unbuffered", ++ "line buffered", ++ "fully buffered" ++ }; ++ ++FILE *test_stream; ++ ++char *test_file_name = NULL; ++int pty_fd; ++char *test_pipe_name = NULL; ++int test_pipe[2]; ++ ++/* This is either -1 or represents a pre-opened file descriptor for ++ the test as returned by prepare_test_file. */ ++int test_fd; ++ ++/*------------------------------------------------------------*/ ++ ++/* Note that throughout this test we reopen, remove, and change ++ to/from a fifo, the test file. This would normally cause a race ++ condition, except that we're in a test container. No other process ++ can run in the test container simultaneously. */ ++ ++void ++prepare_test_data (void) ++{ ++ buffer = (char *) xmalloc (bufsize); ++ ++#ifndef INDEPENDENT_PART ++ /* Both file and pipe need this. */ ++ if (test_file_name == NULL) ++ { ++ debug; ++ int fd = create_temp_file ("tst-setvbuf2", &test_file_name); ++ TEST_VERIFY_EXIT (fd != -1); ++ struct stat64 st; ++ xfstat64 (fd, &st); ++ if (st.st_blksize > 0 && st.st_blksize < BUFSIZ) ++ blksize = st.st_blksize; ++ xclose (fd); ++ } ++#endif ++ ++ for (size_t i = 0; i < 2 * BUFSIZ; i++) ++ { ++ unsigned char c = TEST_STRING[i % strlen (TEST_STRING)]; ++ test_data[i] = c; ++ } ++} ++ ++#ifndef INDEPENDENT_PART ++ ++/* These functions provide a source/sink for the "other" side of any ++ pipe-style descriptor we're using for test. */ ++ ++static pthread_t writer_thread_tid = 0; ++static pthread_t reader_thread_tid = 0; ++ ++typedef struct { ++ int fd; ++ const char *fname; ++} ThreadData; ++/* It's OK if this is static, we only run one at a time. */ ++ThreadData thread_data; ++ ++static void * ++writer_thread_proc (void *closure) ++{ ++ ThreadData *td = (ThreadData *) closure; ++ int fd; ++ int i; ++ ssize_t wn; ++ debug; ++ ++ if (td->fname) ++ td->fd = xopen (td->fname, O_WRONLY, 0777); ++ fd = td->fd; ++ ++ while (1) ++ { ++ i = 0; ++ while (i < BUFSIZ) ++ { ++ wn = write (fd, test_data + i, BUFSIZ - i); ++ if (wn <= 0) ++ break; ++ i += wn; ++ } ++ } ++ return NULL; ++} ++ ++static void * ++reader_thread_proc (void *closure) ++{ ++ ThreadData *td = (ThreadData *) closure; ++ int fd; ++ ssize_t rn; ++ int n = 0; ++ debug; ++ ++ if (td->fname) ++ td->fd = xopen (td->fname, O_RDONLY, 0777); ++ fd = td->fd; ++ ++ while (1) ++ { ++ char buf[BUFSIZ]; ++ rn = read (fd, buf, BUFSIZ); ++ if (rn <= 0) ++ break; ++ TEST_COMPARE_BLOB (buf, rn, test_data+n, rn); ++ n += rn; ++ } ++ return NULL; ++} ++ ++static void ++start_writer_thread (int fd) ++{ ++ debug; ++ thread_data.fd = fd; ++ thread_data.fname = NULL; ++ writer_thread_tid = xpthread_create (NULL, writer_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++start_writer_thread_n (const char *fname) ++{ ++ debug; ++ thread_data.fd = 0; ++ thread_data.fname = fname; ++ writer_thread_tid = xpthread_create (NULL, writer_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++end_writer_thread (void) ++{ ++ debug; ++ if (writer_thread_tid) ++ { ++ pthread_cancel (writer_thread_tid); ++ xpthread_join (writer_thread_tid); ++ xclose (thread_data.fd); ++ writer_thread_tid = 0; ++ } ++} ++ ++static void ++start_reader_thread (int fd) ++{ ++ debug; ++ thread_data.fd = fd; ++ thread_data.fname = NULL; ++ reader_thread_tid = xpthread_create (NULL, reader_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++start_reader_thread_n (const char *fname) ++{ ++ debug; ++ thread_data.fd = 0; ++ thread_data.fname = fname; ++ reader_thread_tid = xpthread_create (NULL, reader_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++end_reader_thread (void) ++{ ++ debug; ++ if (reader_thread_tid) ++ { ++ pthread_cancel (reader_thread_tid); ++ xpthread_join (reader_thread_tid); ++ xclose (thread_data.fd); ++ reader_thread_tid = 0; ++ } ++} ++ ++/*------------------------------------------------------------*/ ++ ++/* These two functions are reponsible for choosing a file to be tested ++ against, typically by returning a filename but in a few cases also ++ providing a file descriptor (i.e. for fdopen). */ ++ ++static const char * ++prepare_test_file (enum test_source_case f, enum test_stream_case s) ++{ ++ debug; ++ ++ test_fd = -1; ++ ++ switch (f) ++ { ++ case test_source_file: ++ { ++ if (test_stream_reads[f]) ++ { ++ debug; ++ FILE *fp = xfopen (test_file_name, "w"); ++ TEST_VERIFY_EXIT (fwrite (test_data, 1, 2 * BUFSIZ, fp) ++ == 2 * BUFSIZ); ++ xfclose (fp); ++ } ++ debug; ++ return test_file_name; ++ } ++ ++ case test_source_pipe: ++ { ++ debug; ++ xpipe (test_pipe); ++ if (test_stream_reads[s]) ++ { ++ start_writer_thread (test_pipe[1]); ++ test_fd = test_pipe[0]; ++ } ++ else ++ { ++ start_reader_thread (test_pipe[0]); ++ test_fd = test_pipe[1]; ++ } ++ test_pipe_name = xasprintf ("/proc/self/fd/%d", test_fd); ++ debug; ++ return test_pipe_name; ++ } ++ ++ case test_source_fifo: ++ { ++ /* We do not want to fail/exit if the file doesn't exist. */ ++ unlink (test_file_name); ++ xmkfifo (test_file_name, 0600); ++ debug; ++ if (test_stream_reads[s]) ++ start_writer_thread_n (test_file_name); ++ else ++ start_reader_thread_n (test_file_name); ++ debug; ++ return test_file_name; ++ } ++ ++ case test_source_pseudo_terminal: ++ { ++ support_openpty (&pty_fd, &test_fd, &test_pipe_name, NULL, NULL); ++ ++ debug; ++ if (test_stream_reads[s]) ++ start_writer_thread (pty_fd); ++ else ++ start_reader_thread (pty_fd); ++ ++ debug; ++ return test_pipe_name; ++ } ++ ++ case test_source_dev_null: ++ debug; ++ return "/dev/null"; ++ ++ default: ++ abort (); ++ } ++} ++ ++static void ++unprepare_test_file (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s) ++{ ++ debug; ++ switch (f) ++ { ++ case test_source_file: ++ break; ++ ++ case test_source_pipe: ++ free (test_pipe_name); ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ break; ++ ++ case test_source_fifo: ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ unlink (test_file_name); ++ break; ++ ++ case test_source_pseudo_terminal: ++ free (test_pipe_name); ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ break; ++ ++ case test_source_dev_null: ++ break; ++ ++ default: ++ abort (); ++ } ++ debug; ++} ++ ++/*------------------------------------------------------------*/ ++ ++/* This function takes a filename and returns a file descriptor, ++ opened according to the method requested. */ ++ ++static FILE * ++open_test_stream (enum test_source_case f, enum test_stream_case s) ++{ ++ int fd; ++ FILE *fp; ++ const char *fname; ++ ++ debug; ++ fname = prepare_test_file (f, s); ++ if (fname == NULL) ++ return NULL; ++ ++ switch (s) ++ { ++ case test_stream_stdin: ++ fp = xfopen (fname, "r"); ++ break; ++ ++ case test_stream_stdout: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_stderr: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_fopen_r: ++ fp = xfopen (fname, "r"); ++ break; ++ ++ case test_stream_fdopen_r: ++ if (test_fd == -1) ++ fd = xopen (fname, O_RDONLY, 0); ++ else ++ fd = test_fd; ++ fp = fdopen (fd, "r"); ++ break; ++ ++ case test_stream_fopen_w: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_fdopen_w: ++ fd = xopen (fname, O_WRONLY|O_CREAT|O_TRUNC, 0777); ++ fp = fdopen (fd, "w"); ++ break; ++ ++ default: ++ abort (); ++ } ++ TEST_VERIFY_EXIT (fp != NULL); ++ ++ if (f == test_source_pseudo_terminal) ++ { ++ struct termios t; ++ /* We disable the NL to CR-LF conversion so that we can compare ++ data without having to remove the extra CRs. */ ++ if (tcgetattr (fileno (fp), &t) < 0) ++ FAIL_EXIT1 ("tcgetattr failed: %m"); ++ t.c_oflag &= ~ONLCR; ++ if (tcsetattr (fileno (fp), TCSANOW, &t) < 0) ++ FAIL_EXIT1 ("tcsetattr failed: %m"); ++ } ++ ++ debug; ++ printf ("source %s stream %s file %s fd %d\n", ++ test_source_name[f], ++ test_stream_name[s], fname, fileno (fp)); ++ return fp; ++} ++ ++#endif ++ ++/*------------------------------------------------------------*/ ++ ++/* These functions do the actual testing - setting various buffering ++ options and verifying that they buffer as expected. */ ++ ++static void ++test_put_string (FILE *fp, const char *s, int count) ++{ ++ while (*s && count--) ++ { ++ fputc (*s++, fp); ++ TEST_VERIFY_EXIT (!ferror (fp)); ++ } ++} ++ ++int ++verify_fully_buffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ if (test_stream_reads[s]) ++ { ++ char buf[10]; ++ dumpfp (fp); ++ size_t fc = fread (buf, 1, 10 - 1, fp); ++ dumpfp (fp); ++ ++ ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; ++ ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ { ++ TEST_VERIFY (fc == 0); ++ TEST_VERIFY (count == 0); ++ } ++ else if (f == test_source_pseudo_terminal) ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 3 || count == 10); ++ } ++ else ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 10); ++ } ++ ++ /* We already checked for the first character being 'a'. */ ++ if (count > 1) ++ { ++ TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); ++ TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ test_put_string (fp, test_data + 1, 10 - 1); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 10); ++ TEST_COMPARE_BLOB (fp->_IO_write_base, 10, test_data, 10); ++ } ++ ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), 0); ++ if (c != test_config_none) ++ TEST_COMPARE (__fbufsize (fp), bufsize); ++ return 0; ++} ++ ++int ++verify_line_buffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ /* "line buffered" for inputs is not really defined; what you really ++ want here is to control the device providing input. For GLIBC a ++ line-buffered input is treated as fully buffered. */ ++ if (test_stream_reads[s]) ++ { ++ char buf[10]; ++ dumpfp (fp); ++ size_t fc = fread (buf, 1, 10 - 1, fp); ++ dumpfp (fp); ++ ++ ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; ++ ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ { ++ TEST_VERIFY (fc == 0); ++ TEST_VERIFY (count == 0); ++ } ++ else if (f == test_source_pseudo_terminal) ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 3 || count == 10); ++ } ++ else ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 10); ++ } ++ ++ /* We already checked for the first character being 'a'. */ ++ if (count > 1) ++ { ++ TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); ++ TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ test_put_string (fp, test_data + 1, 10 - 1); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 3); ++ /* The first "abcdef\n" got flushed, leaving "abc". */ ++ TEST_COMPARE_BLOB (fp->_IO_write_base, 3, test_data + 7, 3); ++ } ++ ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), _IO_LINE_BUF); ++ if (c != test_config_none) ++ TEST_COMPARE (__fbufsize (fp), bufsize); ++ return 0; ++} ++ ++int ++verify_unbuffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ if (test_stream_reads[s]) ++ { ++ /* We've already read one byte. */ ++ dumpfp (fp); ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 0); ++ else ++ { ++ TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 1); ++ TEST_COMPARE (fp->_IO_read_base[0], test_data[0]); ++ TEST_VERIFY (fp->_IO_read_ptr == fp->_IO_read_end); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ fputc (test_data[1], fp); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 0); ++ TEST_COMPARE (fp->_IO_write_base[0], test_data[1]); ++ TEST_VERIFY (fp->_IO_write_end == fp->_IO_write_base); ++ } ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), ++ _IO_UNBUFFERED); ++ TEST_COMPARE (__fbufsize (fp), 1); ++ return 0; ++} ++ ++static int ++do_setvbuf (FILE *fp, void *buf, int flags, int size, ++ enum test_stream_case s) ++{ ++ if (s != test_stream_stdout) ++ printf ("SETVBUF %p %p %s %d\n", ++ fp, buf, ++ flags == _IONBF ? "_IONBF" ++ : flags == _IOLBF ? "_IOLBF" ++ : flags == _IOFBF ? "_IOFBF" ++ : "???", size); ++ if (setvbuf (fp, buf, flags, size)) ++ { ++ perror ("setvbuf"); ++ return 1; ++ } ++ return 0; ++} ++ ++int ++do_second_part (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ /* At this point, FP is the stream to test according to the other ++ parameters. */ ++ ++ int rv = 0; ++ int flags_before; ++ int flags_after; ++ ++ debug; ++ ++ flags_before = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); ++ ++ /* This is where we do the thing we're testing for. */ ++ switch (c) ++ { ++ case test_config_none: ++ /* Buffering is unchanged. */ ++ break; ++ ++ case test_config_unbuffered: ++ do_setvbuf (fp, NULL, _IONBF, 0, s); ++ break; ++ ++ case test_config_line: ++ do_setvbuf (fp, buffer, _IOLBF, bufsize, s); ++ break; ++ ++ case test_config_fully: ++ do_setvbuf (fp, buffer, _IOFBF, bufsize, s); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ flags_after = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); ++ ++ /* Check the buffer mode after we touch it, if we touched it. */ ++ switch (c) ++ { ++ case test_config_none: ++ /* Buffering is unchanged, but may change on the first read/write. */ ++ TEST_COMPARE (flags_after, flags_before); ++ break; ++ ++ case test_config_unbuffered: ++ TEST_COMPARE (flags_after, _IO_UNBUFFERED); ++ break; ++ ++ case test_config_line: ++ TEST_COMPARE (flags_after, _IO_LINE_BUF); ++ break; ++ ++ case test_config_fully: ++ TEST_COMPARE (flags_after, 0); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ /* Glibc defers calculating the appropriate buffering mechanism ++ until it reads from or writes to the device. So we read one ++ character here, and account for that in the tests. */ ++ if (test_stream_reads[s]) ++ { ++ dumpfp (fp); ++ int c = fgetc (fp); ++ if (c != TEST_STRING[0] && f != test_source_dev_null) ++ FAIL ("first char read is %c not %c", c, TEST_STRING[0]); ++ dumpfp (fp); ++ } ++ else ++ { ++ dumpfp (fp); ++ fputc (TEST_STRING[0], fp); ++ dumpfp (fp); ++ } ++ ++ switch (fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)) ++ { ++ case _IO_LINE_BUF: ++ rv += verify_line_buffered (fp, f, s, c); ++ break; ++ ++ case _IO_UNBUFFERED: ++ rv += verify_unbuffered (fp, f, s, c); ++ break; ++ ++ case 0: /* Fully buffered. */ ++ rv += verify_fully_buffered (fp, f, s, c); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ ++ fclose (fp); ++ return rv; ++} ++ ++/*------------------------------------------------------------*/ ++ ++#ifdef INDEPENDENT_PART ++/* This part is the independent sub-process we call to test stdin et ++ al. */ ++ ++int ++main (int argc, char **argv) ++{ ++ /* This is one of the subprocesses we created to test stdin et ++ al. */ ++ FILE *fp; ++ ++ /* If we're called as a regular test, instead of as a sub-process, ++ don't complain. */ ++ if (argc == 1) ++ return 0; ++ ++ if (argc != 4) ++ { ++ int i; ++ for (i = 0; i <= argc; i ++) ++ printf ("argv[%d] = `%s'\n", i, argv[i] ?: "(null)"); ++ FAIL_EXIT1 ("sub-process called wrong"); ++ } ++ ++ prepare_test_data (); ++ ++ enum test_source_case f = atoi (argv[1]); ++ enum test_stream_case s = atoi (argv[2]); ++ enum test_config_case c = atoi (argv[3]); ++ ++ if (s != test_stream_stdout) ++ printf ("\n\033[41mRunning test %s : %s : %s\033[0m\n", ++ test_source_name[f], ++ test_stream_name[s], ++ test_config_name[c]); ++ ++ switch (s) ++ { ++ case test_stream_stdin: ++ fp = stdin; ++ break; ++ case test_stream_stdout: ++ fp = stdout; ++ break; ++ case test_stream_stderr: ++ fp = stderr; ++ break; ++ default: ++ abort (); ++ } ++ ++ return do_second_part (fp, f, s, c); ++} ++ ++#else ++/* This part is the standard test process. */ ++ ++/* Spawn an independent sub-process with std* redirected. */ ++int ++recurse (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ /* We need to test stdin, stdout, or stderr, which means creating a ++ subprocess with one of those redirected from FP. */ ++ debug; ++ ++ pid_t pid; ++ int status; ++ ++ pid = fork (); ++ ++ switch (pid) ++ { ++ case -1: /* error */ ++ perror ("fork"); ++ return 1; ++ break; ++ ++ default: /* parent */ ++ fclose (fp); ++ xwaitpid (pid, &status, 0); ++ if (WIFEXITED (status) ++ && WEXITSTATUS (status) == 0) ++ return 0; ++ return 1; ++ ++ case 0: /* child */ ++ switch (s) ++ { ++ case test_stream_stdin: ++ xclose (0); ++ dup2 (fileno (fp), 0); ++ break; ++ case test_stream_stdout: ++ xclose (1); ++ dup2 (fileno (fp), 1); ++ break; ++ case test_stream_stderr: ++ xclose (2); ++ dup2 (fileno (fp), 2); ++ break; ++ default: ++ abort (); ++ } ++ fclose (fp); ++ ++ /* At this point, we have to run a program... which is tricky to ++ properly support for remote targets or crosses, because of ++ glibc versions etc. Hence we run in a test-container. */ ++ ++ char fs[10], ss[10], cs[10]; ++ sprintf (fs, "%d", f); ++ sprintf (ss, "%d", s); ++ sprintf (cs, "%d", c); ++ execl (IND_PROC, IND_PROC, fs, ss, cs, NULL); ++ if (s == test_stream_stdout) ++ fprintf (stderr, "execl (%s) failed, ", IND_PROC); ++ else ++ printf ("execl (%s) failed, ", IND_PROC); ++ perror ("The error was"); ++ exit (1); ++ break; ++ } ++ ++ return 0; ++} ++ ++int ++do_test (void) ++{ ++ int rv = 0; ++ ++ signal (SIGPIPE, SIG_IGN); ++ ++ prepare_test_data (); ++ ++ for (enum test_source_case f = 0; f < test_source_count; ++f) ++ for (enum test_stream_case s = 0; s < test_stream_count; ++s) ++ for (enum test_config_case c = 0; c < test_config_count; ++c) ++ { ++ printf ("\n\033[43mRunning test %s : %s : %s\033[0m\n", ++ test_source_name[f], ++ test_stream_name[s], ++ test_config_name[c]); ++ ++ FILE *fp = open_test_stream (f, s); ++ ++ if (fp) ++ { ++ ++ if (s <= test_stream_stderr) ++ rv += recurse (fp, f, s, c); ++ else ++ rv += do_second_part (fp, f, s, c); ++ ++ unprepare_test_file (fp, f, s); ++ } ++ } ++ ++ free (buffer); ++ ++ printf ("return %d\n", rv); ++ return rv; ++} ++ ++# include ++#endif ++ diff --git a/glibc.spec b/glibc.spec index 6466004..0c9dc72 100644 --- a/glibc.spec +++ b/glibc.spec @@ -157,7 +157,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 184%{?dist} +Release: 185%{?dist} # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1181,6 +1181,8 @@ Patch873: glibc-RHEL-46726-12.patch Patch874: glibc-RHEL-46726-13.patch Patch875: glibc-RHEL-46726-14.patch Patch876: glibc-RHEL-46726-15.patch +Patch877: glibc-RHEL-46737-1.patch +Patch878: glibc-RHEL-46737-2.patch ############################################################################## # Continued list of core "glibc" package information: @@ -3174,6 +3176,9 @@ update_gconv_modules_cache () %endif %changelog +* Wed Apr 2 2025 DJ Delorie - 2.34-185 +- Extend setvbuf testing (RHEL-46737) + * Wed Apr 2 2025 Florian Weimer - 2.34-184 - Extend scanf testing (RHEL-46726)