Add FUSE based tests for fchmod, lstat, and mkstemp (RHEL-108823)

Resolves: RHEL-108823
This commit is contained in:
Arjun Shankar 2025-08-19 10:10:08 +02:00
parent ca8e2eea50
commit 9c6db562f1
15 changed files with 4453 additions and 1 deletions

23
glibc-RHEL-108823-1.patch Normal file
View File

@ -0,0 +1,23 @@
commit 34bb581e7713589d38c797c214f4c6bf2b14b702
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 16 16:05:19 2024 +0200
support: Include <string.h> for strcmp in support_format_addrinfo.c
This is currently implied by the internal headers, but it makes
sense not to rely on this.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/support/support_format_addrinfo.c b/support/support_format_addrinfo.c
index cbc72910a96a0e36..77f4db345c2912aa 100644
--- a/support/support_format_addrinfo.c
+++ b/support/support_format_addrinfo.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <support/support.h>
#include <support/xmemstream.h>

460
glibc-RHEL-108823-10.patch Normal file
View File

@ -0,0 +1,460 @@
commit e3db0a699c639e97deddcb15939fd9c162801c77
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Sep 21 19:25:35 2024 +0200
misc: FUSE-based tests for mkstemp
The tests check that O_EXCL is used properly, that 0600 is used
as the mode, that the characters used are as expected, and that
the distribution of names generated is reasonably random.
The tests run very slowly on some kernel versions, so make them
xtests.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/misc/Makefile b/misc/Makefile
index 235fc7eacb6a980d..a684fb1076fc2354 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -286,6 +286,12 @@ tests-static := tst-empty
tests-internal += tst-fd_to_filename
tests-static += tst-fd_to_filename
+# Tests with long run times.
+xtests += \
+ tst-mkstemp-fuse \
+ tst-mkstemp-fuse-parallel \
+ # xtests
+
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-error1-mem.out \
$(objpfx)tst-allocate_once-mem.out
diff --git a/misc/tst-mkstemp-fuse-parallel.c b/misc/tst-mkstemp-fuse-parallel.c
new file mode 100644
index 0000000000000000..219f26cb3bf7c0ba
--- /dev/null
+++ b/misc/tst-mkstemp-fuse-parallel.c
@@ -0,0 +1,219 @@
+/* FUSE-based test for mkstemp. Parallel collision statistics.
+ 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 <stdlib.h>
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/fuse.h>
+#include <support/support.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+
+/* The number of subprocesses that call mkstemp. */
+static pid_t processes[4];
+
+/* Enough space to record the expected number of replies (62**3) for
+ each process. */
+enum { results_allocated = array_length (processes) * 62 * 62 * 62 };
+
+/* The thread will store the results there. */
+static uint64_t *results;
+
+/* Currently used part of the results array. */
+static size_t results_used;
+
+/* Fail with EEXIST (so that mkstemp tries again). Record observed
+ names for later statistical analysis. */
+static void
+fuse_thread (struct support_fuse *f, void *closure)
+{
+ struct fuse_in_header *inh;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+ if (inh->opcode != FUSE_LOOKUP || results_used >= results_allocated)
+ {
+ support_fuse_reply_error (f, EIO);
+ continue;
+ }
+
+ char *name = support_fuse_cast (LOOKUP, inh);
+ TEST_COMPARE_BLOB (name, 3, "new", 3);
+ TEST_COMPARE (strlen (name), 9);
+ /* Extract 8 bytes of the name: 'w', the X replacements, and the
+ null terminator. Treat it as an uint64_t for easy sorting
+ below. Endianess does not matter because the relative order
+ of the entries is not important; sorting is only used to find
+ duplicates. */
+ TEST_VERIFY_EXIT (results_used < results_allocated);
+ memcpy (&results[results_used], name + 2, 8);
+ ++results_used;
+ struct fuse_entry_out *out = support_fuse_prepare_entry (f, 2);
+ out->attr.mode = S_IFREG | 0600;
+ support_fuse_reply_prepared (f);
+ }
+}
+
+/* Used to sort the results array, to find duplicates. */
+static int
+results_sort (const void *a1, const void *b1)
+{
+ const uint64_t *a = a1;
+ const uint64_t *b = b1;
+ if (*a < *b)
+ return -1;
+ if (*a == *b)
+ return 0;
+ return 1;
+}
+
+/* Number of occurrences of certain streak lengths. */
+static size_t streak_lengths[6];
+
+/* Called for every encountered streak. */
+static inline void
+report_streak (uint64_t current, size_t length)
+{
+ if (length > 1)
+ {
+ printf ("info: name \"ne%.8s\" repeats: %zu\n",
+ (char *) &current, length);
+ TEST_VERIFY_EXIT (length < array_length (streak_lengths));
+ }
+ TEST_VERIFY_EXIT (length < array_length (streak_lengths));
+ ++streak_lengths[length];
+}
+
+static int
+do_test (void)
+{
+ support_fuse_init ();
+
+ results = xmalloc (results_allocated * sizeof (*results));
+
+ struct shared
+ {
+ /* Used to synchronize the start of all subprocesses, to make it
+ more likely to expose concurrency-related bugs. */
+ pthread_barrier_t barrier1;
+ pthread_barrier_t barrier2;
+
+ /* Filled in after fork. */
+ char mountpoint[4096];
+ };
+
+ /* Used to synchronize the start of all subprocesses, to make it
+ more likely to expose concurrency-related bugs. */
+ struct shared *pshared = support_shared_allocate (sizeof (*pshared));
+ {
+ pthread_barrierattr_t attr;
+ xpthread_barrierattr_init (&attr);
+ xpthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
+ xpthread_barrierattr_destroy (&attr);
+ xpthread_barrier_init (&pshared->barrier1, &attr,
+ array_length (processes) + 1);
+ xpthread_barrier_init (&pshared->barrier2, &attr,
+ array_length (processes) + 1);
+ xpthread_barrierattr_destroy (&attr);
+ }
+
+ for (int i = 0; i < array_length (processes); ++i)
+ {
+ processes[i] = xfork ();
+ if (processes[i] == 0)
+ {
+ /* Wait for mountpoint initialization. */
+ xpthread_barrier_wait (&pshared->barrier1);
+ char *path = xasprintf ("%s/newXXXXXX", pshared->mountpoint);
+
+ /* Park this process until all processes have started. */
+ xpthread_barrier_wait (&pshared->barrier2);
+ errno = 0;
+ TEST_COMPARE (mkstemp (path), -1);
+ TEST_COMPARE (errno, EEXIST);
+ free (path);
+ _exit (0);
+ }
+ }
+
+ /* Do this after the forking, to minimize initialization inteference. */
+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);
+ TEST_VERIFY (strlcpy (pshared->mountpoint, support_fuse_mountpoint (f),
+ sizeof (pshared->mountpoint))
+ < sizeof (pshared->mountpoint));
+ xpthread_barrier_wait (&pshared->barrier1);
+
+ puts ("info: performing mkstemp calls");
+ xpthread_barrier_wait (&pshared->barrier2);
+
+ for (int i = 0; i < array_length (processes); ++i)
+ {
+ int status;
+ xwaitpid (processes[i], &status, 0);
+ TEST_COMPARE (status, 0);
+ }
+
+ support_fuse_unmount (f);
+ xpthread_barrier_destroy (&pshared->barrier2);
+ xpthread_barrier_destroy (&pshared->barrier1);
+
+ printf ("info: checking results (count %zu)\n", results_used);
+ qsort (results, results_used, sizeof (*results), results_sort);
+
+ uint64_t current = -1;
+ size_t streak = 0;
+ for (size_t i = 0; i < results_used; ++i)
+ if (results[i] == current)
+ ++streak;
+ else
+ {
+ report_streak (current, streak);
+ current = results[i];
+ streak = 1;
+ }
+ report_streak (current, streak);
+
+ puts ("info: repetition count distribution:");
+ for (int i = 1; i < array_length (streak_lengths); ++i)
+ printf (" length %d: %zu\n", i, streak_lengths[i]);
+ /* Some arbitrary threshold, hopefully unlikely enough. In over
+ 260,000 runs of a simulation of this test, at most 26 pairs were
+ observed, and only one three-way collisions. */
+ if (streak_lengths[2] > 30)
+ FAIL ("unexpected repetition count 2: %zu", streak_lengths[2]);
+ if (streak_lengths[3] > 2)
+ FAIL ("unexpected repetition count 3: %zu", streak_lengths[3]);
+ for (int i = 4; i < array_length (streak_lengths); ++i)
+ if (streak_lengths[i] > 0)
+ FAIL ("too many repeats of count %d: %zu", i, streak_lengths[i]);
+
+ free (results);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/misc/tst-mkstemp-fuse.c b/misc/tst-mkstemp-fuse.c
new file mode 100644
index 0000000000000000..5ac6a6872a9c513d
--- /dev/null
+++ b/misc/tst-mkstemp-fuse.c
@@ -0,0 +1,197 @@
+/* FUSE-based test for mkstemp.
+ 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 <stdlib.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/fuse.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+
+/* Set to true in do_test to cause the first FUSE_CREATE attempt to fail. */
+static _Atomic bool simulate_creat_race;
+
+/* Basic tests with eventually successful creation. */
+static void
+fuse_thread_basic (struct support_fuse *f, void *closure)
+{
+ char *previous_name = NULL;
+ int state = 0;
+ struct fuse_in_header *inh;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+
+ switch (inh->opcode)
+ {
+ case FUSE_LOOKUP:
+ /* File does not exist initially. */
+ TEST_COMPARE (inh->nodeid, 1);
+ if (simulate_creat_race)
+ {
+ if (state < 3)
+ ++state;
+ else
+ FAIL ("invalid state: %d", state);
+ }
+ else
+ {
+ TEST_COMPARE (state, 0);
+ state = 3;
+ }
+ support_fuse_reply_error (f, ENOENT);
+ break;
+ case FUSE_CREATE:
+ {
+ TEST_COMPARE (inh->nodeid, 1);
+ char *name;
+ struct fuse_create_in *p
+ = support_fuse_cast_name (CREATE, inh, &name);
+ /* Name follows after struct fuse_create_in. */
+ TEST_COMPARE (p->flags & O_ACCMODE, O_RDWR);
+ TEST_VERIFY (p->flags & O_EXCL);
+ TEST_VERIFY (p->flags & O_CREAT);
+ TEST_COMPARE (p->mode & 07777, 0600);
+ TEST_VERIFY (S_ISREG (p->mode));
+ TEST_COMPARE_BLOB (name, 3, "new", 3);
+
+ if (state != 3 && simulate_creat_race)
+ {
+ ++state;
+ support_fuse_reply_error (f, EEXIST);
+ }
+ else
+ {
+ if (previous_name != NULL)
+ /* This test has a very small probability of failure
+ due to a harmless collision (one in 62**6 tests). */
+ TEST_VERIFY (strcmp (name, previous_name) != 0);
+ TEST_COMPARE (state, 3);
+ ++state;
+ struct fuse_entry_out *entry;
+ struct fuse_open_out *open;
+ support_fuse_prepare_create (f, 2, &entry, &open);
+ entry->attr.mode = S_IFREG | 0600;
+ support_fuse_reply_prepared (f);
+ }
+ free (previous_name);
+ previous_name = xstrdup (name);
+ }
+ break;
+ case FUSE_FLUSH:
+ case FUSE_RELEASE:
+ TEST_COMPARE (state, 4);
+ TEST_COMPARE (inh->nodeid, 2);
+ support_fuse_reply_empty (f);
+ break;
+ default:
+ support_fuse_reply_error (f, EIO);
+ }
+ }
+ free (previous_name);
+}
+
+/* Reply that all files exist. */
+static void
+fuse_thread_eexist (struct support_fuse *f, void *closure)
+{
+ uint64_t counter = 0;
+ struct fuse_in_header *inh;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+
+ switch (inh->opcode)
+ {
+ case FUSE_LOOKUP:
+ ++counter;
+ TEST_COMPARE (inh->nodeid, 1);
+ char *name = support_fuse_cast (LOOKUP, inh);
+ TEST_COMPARE_BLOB (name, 3, "new", 3);
+ TEST_COMPARE (strlen (name), 9);
+ for (int i = 3; i <= 8; ++i)
+ {
+ /* The glibc implementation uses letters and digits only. */
+ char ch = name[i];
+ TEST_VERIFY (('0' <= ch && ch <= '9')
+ || ('a' <= ch && ch <= 'z')
+ || ('A' <= ch && ch <= 'Z'));
+ }
+ struct fuse_entry_out out =
+ {
+ .nodeid = 2,
+ .attr = {
+ .mode = S_IFREG | 0600,
+ .ino = 2,
+ },
+ };
+ support_fuse_reply (f, &out, sizeof (out));
+ break;
+ default:
+ support_fuse_reply_error (f, EIO);
+ }
+ }
+ /* Verify that mkstemp has retried a lot. The current
+ implementation tries 62 * 62 * 62 times until it goves up. */
+ TEST_VERIFY (counter >= 200000);
+}
+
+static int
+do_test (void)
+{
+ support_fuse_init ();
+
+ for (int do_simulate_creat_race = 0; do_simulate_creat_race < 2;
+ ++do_simulate_creat_race)
+ {
+ simulate_creat_race = do_simulate_creat_race;
+ printf ("info: testing with simulate_creat_race == %d\n",
+ (int) simulate_creat_race);
+ struct support_fuse *f = support_fuse_mount (fuse_thread_basic, NULL);
+ char *path = xasprintf ("%s/newXXXXXX", support_fuse_mountpoint (f));
+ int fd = mkstemp (path);
+ TEST_VERIFY (fd > 2);
+ xclose (fd);
+ free (path);
+ support_fuse_unmount (f);
+ }
+
+ puts ("info: testing EEXIST failure case for mkstemp");
+ {
+ struct support_fuse *f = support_fuse_mount (fuse_thread_eexist, NULL);
+ char *path = xasprintf ("%s/newXXXXXX", support_fuse_mountpoint (f));
+ errno = 0;
+ TEST_COMPARE (mkstemp (path), -1);
+ TEST_COMPARE (errno, EEXIST);
+ free (path);
+ support_fuse_unmount (f);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,33 @@
commit 455c7622835d16c79e49fe75b8d3a1ae59a3d0ee
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Sep 21 19:25:35 2024 +0200
support: Fix memory leaks in FUSE tests
The internal read buffer (used by all FUSE tests) was not freed.
The support/tst-support_fuse test missed a deallocation.
diff --git a/support/support_fuse.c b/support/support_fuse.c
index 135dbf1198d5a4c7..f6c063b549e2c26c 100644
--- a/support/support_fuse.c
+++ b/support/support_fuse.c
@@ -659,6 +659,7 @@ support_fuse_unmount (struct support_fuse *f)
if (rmdir (f->mountpoint) != 0)
FAIL ("FUSE: rmdir (\"%s\"): %m", f->mountpoint);
xclose (f->fd);
+ free (f->buffer_start);
free (f->mountpoint);
free (f->readdir_buffer);
free (f);
diff --git a/support/tst-support_fuse.c b/support/tst-support_fuse.c
index c4075a6608d9dd65..9ee637cbab6f6b9e 100644
--- a/support/tst-support_fuse.c
+++ b/support/tst-support_fuse.c
@@ -331,6 +331,7 @@ do_test (void)
{
char *subdir_path = xasprintf ("%s/subdir", support_fuse_mountpoint (f));
xmkdir (subdir_path, 01234);
+ free (subdir_path);
}
{

View File

@ -0,0 +1,26 @@
commit 366cce74d2aa2e5753d8787d415b745fd57fda04
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Sep 21 19:29:13 2024 +0200
support: Add valgrind instructions to <support/fuse.h>
Replacing an outdated comment (namespace setup is now handled by
support_fuse_init).
diff --git a/support/fuse.h b/support/fuse.h
index 4c365fbc0c7408b9..1c862bedbe487ce6 100644
--- a/support/fuse.h
+++ b/support/fuse.h
@@ -16,8 +16,10 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-/* Before using this functionality, use support_enter_mount_namespace
- to ensure that mounts do not impact the overall system. */
+/* To run FUSE tests under valgrind, pass the
+ --sim-hints=fuse-compatible option to valgrind. This option tells
+ valgrind that additional system calls effectively call back into
+ the current program. */
#ifndef SUPPORT_FUSE_H
#define SUPPORT_FUSE_H

View File

@ -0,0 +1,17 @@
commit 3ef26b708725b528a1c69ab3eb523036c50b89d6
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Sep 24 13:05:48 2024 +0200
misc: Link tst-mkstemp-fuse-parallel with $(shared-thread-library)
The barrier functions require this on Hurd.
diff --git a/misc/Makefile b/misc/Makefile
index a684fb1076fc2354..6aa74d332b40ca94 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -369,3 +369,4 @@ $(objpfx)tst-select: $(librt)
$(objpfx)tst-select-time64: $(librt)
$(objpfx)tst-pselect: $(librt)
$(objpfx)tst-pselect-time64: $(librt)
+$(objpfx)tst-mkstemp-fuse-parallel: $(shared-thread-library)

View File

@ -0,0 +1,39 @@
commit 6f999af332c91035350390ef8af96388b8f4fd2c
Author: Arjun Shankar <arjun@redhat.com>
Date: Mon Aug 18 15:33:13 2025 +0200
support: Handle FUSE_GETXATTR during FUSE FS mount
When testing with some kernel versions, support FUSE infrastructure
encounters a FUSE_GETXATTR request, leading to FUSE tests hanging until
timed out. Therefore, pass FUSE_GETXATTR requests from
support_fuse_handle_mountpoint to support_fuse_handle_directory, and
adjust support_fuse_handle_directory to return ENOSYS so that tests can
proceed.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/support/support_fuse.c b/support/support_fuse.c
index f6c063b549e2c26c..5af2f7d8ab93a5ea 100644
--- a/support/support_fuse.c
+++ b/support/support_fuse.c
@@ -212,6 +212,9 @@ support_fuse_handle_directory (struct support_fuse *f)
support_fuse_reply_prepared (f);
}
return true;
+ case FUSE_GETXATTR:
+ support_fuse_reply_error (f, ENOSYS);
+ return true;
default:
return false;
}
@@ -222,7 +225,8 @@ support_fuse_handle_mountpoint (struct support_fuse *f)
{
TEST_VERIFY (f->inh != NULL);
/* 1 is the root node. */
- if (f->inh->opcode == FUSE_GETATTR && f->inh->nodeid == 1)
+ if ((f->inh->opcode == FUSE_GETATTR || f->inh->opcode == FUSE_GETXATTR)
+ && f->inh->nodeid == 1)
return support_fuse_handle_directory (f);
return false;
}

41
glibc-RHEL-108823-2.patch Normal file
View File

@ -0,0 +1,41 @@
commit 34e52acd55d69964d14fb3188c5538442b8b32be
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 22 16:14:17 2024 +0200
support: Report errno constants in TEST_COMPARE failures
If the expression is errno, decode it as an errno constant
using strerrorname_np.
Reviewed-by: Arjun Shankar <arjun@redhat.com>
diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c
index ae73d200cda81ea9..dba79e413feffec5 100644
--- a/support/support_test_compare_failure.c
+++ b/support/support_test_compare_failure.c
@@ -17,7 +17,9 @@
<https://www.gnu.org/licenses/>. */
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
+#include <string.h>
#include <support/check.h>
static void
@@ -31,7 +33,14 @@ report (const char *which, const char *expr, long long value, int positive,
printf ("%lld", value);
unsigned long long mask
= (~0ULL) >> (8 * (sizeof (unsigned long long) - size));
- printf (" (0x%llx); from: %s\n", (unsigned long long) value & mask, expr);
+ const char *errno_constant = NULL;
+ if (strcmp (expr, "errno") == 0
+ && positive && (unsigned long long int) value <= INT_MAX)
+ errno_constant = strerrorname_np (value);
+ printf (" (0x%llx", (unsigned long long) value & mask);
+ if (errno_constant != NULL)
+ printf (", %s", errno_constant);
+ printf ("); from: %s\n", expr);
}
void

88
glibc-RHEL-108823-3.patch Normal file
View File

@ -0,0 +1,88 @@
commit 424d97be50488beb6196c0ff0bc3dfeb87b4281c
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 30 20:37:18 2024 +0200
io: Add error tests for fchmod
On Linux most descriptors that do not correspond to file system
entities (such as anonymous pipes and sockets) have file permissions
that can be changed. While it is possible to create a custom file
system that returns (say) EINVAL for an fchmod attempt, testing this
does not appear to be useful.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/io/Makefile b/io/Makefile
index 54d950d51f31758b..8ecd2842e20fef97 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -188,6 +188,7 @@ tests := \
tst-closefrom \
tst-copy_file_range \
tst-faccessat \
+ tst-fchmod-errors \
tst-fchmodat \
tst-fchownat \
tst-fcntl \
diff --git a/io/tst-fchmod-errors.c b/io/tst-fchmod-errors.c
new file mode 100644
index 0000000000000000..ee15300fc3edf6f0
--- /dev/null
+++ b/io/tst-fchmod-errors.c
@@ -0,0 +1,56 @@
+/* Test various fchmod error cases.
+ 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 <support/check.h>
+#include <support/xunistd.h>
+
+static int
+do_test (void)
+{
+ {
+ /* Permissions on /dev/null (the opened descriptor) cannot be changed. */
+ int fd = xopen ("/dev/null", O_RDWR, 0);
+ errno = 0;
+ TEST_COMPARE (fchmod (fd, 0), -1);
+ TEST_COMPARE (errno, EPERM);
+ xclose (fd);
+
+ /* Now testing an invalid file descriptor. */
+ errno = 0;
+ TEST_COMPARE (fchmod (fd, 0600), -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+
+ errno = 0;
+ TEST_COMPARE (fchmod (-1, 0600), -1);
+ TEST_COMPARE (errno, EBADF);
+
+ errno = 0;
+ TEST_COMPARE (fchmod (AT_FDCWD, 0600), -1);
+ TEST_COMPARE (errno, EBADF);
+
+ /* Linux supports fchmod on pretty much all file descriptors, so
+ there is no check for failure on specific types of descriptors
+ here. */
+
+ return 0;
+}
+
+#include <support/test-driver.c>

44
glibc-RHEL-108823-4.patch Normal file
View File

@ -0,0 +1,44 @@
commit 3844cdc33093dbe1e33ddb831eada9bdb4a482b9
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 30 22:07:12 2024 +0200
io: Fix destructive nature of tst-fchmod-errors
We must not change the permissions of /dev/null if running
as root.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/io/tst-fchmod-errors.c b/io/tst-fchmod-errors.c
index ee15300fc3edf6f0..bf2a4c568e33aeaa 100644
--- a/io/tst-fchmod-errors.c
+++ b/io/tst-fchmod-errors.c
@@ -18,8 +18,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <stdio.h>
#include <support/check.h>
#include <support/xunistd.h>
+#include <unistd.h>
static int
do_test (void)
@@ -27,9 +29,14 @@ do_test (void)
{
/* Permissions on /dev/null (the opened descriptor) cannot be changed. */
int fd = xopen ("/dev/null", O_RDWR, 0);
- errno = 0;
- TEST_COMPARE (fchmod (fd, 0), -1);
- TEST_COMPARE (errno, EPERM);
+ if (getuid () == 0)
+ puts ("info: /dev/null fchmod test skipped because of root privileges");
+ else
+ {
+ errno = 0;
+ TEST_COMPARE (fchmod (fd, 0), -1);
+ TEST_COMPARE (errno, EPERM);
+ }
xclose (fd);
/* Now testing an invalid file descriptor. */

1646
glibc-RHEL-108823-5.patch Normal file

File diff suppressed because it is too large Load Diff

414
glibc-RHEL-108823-6.patch Normal file
View File

@ -0,0 +1,414 @@
commit 3b1d32177635023e37bec7fbfd77c3cfb2659eb1
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 30 21:52:10 2024 +0200
support: Add <support/xdirent.h>
Use static functions for readdir/readdir_r, so that
-D_FILE_OFFSET_BITS=64 does not improperly redirect calls to the wrong
implementation.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/support/Makefile b/support/Makefile
index 23ce2ccec89743bb..2cc0654e86a5e0de 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -73,6 +73,8 @@ libsupport-routines = \
support_quote_blob \
support_quote_blob_wide \
support_quote_string \
+ support_readdir_check \
+ support_readdir_r_check \
support_record_failure \
support_run_diff \
support_select_modifies_timeout \
@@ -115,6 +117,7 @@ libsupport-routines = \
xclock_settime_time64 \
xclone \
xclose \
+ xclosedir \
xconnect \
xcopy_file_range \
xdlfcn \
@@ -122,6 +125,7 @@ libsupport-routines = \
xdup2 \
xfchmod \
xfclose \
+ xfdopendir \
xfgets \
xfopen \
xfork \
@@ -143,6 +147,7 @@ libsupport-routines = \
xmunmap \
xnewlocale \
xopen \
+ xopendir \
xpipe \
xpoll \
xposix_memalign \
@@ -328,6 +333,7 @@ tests = \
tst-test_compare_string \
tst-test_compare_string_wide \
tst-timespec \
+ tst-xdirent \
tst-xreadlink \
tst-xsigstack \
# tests
diff --git a/support/support_readdir_check.c b/support/support_readdir_check.c
new file mode 100644
index 0000000000000000..5687004276f87d93
--- /dev/null
+++ b/support/support_readdir_check.c
@@ -0,0 +1,30 @@
+/* Error-checking helper for xreaddir, xreaddir64.
+ 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 <support/xdirent.h>
+
+#include <support/check.h>
+
+void *
+support_readdir_check (const char *name, void *result, int saved_errno)
+{
+ if (result == NULL && errno != 0)
+ FAIL_EXIT1 ("%s: %m", name);
+ errno = saved_errno;
+ return result;
+}
diff --git a/support/support_readdir_r_check.c b/support/support_readdir_r_check.c
new file mode 100644
index 0000000000000000..6bbb0d0b32fb949e
--- /dev/null
+++ b/support/support_readdir_r_check.c
@@ -0,0 +1,35 @@
+/* Error-checking helper for xreaddir_r, xreaddir64_r.
+ 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 <support/xdirent.h>
+
+#include <support/check.h>
+
+int
+support_readdir_r_check (const char *name, int result, void *buf, void *ptr)
+{
+ if (result != 0)
+ {
+ errno = result;
+ FAIL_EXIT1 ("%s: %m", name);
+ }
+ if (buf != ptr)
+ FAIL_EXIT1 ("%s: buffer pointer and returned pointer differ: %p != %p",
+ name, buf, ptr);
+ return result;
+}
diff --git a/support/tst-xdirent.c b/support/tst-xdirent.c
new file mode 100644
index 0000000000000000..642483165a36765c
--- /dev/null
+++ b/support/tst-xdirent.c
@@ -0,0 +1,76 @@
+/* Compile test for error-checking wrappers for <dirent.h>
+ 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 <support/xdirent.h>
+
+#include <libc-diag.h>
+#include <support/check.h>
+#include <unistd.h>
+
+static int
+do_test (void)
+{
+ {
+ DIR *d = xopendir (".");
+ struct dirent *e = xreaddir (d);
+ /* Assume that the "." special entry always comes first. */
+ TEST_COMPARE_STRING (e->d_name, ".");
+ while (xreaddir (d) != NULL)
+ ;
+ xclosedir (d);
+ }
+
+ {
+ DIR *d = xopendir (".");
+ struct dirent64 *e = xreaddir64 (d);
+ TEST_COMPARE_STRING (e->d_name, ".");
+ while (xreaddir64 (d) != NULL)
+ ;
+ xclosedir (d);
+ }
+
+ /* The functions readdir_r, readdir64_r were deprecated in glibc 2.24. */
+ DIAG_PUSH_NEEDS_COMMENT;
+ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
+
+ {
+ DIR *d = xopendir (".");
+ struct dirent buf = { 0, };
+ TEST_VERIFY (xreaddir_r (d, &buf));
+ TEST_COMPARE_STRING (buf.d_name, ".");
+ while (xreaddir_r (d, &buf))
+ ;
+ xclosedir (d);
+ }
+
+ {
+ DIR *d = xopendir (".");
+ struct dirent64 buf = { 0, };
+ TEST_VERIFY (xreaddir64_r (d, &buf));
+ TEST_COMPARE_STRING (buf.d_name, ".");
+ while (xreaddir64_r (d, &buf))
+ ;
+ xclosedir (d);
+ }
+
+ DIAG_POP_NEEDS_COMMENT;
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/xclosedir.c b/support/xclosedir.c
new file mode 100644
index 0000000000000000..b490df5598e76226
--- /dev/null
+++ b/support/xclosedir.c
@@ -0,0 +1,28 @@
+/* Error-checking wrapper for closedir.
+ 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 <support/xdirent.h>
+
+#include <support/check.h>
+
+void
+xclosedir (DIR *dir)
+{
+ if (closedir (dir) != 0)
+ FAIL_EXIT1 ("closedir: %m");
+}
diff --git a/support/xdirent.h b/support/xdirent.h
new file mode 100644
index 0000000000000000..8465d70ec1ea97d8
--- /dev/null
+++ b/support/xdirent.h
@@ -0,0 +1,86 @@
+/* Error-checking wrappers for <dirent.h>
+ 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/>. */
+
+#ifndef SUPPORT_XDIRENT_H
+#define SUPPORT_XDIRENT_H
+
+#include <dirent.h>
+#include <errno.h>
+#include <libc-diag.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+__BEGIN_DECLS
+
+DIR *xopendir (const char *path);
+DIR *xfdopendir (int fd);
+void xclosedir (DIR *);
+
+void *support_readdir_check (const char *, void *, int);
+
+static __attribute__ ((unused)) struct dirent *
+xreaddir (DIR *stream)
+{
+ int saved_errno = errno;
+ errno = 0;
+ struct dirent *result = readdir (stream);
+ return support_readdir_check ("readdir", result, saved_errno);
+}
+
+static __attribute__ ((unused)) struct dirent64 *
+xreaddir64 (DIR *stream)
+{
+ int saved_errno = errno;
+ errno = 0;
+ struct dirent64 *result = readdir64 (stream);
+ return support_readdir_check ("readdir64", result, saved_errno);
+}
+
+/* The functions readdir_r, readdir64_r were deprecated in glibc 2.24. */
+DIAG_PUSH_NEEDS_COMMENT;
+DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
+
+int support_readdir_r_check (const char *, int, void *, void *);
+
+static __attribute__ ((unused)) bool
+xreaddir_r (DIR *stream, struct dirent *buf)
+{
+ struct dirent *ptr;
+ int ret = readdir_r (stream, buf, &ptr);
+ if (ret == 0 && ptr == NULL)
+ return false;
+ support_readdir_r_check ("readdir_r", ret, buf, ptr);
+ return true;
+}
+
+static __attribute__ ((unused)) bool
+xreaddir64_r (DIR *stream, struct dirent64 *buf)
+{
+ struct dirent64 *ptr;
+ int ret = readdir64_r (stream, buf, &ptr);
+ if (ret == 0 && ptr == NULL)
+ return false;
+ support_readdir_r_check ("readdir64_r", ret, buf, ptr);
+ return true;
+}
+
+DIAG_POP_NEEDS_COMMENT;
+
+__END_DECLS
+
+#endif /* SUPPORT_XDIRENT_H */
diff --git a/support/xfdopendir.c b/support/xfdopendir.c
new file mode 100644
index 0000000000000000..d881d28c738a26a8
--- /dev/null
+++ b/support/xfdopendir.c
@@ -0,0 +1,30 @@
+/* Error-checking wrapper for fdopendir.
+ 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 <support/xdirent.h>
+
+#include <support/check.h>
+
+DIR *
+xfdopendir (int fd)
+{
+ DIR *result = fdopendir (fd);
+ if (result == NULL)
+ FAIL_EXIT1 ("fdopendir (%d): %m", fd);
+ return result;
+}
diff --git a/support/xopendir.c b/support/xopendir.c
new file mode 100644
index 0000000000000000..e4aee07fee074d5e
--- /dev/null
+++ b/support/xopendir.c
@@ -0,0 +1,30 @@
+/* Error-checking wrapper for opendir.
+ 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 <support/xdirent.h>
+
+#include <support/check.h>
+
+DIR *
+xopendir (const char *path)
+{
+ DIR *result = opendir (path);
+ if (result == NULL)
+ FAIL_EXIT1 ("opendir (\"%s\"): %m", path);
+ return result;
+}

1320
glibc-RHEL-108823-7.patch Normal file

File diff suppressed because it is too large Load Diff

141
glibc-RHEL-108823-8.patch Normal file
View File

@ -0,0 +1,141 @@
commit f4ae345810942db891bddf9b482c72b3a120c3b2
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 29 11:06:08 2024 +0200
io: Add tst-lstat-nofollow, tst-lstat-nofollow-time64
They verify that lstat, lstat64 do not follow symbolic links.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/io/Makefile b/io/Makefile
index 8ecd2842e20fef97..ed9d4c3eed5941a3 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -208,6 +208,7 @@ tests := \
tst-lchmod \
tst-linkat \
tst-lockf \
+ tst-lstat-nofollow \
tst-lutimes \
tst-mkdirat \
tst-mkfifoat \
@@ -236,6 +237,7 @@ tests-time64 := \
tst-futimes-time64\
tst-futimesat-time64 \
tst-lchmod-time64 \
+ tst-lstat-nofollow-time64 \
tst-lutimes-time64 \
tst-stat-time64 \
tst-utime-time64 \
diff --git a/io/tst-lstat-nofollow-time64.c b/io/tst-lstat-nofollow-time64.c
new file mode 100644
index 0000000000000000..45feb3f13085ae44
--- /dev/null
+++ b/io/tst-lstat-nofollow-time64.c
@@ -0,0 +1 @@
+#include "tst-lstat-nofollow.c"
diff --git a/io/tst-lstat-nofollow.c b/io/tst-lstat-nofollow.c
new file mode 100644
index 0000000000000000..5bbb557c72938a8a
--- /dev/null
+++ b/io/tst-lstat-nofollow.c
@@ -0,0 +1,98 @@
+/* Test that lstat does not follow symbolic links.
+ 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 <string.h>
+#include <support/check.h>
+#include <support/fuse.h>
+#include <support/support.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void
+fuse_thread (struct support_fuse *f, void *closure)
+{
+ struct fuse_in_header *inh;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+ switch (inh->opcode)
+ {
+ case FUSE_LOOKUP:
+ {
+ TEST_COMPARE (inh->nodeid, 1);
+ TEST_COMPARE_STRING (support_fuse_cast (LOOKUP, inh), "symlink");
+ struct fuse_entry_out *out = support_fuse_prepare_entry (f, 2);
+ out->attr.mode = S_IFLNK | 0777;
+ out->attr.size = strlen ("target");
+ support_fuse_reply_prepared (f);
+ }
+ break;
+ case FUSE_GETATTR:
+ {
+ TEST_COMPARE (inh->nodeid, 2);
+ struct fuse_attr_out *out = support_fuse_prepare_attr (f);
+ out->attr.mode = S_IFLNK | 0777;
+ out->attr.size = strlen ("target");
+ support_fuse_reply_prepared (f);
+ }
+ break;
+ case FUSE_READLINK:
+ /* The lstat operation must not attempt to look at the
+ symbolic link target. */
+ FAIL ("attempt to obtain target of symblic link for node %llu",
+ (unsigned long long int) inh->nodeid);
+ break;
+ default:
+ FAIL ("unexpected event %s", support_fuse_opcode (inh->opcode));
+ }
+ }
+}
+
+static int
+do_test (void)
+{
+ support_fuse_init ();
+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);
+ char *symlink_path = xasprintf ("%s/symlink", support_fuse_mountpoint (f));
+
+ {
+ struct stat st = { 0, };
+ TEST_COMPARE (lstat (symlink_path, &st), 0);
+ TEST_COMPARE (st.st_uid, getuid ());
+ TEST_COMPARE (st.st_gid, getgid ());
+ TEST_COMPARE (st.st_size, 6);
+ TEST_COMPARE (st.st_mode, S_IFLNK | 0777);
+ }
+
+ {
+ struct stat64 st = { 0, };
+ TEST_COMPARE (lstat64 (symlink_path, &st), 0);
+ TEST_COMPARE (st.st_uid, getuid ());
+ TEST_COMPARE (st.st_gid, getgid ());
+ TEST_COMPARE (st.st_size, 6);
+ TEST_COMPARE (st.st_mode, S_IFLNK | 0777);
+ }
+
+ free (symlink_path);
+ support_fuse_unmount (f);
+ return 0;
+}
+
+#include <support/test-driver.c>

