1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 // UNSUPPORTED: no-filesystem
11 // UNSUPPORTED: availability-filesystem-missing
13 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
14 // chmod+rm because chmod is too broken.
15 // XFAIL: LIBCXX-ANDROID-FIXME && android-device-api={{21|22}}
19 // class recursive_directory_iterator
21 // recursive_directory_iterator& operator++();
22 // recursive_directory_iterator& increment(error_code& ec) noexcept;
24 #include "filesystem_include.h"
25 #include <type_traits>
29 #include "assert_macros.h"
30 #include "test_macros.h"
31 #include "filesystem_test_helper.h"
35 static void test_increment_signatures()
37 recursive_directory_iterator d
; ((void)d
);
38 std::error_code ec
; ((void)ec
);
40 ASSERT_SAME_TYPE(decltype(++d
), recursive_directory_iterator
&);
41 ASSERT_NOT_NOEXCEPT(++d
);
43 ASSERT_SAME_TYPE(decltype(d
.increment(ec
)), recursive_directory_iterator
&);
44 ASSERT_NOT_NOEXCEPT(d
.increment(ec
));
47 static void test_prefix_increment()
49 static_test_env static_env
;
50 const path testDir
= static_env
.Dir
;
51 const std::set
<path
> dir_contents(static_env
.RecDirIterationList
.begin(),
52 static_env
.RecDirIterationList
.end());
53 const recursive_directory_iterator endIt
{};
56 recursive_directory_iterator
it(testDir
, ec
);
59 std::set
<path
> unseen_entries
= dir_contents
;
60 while (!unseen_entries
.empty()) {
62 const path entry
= *it
;
63 assert(unseen_entries
.erase(entry
) == 1);
64 recursive_directory_iterator
& it_ref
= ++it
;
65 assert(&it_ref
== &it
);
71 static void test_postfix_increment()
73 static_test_env static_env
;
74 const path testDir
= static_env
.Dir
;
75 const std::set
<path
> dir_contents(static_env
.RecDirIterationList
.begin(),
76 static_env
.RecDirIterationList
.end());
77 const recursive_directory_iterator endIt
{};
80 recursive_directory_iterator
it(testDir
, ec
);
83 std::set
<path
> unseen_entries
= dir_contents
;
84 while (!unseen_entries
.empty()) {
86 const path entry
= *it
;
87 assert(unseen_entries
.erase(entry
) == 1);
88 const path entry2
= *it
++;
89 assert(entry2
== entry
);
95 static void test_increment_method()
97 static_test_env static_env
;
98 const path testDir
= static_env
.Dir
;
99 const std::set
<path
> dir_contents(static_env
.RecDirIterationList
.begin(),
100 static_env
.RecDirIterationList
.end());
101 const recursive_directory_iterator endIt
{};
104 recursive_directory_iterator
it(testDir
, ec
);
107 std::set
<path
> unseen_entries
= dir_contents
;
108 while (!unseen_entries
.empty()) {
110 const path entry
= *it
;
111 assert(unseen_entries
.erase(entry
) == 1);
112 recursive_directory_iterator
& it_ref
= it
.increment(ec
);
114 assert(&it_ref
== &it
);
120 static void test_follow_symlinks()
122 static_test_env static_env
;
123 const path testDir
= static_env
.Dir
;
124 auto const& IterList
= static_env
.RecDirFollowSymlinksIterationList
;
126 const std::set
<path
> dir_contents(IterList
.begin(), IterList
.end());
127 const recursive_directory_iterator endIt
{};
130 recursive_directory_iterator
it(testDir
,
131 directory_options::follow_directory_symlink
, ec
);
134 std::set
<path
> unseen_entries
= dir_contents
;
135 while (!unseen_entries
.empty()) {
137 const path entry
= *it
;
139 assert(unseen_entries
.erase(entry
) == 1);
140 recursive_directory_iterator
& it_ref
= it
.increment(ec
);
142 assert(&it_ref
== &it
);
147 // Windows doesn't support setting perms::none to trigger failures
148 // reading directories.
149 #ifndef TEST_WIN_NO_FILESYSTEM_PERMS_NONE
150 static void access_denied_on_recursion_test_case()
154 const path testFiles
[] = {
155 env
.create_dir("dir1"),
156 env
.create_dir("dir1/dir2"),
157 env
.create_file("dir1/dir2/file1"),
158 env
.create_file("dir1/file2")
160 const path startDir
= testFiles
[0];
161 const path permDeniedDir
= testFiles
[1];
162 const path otherFile
= testFiles
[3];
163 auto SkipEPerm
= directory_options::skip_permission_denied
;
165 // Change the permissions so we can no longer iterate
166 permissions(permDeniedDir
, perms::none
);
168 const recursive_directory_iterator endIt
;
170 // Test that recursion resulting in a "EACCESS" error is not ignored
173 std::error_code ec
= GetTestEC();
174 recursive_directory_iterator
it(startDir
, ec
);
175 assert(ec
!= GetTestEC());
177 while (it
!= endIt
&& it
->path() != permDeniedDir
)
180 assert(*it
== permDeniedDir
);
186 // Same as above but test operator++().
188 std::error_code ec
= GetTestEC();
189 recursive_directory_iterator
it(startDir
, ec
);
191 while (it
!= endIt
&& it
->path() != permDeniedDir
)
194 assert(*it
== permDeniedDir
);
196 TEST_THROWS_TYPE(filesystem_error
, ++it
);
198 // Test that recursion resulting in a "EACCESS" error is ignored when the
199 // correct options are given to the constructor.
201 std::error_code ec
= GetTestEC();
202 recursive_directory_iterator
it(startDir
, SkipEPerm
, ec
);
206 bool seenOtherFile
= false;
207 if (*it
== otherFile
) {
209 seenOtherFile
= true;
210 assert (it
!= endIt
);
212 assert(*it
== permDeniedDir
);
222 assert(*it
== otherFile
);
225 // Test that construction resulting in a "EACCESS" error is not ignored
229 recursive_directory_iterator
it(permDeniedDir
, ec
);
233 // Same as above but testing the throwing constructors
235 TEST_THROWS_TYPE(filesystem_error
,
236 recursive_directory_iterator(permDeniedDir
));
238 // Test that construction resulting in a "EACCESS" error constructs the
239 // end iterator when the correct options are given.
241 std::error_code ec
= GetTestEC();
242 recursive_directory_iterator
it(permDeniedDir
, SkipEPerm
, ec
);
248 // See llvm.org/PR35078
249 static void test_PR35078()
253 const path testFiles
[] = {
254 env
.create_dir("dir1"),
255 env
.create_dir("dir1/dir2"),
256 env
.create_dir("dir1/dir2/dir3"),
257 env
.create_file("dir1/file1"),
258 env
.create_file("dir1/dir2/dir3/file2")
260 const path startDir
= testFiles
[0];
261 const path permDeniedDir
= testFiles
[1];
262 const path nestedDir
= testFiles
[2];
263 const path nestedFile
= testFiles
[3];
265 // Change the permissions so we can no longer iterate
266 permissions(permDeniedDir
,
267 perms::group_exec
|perms::owner_exec
|perms::others_exec
,
268 perm_options::remove
);
270 const std::errc eacess
= std::errc::permission_denied
;
271 std::error_code ec
= GetTestEC();
273 const recursive_directory_iterator endIt
;
275 auto SetupState
= [&](bool AllowEAccess
, bool& SeenFile3
) {
277 auto Opts
= AllowEAccess
? directory_options::skip_permission_denied
278 : directory_options::none
;
279 recursive_directory_iterator
it(startDir
, Opts
, ec
);
280 while (!ec
&& it
!= endIt
&& *it
!= nestedDir
) {
281 if (*it
== nestedFile
)
289 bool SeenNestedFile
= false;
290 recursive_directory_iterator it
= SetupState(false, SeenNestedFile
);
292 assert(*it
== nestedDir
);
296 assert(ErrorIs(ec
, eacess
));
300 bool SeenNestedFile
= false;
301 recursive_directory_iterator it
= SetupState(true, SeenNestedFile
);
303 assert(*it
== nestedDir
);
307 if (SeenNestedFile
) {
311 assert(*it
== nestedFile
);
315 bool SeenNestedFile
= false;
316 recursive_directory_iterator it
= SetupState(false, SeenNestedFile
);
318 assert(*it
== nestedDir
);
320 ExceptionChecker
Checker(std::errc::permission_denied
,
321 "recursive_directory_iterator::operator++()",
322 format_string("attempting recursion into \"%s\"",
323 nestedDir
.string().c_str()));
324 TEST_VALIDATE_EXCEPTION(filesystem_error
, Checker
, ++it
);
329 // See llvm.org/PR35078
330 static void test_PR35078_with_symlink()
334 const path testFiles
[] = {
335 env
.create_dir("dir1"),
336 env
.create_file("dir1/file1"),
337 env
.create_dir("sym_dir"),
338 env
.create_dir("sym_dir/nested_sym_dir"),
339 env
.create_directory_symlink("sym_dir/nested_sym_dir", "dir1/dir2"),
340 env
.create_dir("sym_dir/dir1"),
341 env
.create_dir("sym_dir/dir1/dir2"),
344 // const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
345 const path startDir
= testFiles
[0];
346 const path nestedFile
= testFiles
[1];
347 const path permDeniedDir
= testFiles
[2];
348 const path symDir
= testFiles
[4];
350 // Change the permissions so we can no longer iterate
351 permissions(permDeniedDir
,
352 perms::group_exec
|perms::owner_exec
|perms::others_exec
,
353 perm_options::remove
);
355 const std::errc eacess
= std::errc::permission_denied
;
356 std::error_code ec
= GetTestEC();
358 const recursive_directory_iterator endIt
;
360 auto SetupState
= [&](bool AllowEAccess
, bool FollowSym
, bool& SeenFile3
) {
362 auto Opts
= AllowEAccess
? directory_options::skip_permission_denied
363 : directory_options::none
;
365 Opts
|= directory_options::follow_directory_symlink
;
366 recursive_directory_iterator
it(startDir
, Opts
, ec
);
367 while (!ec
&& it
!= endIt
&& *it
!= symDir
) {
368 if (*it
== nestedFile
)
381 {false, false, true}, {true, true, true}, {true, false, true},
385 for (auto TC
: TestCases
) {
386 bool SeenNestedFile
= false;
387 recursive_directory_iterator it
= SetupState(TC
.SkipPermDenied
,
392 assert(*it
== symDir
);
395 if (TC
.ExpectSuccess
) {
397 if (SeenNestedFile
) {
401 assert(*it
== nestedFile
);
405 assert(ErrorIs(ec
, eacess
));
412 // See llvm.org/PR35078
413 static void test_PR35078_with_symlink_file()
417 const path testFiles
[] = {
418 env
.create_dir("dir1"),
419 env
.create_dir("dir1/dir2"),
420 env
.create_file("dir1/file2"),
421 env
.create_dir("sym_dir"),
422 env
.create_dir("sym_dir/sdir1"),
423 env
.create_file("sym_dir/sdir1/sfile1"),
424 env
.create_symlink("sym_dir/sdir1/sfile1", "dir1/dir2/file1")
426 const unsigned TestFilesSize
= sizeof(testFiles
) / sizeof(testFiles
[0]);
427 const path startDir
= testFiles
[0];
428 const path nestedDir
= testFiles
[1];
429 const path nestedFile
= testFiles
[2];
430 const path permDeniedDir
= testFiles
[3];
431 const path symFile
= testFiles
[TestFilesSize
- 1];
433 // Change the permissions so we can no longer iterate
434 permissions(permDeniedDir
,
435 perms::group_exec
|perms::owner_exec
|perms::others_exec
,
436 perm_options::remove
);
438 const std::errc eacess
= std::errc::permission_denied
;
439 std::error_code ec
= GetTestEC();
441 const recursive_directory_iterator EndIt
;
443 auto SetupState
= [&](bool AllowEAccess
, bool FollowSym
, bool& SeenNestedFile
) {
444 SeenNestedFile
= false;
445 auto Opts
= AllowEAccess
? directory_options::skip_permission_denied
446 : directory_options::none
;
448 Opts
|= directory_options::follow_directory_symlink
;
449 recursive_directory_iterator
it(startDir
, Opts
, ec
);
450 while (!ec
&& it
!= EndIt
&& *it
!= nestedDir
) {
451 if (*it
== nestedFile
)
452 SeenNestedFile
= true;
464 {false, false, true}, {true, true, true}, {true, false, true},
468 for (auto TC
: TestCases
){
469 bool SeenNestedFile
= false;
470 recursive_directory_iterator it
= SetupState(TC
.SkipPermDenied
,
475 assert(*it
== nestedDir
);
480 assert(*it
== symFile
);
483 if (TC
.ExpectSuccess
) {
484 if (!SeenNestedFile
) {
487 assert(*it
== nestedFile
);
495 assert(ErrorIs(ec
, eacess
));
500 #endif // TEST_WIN_NO_FILESYSTEM_PERMS_NONE
502 int main(int, char**) {
503 test_increment_signatures();
504 test_prefix_increment();
505 test_postfix_increment();
506 test_increment_method();
507 test_follow_symlinks();
508 #ifndef TEST_WIN_NO_FILESYSTEM_PERMS_NONE
509 access_denied_on_recursion_test_case();
511 test_PR35078_with_symlink();
512 test_PR35078_with_symlink_file();