Backport test implementation to verify rewinddir behavior

Resolves: RHEL-56540
This commit is contained in:
Frédéric Bérat 2025-01-08 17:20:39 +01:00
parent 6818485a9d
commit b339cb6eb4
4 changed files with 806 additions and 0 deletions

526
glibc-RHEL-56540-1.patch Normal file
View File

@ -0,0 +1,526 @@
commit 1251e9ea49fba9f53bbf4f290f3db90c01931fa7
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Sep 12 09:40:25 2024 +0200
support: Add <support/readdir.h>
It allows to read directories using the six readdir variants
without writing type-specific code or using skeleton files
that are compiled four times.
The readdir_r subtest for support_readdir_expect_error revealed
bug 32124.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/support/Makefile b/support/Makefile
index c7e03b76cc64c805..3b8509c88db4662a 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -77,6 +77,7 @@ libsupport-routines = \
support_quote_blob \
support_quote_blob_wide \
support_quote_string \
+ support_readdir \
support_readdir_check \
support_readdir_r_check \
support_record_failure \
@@ -326,6 +327,7 @@ tests = \
tst-support_quote_blob \
tst-support_quote_blob_wide \
tst-support_quote_string \
+ tst-support_readdir \
tst-support_record_failure \
tst-test_compare \
tst-test_compare_blob \
diff --git a/support/readdir.h b/support/readdir.h
new file mode 100644
index 0000000000000000..7d7c7650d42efb70
--- /dev/null
+++ b/support/readdir.h
@@ -0,0 +1,85 @@
+/* Type-generic wrapper for readdir functions.
+ 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_READDIR_H
+#define SUPPORT_READDIR_H
+
+#include <dirent.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/* Definition independent of _FILE_OFFSET_BITS. */
+struct support_dirent
+{
+ uint64_t d_ino;
+ uint64_t d_off; /* 0 if d_off is not supported. */
+ uint32_t d_type;
+ char *d_name;
+};
+
+/* Operation to be performed by support_readdir below. */
+enum support_readdir_op
+ {
+ SUPPORT_READDIR,
+ SUPPORT_READDIR64,
+ SUPPORT_READDIR_R,
+ SUPPORT_READDIR64_R,
+ SUPPORT_READDIR64_COMPAT,
+ SUPPORT_READDIR64_R_COMPAT,
+ };
+
+/* Returns the last supported function. May exclude
+ SUPPORT_READDIR64_R_COMPAT if not implemented. */
+enum support_readdir_op support_readdir_op_last (void);
+
+/* Returns the name of the function that corresponds to the OP constant. */
+const char *support_readdir_function (enum support_readdir_op op);
+
+/* Returns the d_ino field width for OP, in bits. */
+unsigned int support_readdir_inode_width (enum support_readdir_op op);
+
+/* Returns the d_off field width for OP, in bits. Zero if not present. */
+unsigned int support_readdir_offset_width (enum support_readdir_op op);
+
+/* Returns true if OP is an _r variant with name length restrictions. */
+bool support_readdir_r_variant (enum support_readdir_op op);
+
+/* First, free E->d_name and set the field to NULL. Then call the
+ readdir variant as specified by OP. If successfully, copy fields
+ to E, make a copy of the entry name using strdup, and write its
+ addres sto E->d_name.
+
+ Return true if an entry was read, or false if the end of the
+ directory stream was reached. Terminates the process upon error.
+ The caller is expected to free E->d_name if the function is not
+ called again for this E.
+
+ Note that this function assumes that E->d_name has been initialized
+ to NULL or has been allocated by a previous call to this function. */
+bool support_readdir (DIR *stream, enum support_readdir_op op,
+ struct support_dirent *e) __nonnull ((1, 3));
+
+/* Checks that the readdir operation OP fails with errno value EXPECTED. */
+void support_readdir_expect_error (DIR *stream, enum support_readdir_op op,
+ int expected) __nonnull ((1));
+
+__END_DECLS
+
+#endif /* SUPPORT_READDIR_H */
diff --git a/support/support_readdir.c b/support/support_readdir.c
new file mode 100644
index 0000000000000000..10d808416f7a0456
--- /dev/null
+++ b/support/support_readdir.c
@@ -0,0 +1,318 @@
+/* Type-generic wrapper for readdir functions.
+ 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/readdir.h>
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xdirent.h>
+
+/* Copied from <olddirent.h>. */
+struct __old_dirent64
+ {
+ __ino_t d_ino;
+ __off64_t d_off;
+ unsigned short int d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+ };
+
+static struct __old_dirent64 *(*readdir64_compat) (DIR *);
+static int (*readdir64_r_compat) (DIR *, struct __old_dirent64 *,
+ struct __old_dirent64 **);
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ /* These compat symbols exists on alpha, i386, m67k , powerpc, s390,
+ sparc. at the same GLIBC_2.1 version. */
+ readdir64_compat = dlvsym (RTLD_DEFAULT, "readdir64", "GLIBC_2.1");
+ readdir64_r_compat = dlvsym (RTLD_DEFAULT, "readdir64_r", "GLIBC_2.1");
+}
+
+enum support_readdir_op
+support_readdir_op_last (void)
+{
+ if (readdir64_r_compat != NULL)
+ {
+ TEST_VERIFY (readdir64_compat != NULL);
+ return SUPPORT_READDIR64_R_COMPAT;
+ }
+ else
+ return SUPPORT_READDIR64_R;
+}
+
+const char *
+support_readdir_function (enum support_readdir_op op)
+{
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ return "readdir";
+ case SUPPORT_READDIR64:
+ return "readdir64";
+ case SUPPORT_READDIR_R:
+ return "readdir_r";
+ case SUPPORT_READDIR64_R:
+ return "readdir64_r";
+ case SUPPORT_READDIR64_COMPAT:
+ return "readdir64@GBLIC_2.1";
+ case SUPPORT_READDIR64_R_COMPAT:
+ return "readdir64_r@GBLIC_2.1";
+ }
+ FAIL_EXIT1 ("invalid support_readdir_op constant: %d", op);
+}
+
+unsigned int
+support_readdir_inode_width (enum support_readdir_op op)
+{
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ case SUPPORT_READDIR_R:
+ return sizeof ((struct dirent) { 0, }.d_ino) * 8;
+ case SUPPORT_READDIR64:
+ case SUPPORT_READDIR64_R:
+ return sizeof ((struct dirent64) { 0, }.d_ino) * 8;
+ case SUPPORT_READDIR64_COMPAT:
+ case SUPPORT_READDIR64_R_COMPAT:
+ return sizeof ((struct __old_dirent64) { 0, }.d_ino) * 8;
+ }
+ FAIL_EXIT1 ("invalid support_readdir_op constant: %d", op);
+}
+
+unsigned int
+support_readdir_offset_width (enum support_readdir_op op)
+{
+#ifdef _DIRENT_HAVE_D_OFF
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ case SUPPORT_READDIR_R:
+ return sizeof ((struct dirent) { 0, }.d_off) * 8;
+ case SUPPORT_READDIR64:
+ case SUPPORT_READDIR64_R:
+ return sizeof ((struct dirent64) { 0, }.d_off) * 8;
+ case SUPPORT_READDIR64_COMPAT:
+ case SUPPORT_READDIR64_R_COMPAT:
+ return sizeof ((struct __old_dirent64) { 0, }.d_off) * 8;
+ }
+#else
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ case SUPPORT_READDIR_R:
+ case SUPPORT_READDIR64:
+ case SUPPORT_READDIR64_R:
+ case SUPPORT_READDIR64_COMPAT:
+ case SUPPORT_READDIR64_R_COMPAT:
+ return 0;
+ }
+#endif
+ FAIL_EXIT1 ("invalid support_readdir_op constant: %d", op);
+}
+
+bool
+support_readdir_r_variant (enum support_readdir_op op)
+{
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ case SUPPORT_READDIR64:
+ case SUPPORT_READDIR64_COMPAT:
+ return false;
+ case SUPPORT_READDIR_R:
+ case SUPPORT_READDIR64_R:
+ case SUPPORT_READDIR64_R_COMPAT:
+ return true;
+ }
+ FAIL_EXIT1 ("invalid support_readdir_op constant: %d", op);
+}
+
+static bool
+copy_dirent (struct support_dirent *dst, struct dirent *src)
+{
+ if (src == NULL)
+ return false;
+ dst->d_ino = src->d_ino;
+#ifdef _DIRENT_HAVE_D_OFF
+ dst->d_off = src->d_off;
+#else
+ dst->d_off = 0;
+#endif
+ dst->d_type = src->d_type;
+ dst->d_name = xstrdup (src->d_name);
+ return true;
+}
+
+static bool
+copy_dirent64 (struct support_dirent *dst, struct dirent64 *src)
+{
+ if (src == NULL)
+ return false;
+ dst->d_ino = src->d_ino;
+#ifdef _DIRENT_HAVE_D_OFF
+ dst->d_off = src->d_off;
+#else
+ dst->d_off = 0;
+#endif
+ dst->d_type = src->d_type;
+ dst->d_name = xstrdup (src->d_name);
+ return true;
+}
+
+static bool
+copy_old_dirent64 (struct support_dirent *dst, struct __old_dirent64 *src)
+{
+ if (src == NULL)
+ return false;
+ dst->d_ino = src->d_ino;
+#ifdef _DIRENT_HAVE_D_OFF
+ dst->d_off = src->d_off;
+#else
+ dst->d_off = 0;
+#endif
+ dst->d_type = src->d_type;
+ dst->d_name = xstrdup (src->d_name);
+ return true;
+}
+
+bool
+support_readdir (DIR *stream, enum support_readdir_op op,
+ struct support_dirent *e)
+{
+ free (e->d_name);
+ e->d_name = NULL;
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ return copy_dirent (e, xreaddir (stream));
+ case SUPPORT_READDIR64:
+ return copy_dirent64 (e, xreaddir64 (stream));
+
+ /* The functions readdir_r, readdir64_r were deprecated in glibc 2.24. */
+ DIAG_PUSH_NEEDS_COMMENT;
+ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
+
+ case SUPPORT_READDIR_R:
+ {
+ struct dirent buf;
+ if (!xreaddir_r (stream, &buf))
+ return false;
+ return copy_dirent (e, &buf);
+ }
+ case SUPPORT_READDIR64_R:
+ {
+ struct dirent64 buf;
+ if (!xreaddir64_r (stream, &buf))
+ return false;
+ return copy_dirent64 (e, &buf);
+ }
+
+ DIAG_POP_NEEDS_COMMENT;
+
+ case SUPPORT_READDIR64_COMPAT:
+ if (readdir64_compat == NULL)
+ FAIL_EXIT1 ("readdir64 compat function not implemented");
+ return copy_old_dirent64 (e, readdir64_compat (stream));
+
+ case SUPPORT_READDIR64_R_COMPAT:
+ {
+ if (readdir64_r_compat == NULL)
+ FAIL_EXIT1 ("readdir64_r compat function not implemented");
+ struct __old_dirent64 buf;
+ struct __old_dirent64 *e1;
+ int ret = readdir64_r_compat (stream, &buf, &e1);
+ if (ret != 0)
+ {
+ errno = ret;
+ FAIL ("readdir64_r@GLIBC_2.1: %m");
+ return false;
+ }
+ if (e1 == NULL)
+ return false;
+ return copy_old_dirent64 (e, e1);
+ }
+ }
+ FAIL_EXIT1 ("support_readdir: invalid op argument %d", (int) op);
+}
+
+void
+support_readdir_expect_error (DIR *stream, enum support_readdir_op op,
+ int expected)
+{
+ switch (op)
+ {
+ case SUPPORT_READDIR:
+ errno = 0;
+ TEST_VERIFY (readdir (stream) == NULL);
+ TEST_COMPARE (errno, expected);
+ return;
+ case SUPPORT_READDIR64:
+ errno = 0;
+ TEST_VERIFY (readdir64 (stream) == NULL);
+ TEST_COMPARE (errno, expected);
+ return;
+
+ /* The functions readdir_r, readdir64_r were deprecated in glibc 2.24. */
+ DIAG_PUSH_NEEDS_COMMENT;
+ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
+
+ case SUPPORT_READDIR_R:
+ {
+ struct dirent buf;
+ struct dirent *e;
+ errno = readdir_r (stream, &buf, &e);
+ TEST_COMPARE (errno, expected);;
+ }
+ return;
+ case SUPPORT_READDIR64_R:
+ {
+ struct dirent64 buf;
+ struct dirent64 *e;
+ errno = readdir64_r (stream, &buf, &e);
+ TEST_COMPARE (errno, expected);;
+ }
+ return;
+
+ DIAG_POP_NEEDS_COMMENT;
+
+ case SUPPORT_READDIR64_COMPAT:
+ if (readdir64_compat == NULL)
+ FAIL_EXIT1 ("readdir64_r compat function not implemented");
+ errno = 0;
+ TEST_VERIFY (readdir64_compat (stream) == NULL);
+ TEST_COMPARE (errno, expected);
+ return;
+ case SUPPORT_READDIR64_R_COMPAT:
+ {
+ if (readdir64_r_compat == NULL)
+ FAIL_EXIT1 ("readdir64_r compat function not implemented");
+ struct __old_dirent64 buf;
+ struct __old_dirent64 *e;
+ errno = readdir64_r_compat (stream, &buf, &e);
+ TEST_COMPARE (errno, expected);
+ }
+ return;
+ }
+ FAIL_EXIT1 ("support_readdir_expect_error: invalid op argument %d",
+ (int) op);
+}
diff --git a/support/tst-support_readdir.c b/support/tst-support_readdir.c
new file mode 100644
index 0000000000000000..c0639571c7c3f516
--- /dev/null
+++ b/support/tst-support_readdir.c
@@ -0,0 +1,70 @@
+/* Test the support_readdir function.
+ 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/readdir.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xdirent.h>
+#include <support/xunistd.h>
+
+static int
+do_test (void)
+{
+ DIR *reference_stream = xopendir (".");
+ struct dirent64 *reference = xreaddir64 (reference_stream);
+
+ for (enum support_readdir_op op = 0; op <= support_readdir_op_last (); ++op)
+ {
+ DIR *stream = xopendir (".");
+ struct support_dirent e;
+ memset (&e, 0xcc, sizeof (e));
+ e.d_name = NULL;
+ TEST_VERIFY (support_readdir (stream, op, &e));
+ TEST_COMPARE (e.d_ino, reference->d_ino);
+ if (support_readdir_offset_width (op) != 0)
+ TEST_COMPARE (e.d_off, reference->d_off);
+ else
+ TEST_COMPARE (e.d_off, 0);
+ TEST_COMPARE (e.d_type, reference->d_type);
+ TEST_COMPARE_STRING (e.d_name, reference->d_name);
+ free (e.d_name);
+ xclosedir (stream);
+ }
+
+ xclosedir (reference_stream);
+
+ /* Error injection test. */
+ int devnull = xopen ("/dev/null", O_RDONLY, 0);
+ for (enum support_readdir_op op = 0; op <= support_readdir_op_last (); ++op)
+ {
+ DIR *stream = xopendir (".");
+ /* A descriptor incompatible with readdir. */
+ xdup2 (devnull, dirfd (stream));
+ errno = -1;
+ support_readdir_expect_error (stream, op, ENOTDIR);
+ xclosedir (stream);
+ }
+ xclose (devnull);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