143
glibc-RHEL-108823-9.patch Normal file
View File

@ -0,0 +1,143 @@
commit 43669fcf7315f494bbbc2c040cedeb0fa8416a5f
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Aug 22 11:02:51 2024 +0200
io: Add FUSE-based test for fchmod
Test all mode arguments, and that extra bits are ignored
as required by POSIX.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/io/Makefile b/io/Makefile
index ed9d4c3eed5941a3..085c7db66a406793 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -189,6 +189,7 @@ tests := \
tst-copy_file_range \
tst-faccessat \
tst-fchmod-errors \
+ tst-fchmod-fuse \
tst-fchmodat \
tst-fchownat \
tst-fcntl \
diff --git a/io/tst-fchmod-fuse.c b/io/tst-fchmod-fuse.c
new file mode 100644
index 0000000000000000..fbd3309963491105
--- /dev/null
+++ b/io/tst-fchmod-fuse.c
@@ -0,0 +1,114 @@
+/* FUSE-based test for fchmod.
+ 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 <support/fuse.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+
+/* Set from do_test to indicate the expected incoming mode change request. */
+static _Atomic int expected_mode;
+
+static void
+fuse_thread (struct support_fuse *f, void *closure)
+{
+ struct fuse_in_header *inh;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+ switch (inh->opcode)
+ {
+ case FUSE_LOOKUP:
+ {
+ char *name = support_fuse_cast (LOOKUP, inh);
+ TEST_COMPARE_STRING (name, "file");
+ struct fuse_entry_out *out
+ = support_fuse_prepare_entry (f, 2);
+ out->attr.mode = S_IFREG | 0600;
+ support_fuse_reply_prepared (f);
+ }
+ break;
+ case FUSE_OPEN:
+ {
+ TEST_COMPARE (inh->nodeid, 2);
+ struct fuse_open_in *p = support_fuse_cast (OPEN, inh);
+ TEST_COMPARE (p->flags & O_ACCMODE, O_RDWR);
+ struct fuse_open_out out = { 0, };
+ support_fuse_reply (f, &out, sizeof (out));
+ }
+ break;
+ case FUSE_SETATTR:
+ {
+ TEST_COMPARE (inh->nodeid, 2);
+ struct fuse_setattr_in *p = support_fuse_cast (SETATTR, inh);
+ TEST_COMPARE (p->valid , FATTR_MODE);
+ TEST_COMPARE (p->mode, S_IFREG | expected_mode);
+ struct fuse_attr_out *out = support_fuse_prepare_attr (f);
+ out->attr.mode = S_IFREG | p->mode;
+ support_fuse_reply_prepared (f);
+ }
+ break;
+ case FUSE_FLUSH:
+ support_fuse_reply_empty (f);
+ break;
+ default:
+ support_fuse_reply_error (f, EIO);
+ }
+ }
+}
+
+/* Test all mode values with the specified extra bits. */
+static void
+test_with_bits (int fd, unsigned int extra_bits)
+{
+ for (int do_mode = 0; do_mode <= 07777; ++do_mode)
+ {
+ expected_mode = do_mode;
+ TEST_COMPARE (fchmod (fd, extra_bits | do_mode), 0);
+ }
+}
+
+static int
+do_test (void)
+{
+ support_fuse_init ();
+
+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);
+ char *path = xasprintf ("%s/file", support_fuse_mountpoint (f));
+ int fd = xopen (path, O_RDWR, 0600);
+ free (path);
+
+ test_with_bits (fd, 0);
+ /* POSIX requires that the extra bits are ignored. */
+ test_with_bits (fd, S_IFREG);
+ test_with_bits (fd, S_IFDIR);
+ test_with_bits (fd, ~07777);
+
+ xclose (fd);
+ support_fuse_unmount (f);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -147,7 +147,7 @@ Version: %{glibcversion}
# - It allows using the Release number without the %%dist tag in the dependency
# generator to make the generated requires interchangeable between Rawhide
# and ELN (.elnYY < .fcXX).
%global baserelease 55
%global baserelease 56
Release: %{baserelease}%{?dist}
# Licenses:
@ -685,6 +685,20 @@ Patch359: glibc-RHEL-108974-31.patch
Patch360: glibc-RHEL-108974-32.patch
Patch361: glibc-RHEL-108974-33.patch
Patch362: glibc-RHEL-108974-34.patch
Patch363: glibc-RHEL-108823-1.patch
Patch364: glibc-RHEL-108823-2.patch
Patch365: glibc-RHEL-108823-3.patch
Patch366: glibc-RHEL-108823-4.patch
Patch367: glibc-RHEL-108823-5.patch
Patch368: glibc-RHEL-108823-6.patch
Patch369: glibc-RHEL-108823-7.patch
Patch370: glibc-RHEL-108823-8.patch
Patch371: glibc-RHEL-108823-9.patch
Patch372: glibc-RHEL-108823-10.patch
Patch373: glibc-RHEL-108823-11.patch
Patch374: glibc-RHEL-108823-12.patch
Patch375: glibc-RHEL-108823-13.patch
Patch376: glibc-RHEL-108823-14.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -2698,6 +2712,9 @@ update_gconv_modules_cache ()
%endif
%changelog
* Tue Aug 19 2025 Arjun Shankar <arjun@redhat.com> - 2.39-56
- Add FUSE based tests for fchmod, lstat, and mkstemp (RHEL-108823)
* Wed Aug 13 2025 Arjun Shankar <arjun@redhat.com> - 2.39-55
- Various updates to the manual from upstream (RHEL-108974)