1 /* Test for reading directories with getdents64.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
27 #include <support/check.h>
28 #include <support/support.h>
29 #include <support/xunistd.h>
33 /* Called by large_buffer_checks below. */
35 large_buffer_check (int fd
, char *large_buffer
, size_t large_buffer_size
)
37 xlseek (fd
, 0, SEEK_SET
);
38 ssize_t ret
= getdents64 (fd
, large_buffer
, large_buffer_size
);
40 FAIL_EXIT1 ("getdents64 for buffer of %zu bytes failed: %m",
42 if (ret
< offsetof (struct dirent64
, d_name
))
43 FAIL_EXIT1 ("getdents64 for buffer of %zu returned small value %zd",
44 large_buffer_size
, ret
);
47 /* Bug 24740: Make sure that the system call argument is adjusted
48 properly for the int type. A large value should stay a large
49 value, and not wrap around to something small, causing the system
50 call to fail with EINVAL. */
52 large_buffer_checks (int fd
)
54 size_t large_buffer_size
;
55 if (!__builtin_add_overflow (UINT_MAX
, 2, &large_buffer_size
))
57 int flags
= MAP_ANONYMOUS
| MAP_PRIVATE
;
59 flags
|= MAP_NORESERVE
;
61 void *large_buffer
= mmap (NULL
, large_buffer_size
,
62 PROT_READ
| PROT_WRITE
, flags
, -1, 0);
63 if (large_buffer
== MAP_FAILED
)
64 printf ("warning: could not allocate %zu bytes of memory,"
65 " subtests skipped\n", large_buffer_size
);
68 large_buffer_check (fd
, large_buffer
, INT_MAX
);
69 large_buffer_check (fd
, large_buffer
, (size_t) INT_MAX
+ 1);
70 large_buffer_check (fd
, large_buffer
, (size_t) INT_MAX
+ 2);
71 large_buffer_check (fd
, large_buffer
, UINT_MAX
);
72 large_buffer_check (fd
, large_buffer
, (size_t) UINT_MAX
+ 1);
73 large_buffer_check (fd
, large_buffer
, (size_t) UINT_MAX
+ 2);
74 xmunmap (large_buffer
, large_buffer_size
);
80 do_test_large_size (void)
82 int fd
= xopen (".", O_RDONLY
| O_DIRECTORY
, 0);
83 TEST_VERIFY (fd
>= 0);
84 large_buffer_checks (fd
);
90 do_test_by_size (size_t buffer_size
)
92 /* The test compares the iteration order with readdir64. */
93 DIR *reference
= opendir (".");
94 TEST_VERIFY_EXIT (reference
!= NULL
);
96 int fd
= xopen (".", O_RDONLY
| O_DIRECTORY
, 0);
97 TEST_VERIFY (fd
>= 0);
99 /* Perform two passes, with a rewind operating between passes. */
100 for (int pass
= 0; pass
< 2; ++pass
)
102 /* Check that we need to fill the buffer multiple times. */
107 /* Simple way to make sure that the memcpy below does not read
108 non-existing data. */
111 char buffer
[buffer_size
];
115 ssize_t ret
= getdents64 (fd
, &data
.buffer
, sizeof (data
.buffer
));
117 FAIL_EXIT1 ("getdents64: %m");
122 char *current
= data
.buffer
;
123 char *end
= data
.buffer
+ ret
;
124 while (current
!= end
)
126 struct dirent64 entry
;
127 memcpy (&entry
, current
, sizeof (entry
));
128 /* Truncate overlong strings. */
129 entry
.d_name
[sizeof (entry
.d_name
) - 1] = '\0';
130 TEST_VERIFY (strlen (entry
.d_name
) < sizeof (entry
.d_name
) - 1);
133 struct dirent64
*refentry
= readdir64 (reference
);
134 if (refentry
== NULL
&& errno
== 0)
135 FAIL_EXIT1 ("readdir64 failed too early, at: %s",
137 else if (refentry
== NULL
)
138 FAIL_EXIT1 ("readdir64: %m");
140 TEST_COMPARE_STRING (entry
.d_name
, refentry
->d_name
);
141 TEST_COMPARE (entry
.d_ino
, refentry
->d_ino
);
142 TEST_COMPARE (entry
.d_off
, refentry
->d_off
);
143 TEST_COMPARE (entry
.d_type
, refentry
->d_type
);
145 /* Offset zero is reserved for the first entry. */
146 TEST_VERIFY (entry
.d_off
!= 0);
148 TEST_VERIFY_EXIT (entry
.d_reclen
<= end
- current
);
149 current
+= entry
.d_reclen
;
153 /* We expect to have reached the end of the stream. */
155 TEST_VERIFY (readdir64 (reference
) == NULL
);
156 TEST_COMPARE (errno
, 0);
158 /* direntries_read has been called more than once. */
159 TEST_VERIFY (read_count
> 0);
161 /* Rewind both directory streams. */
162 xlseek (fd
, 0, SEEK_SET
);
163 rewinddir (reference
);
167 closedir (reference
);
173 do_test_by_size (512);
174 do_test_by_size (1024);
175 do_test_by_size (4096);
177 do_test_large_size ();
182 #include <support/test-driver.c>