34
glibc-RHEL-56540-2.patch Normal file
View File

@ -0,0 +1,34 @@
commit c9154cad66aa0b11ede62cc9190d3485c5ef6941
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Sep 12 18:26:04 2024 +0200
support: Fix Hurd build of tst-support_readdir
Check for the availability of the d_off member at compile time, not
run time.
Fixes commit 1251e9ea49fba9f53bbf4f290f3db90c01931fa7
("support: Add <support/readdir.h>").
diff --git a/support/tst-support_readdir.c b/support/tst-support_readdir.c
index c0639571c7c3f516..66be94fa802e727a 100644
--- a/support/tst-support_readdir.c
+++ b/support/tst-support_readdir.c
@@ -39,10 +39,13 @@ do_test (void)
e.d_name = NULL;
TEST_VERIFY (support_readdir (stream, op, &e));
TEST_COMPARE (e.d_ino, reference->d_ino);
- if (support_readdir_offset_width (op) != 0)
- TEST_COMPARE (e.d_off, reference->d_off);
- else
- TEST_COMPARE (e.d_off, 0);
+#ifdef _DIRENT_HAVE_D_OFF
+ TEST_VERIFY (support_readdir_offset_width (op) != 0);
+ TEST_COMPARE (e.d_off, reference->d_off);
+#else
+ TEST_COMPARE (support_readdir_offset_width (op), 0);
+ TEST_COMPARE (e.d_off, 0);
+#endif
TEST_COMPARE (e.d_type, reference->d_type);
TEST_COMPARE_STRING (e.d_name, reference->d_name);
free (e.d_name);

241
glibc-RHEL-56540-3.patch Normal file
View File

@ -0,0 +1,241 @@
commit 6aa1645f669322b36bda8e1fded6fd524d3e08ff
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Sep 21 19:32:34 2024 +0200
dirent: Add tst-rewinddir
It verifies that rewinddir allows restarting the directory
iteration.
Reviewed-by: DJ Delorie <dj@redhat.com>
Conflicts:
dirent/Makefile (new test added)
diff --git a/dirent/Makefile b/dirent/Makefile
index afc7226a5bf012d6..5bad3c112209a2ce 100644
--- a/dirent/Makefile
+++ b/dirent/Makefile
@@ -31,7 +31,8 @@ routines := opendir closedir readdir readdir_r rewinddir \
scandir-cancel scandir-tail scandir64-tail
tests := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \
- tst-fdopendir2 tst-scandir tst-scandir64
+ tst-fdopendir2 tst-scandir tst-scandir64 \
+ tst-rewinddir \
CFLAGS-scandir.c += $(uses-callbacks)
CFLAGS-scandir64.c += $(uses-callbacks)
diff --git a/dirent/tst-rewinddir.c b/dirent/tst-rewinddir.c
new file mode 100644
index 0000000000000000..1479766ebe8fc911
--- /dev/null
+++ b/dirent/tst-rewinddir.c
@@ -0,0 +1,207 @@
+/* Test for rewinddir, using FUSE.
+ 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 <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/fuse.h>
+#include <support/readdir.h>
+#include <support/support.h>
+#include <support/xdirent.h>
+
+/* Return the file name at the indicated directory offset. */
+static char *
+name_at_offset (unsigned int offset)
+{
+ if (offset <= 1)
+ return xstrdup (".." + !offset); /* "." or "..". */
+ else
+ /* Pad the name with a lot of zeros, so that the dirent buffer gets
+ filled more quickly. */
+ return xasprintf ("file%0240u", offset);
+}
+
+/* This many directory entries, including "." and "..". */
+enum { directory_entries = 200 };
+
+/* Add the directory entry at OFFSET to the stream D. */
+static uint64_t
+add_directory_entry (struct support_fuse_dirstream *d, uint64_t offset)
+{
+ if (offset >= directory_entries)
+ return 0;
+
+ char *name = name_at_offset (offset);
+ uint64_t ino = 1000 + offset; /* Arbitrary value, distinct from 1. */
+ uint32_t type = DT_REG;
+ if (offset <= 1)
+ {
+ type = DT_DIR;
+ ino = 1;
+ }
+
+ ++offset;
+ bool added = support_fuse_dirstream_add (d, ino, offset, type, name);
+ free (name);
+ if (added)
+ return offset;
+ else
+ return 0;
+}
+
+/* Set to true if getdents64 should produce only one entry. */
+static bool one_entry_per_getdents64;
+
+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_READDIR:
+ if (inh->nodeid == 1)
+ {
+ uint64_t offset = support_fuse_cast (READ, inh)->offset;
+ struct support_fuse_dirstream *d
+ = support_fuse_prepare_readdir (f);
+ while (true)
+ {
+ offset = add_directory_entry (d, offset);
+ if (offset == 0 || one_entry_per_getdents64)
+ break;
+ }
+ support_fuse_reply_prepared (f);
+ }
+ else
+ support_fuse_reply_error (f, EIO);
+ break;
+ default:
+ FAIL ("unexpected event %s", support_fuse_opcode (inh->opcode));
+ support_fuse_reply_error (f, EIO);
+ }
+ }
+}
+
+/* Lists the entire directory from start to end. */
+static void
+verify_directory (DIR *dir, enum support_readdir_op op)
+{
+ struct support_dirent e = { 0, };
+
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, ".");
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, "..");
+ for (int i = 2; i < directory_entries; ++i)
+ {
+ char *expected = name_at_offset (i);
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, expected);
+ free (expected);
+ }
+ TEST_VERIFY (!support_readdir (dir, op, &e));
+ free (e.d_name);
+}
+
+/* Run tests with rewinding after ENTRIES readdir calls. */
+static void
+rewind_after (unsigned int rewind_at)
+{
+ for (enum support_readdir_op op = 0; op <= support_readdir_op_last (); ++op)
+ {
+ printf ("info: testing %s (rewind_at=%u)\n",
+ support_readdir_function (op), rewind_at);
+
+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);
+ DIR *dir = xopendir (support_fuse_mountpoint (f));
+ struct support_dirent e = { 0, };
+
+ switch (rewind_at)
+ {
+ case 0:
+ break;
+ case 1:
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, ".");
+ break;
+ default:
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, ".");
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, "..");
+ for (int i = 2; i < directory_entries; ++i)
+ {
+ if (i == rewind_at)
+ break;
+ char *expected = name_at_offset (i);
+ TEST_VERIFY (support_readdir (dir, op, &e));
+ TEST_COMPARE_STRING (e.d_name, expected);
+ free (expected);
+ }
+ break;
+ }
+
+ errno = 0;
+ rewinddir (dir);
+ TEST_COMPARE (errno, 0);
+ verify_directory (dir, op);
+
+ free (e.d_name);
+ xclosedir (dir);
+ support_fuse_unmount (f);
+ }
+}
+
+static int
+do_test (void)
+{
+ support_fuse_init ();
+
+ /* One pass without rewinding to verify that the generated directory
+ content matches expectations. */
+ {
+ struct support_fuse *f = support_fuse_mount (fuse_thread, NULL);
+ DIR *dir = xopendir (support_fuse_mountpoint (f));
+ verify_directory (dir, SUPPORT_READDIR64);
+ xclosedir (dir);
+ support_fuse_unmount (f);
+ }
+
+ for (int do_unbuffered = 0; do_unbuffered < 2; ++do_unbuffered)
+ {
+ one_entry_per_getdents64 = do_unbuffered;
+
+ for (int i = 0; i < 20; ++i)
+ rewind_after (i);
+ rewind_after (50);
+ rewind_after (100);
+ rewind_after (150);
+ rewind_after (180);
+ rewind_after (199);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -1039,6 +1039,9 @@ Patch731: glibc-RHEL-54250.patch
Patch732: glibc-RHEL-56743.patch
Patch733: glibc-RHEL-57586.patch
Patch734: glibc-RHEL-56539.patch
Patch735: glibc-RHEL-56540-1.patch
Patch736: glibc-RHEL-56540-2.patch
Patch737: glibc-RHEL-56540-3.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -3041,6 +3044,8 @@ update_gconv_modules_cache ()
- Backport: cover the documentation coverage gap adding thread safety
annotations for clock_gettime and clock_getres (RHEL-57586)
- Backport test implementation to verify lstat behavior (RHEL-56539)
- Backport test implementation to verify rewinddir behavior
(RHEL-56540)
* Thu Dec 19 2024 DJ Delorie <dj@redhat.com> - 2.34-148
- Increase ungetc test coverage, guarantee single char pushback (RHEL-46738)