1 //===----------------------------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 // UNSUPPORTED: c++98, c++03
11
12 // <filesystem>
13
14 // class recursive_directory_iterator
15
16 // recursive_directory_iterator& operator++();
17 // recursive_directory_iterator& increment(error_code& ec) noexcept;
18
19 #include "filesystem_include.hpp"
20 #include <type_traits>
21 #include <set>
22 #include <cassert>
23
24 #include "test_macros.h"
25 #include "rapid-cxx-test.hpp"
26 #include "filesystem_test_helper.hpp"
27
28 using namespace fs;
29
30 TEST_SUITE(recursive_directory_iterator_increment_tests)
31
TEST_CASE(test_increment_signatures)32 TEST_CASE(test_increment_signatures)
33 {
34 recursive_directory_iterator d; ((void)d);
35 std::error_code ec; ((void)ec);
36
37 ASSERT_SAME_TYPE(decltype(++d), recursive_directory_iterator&);
38 ASSERT_NOT_NOEXCEPT(++d);
39
40 ASSERT_SAME_TYPE(decltype(d.increment(ec)), recursive_directory_iterator&);
41 ASSERT_NOT_NOEXCEPT(d.increment(ec));
42 }
43
TEST_CASE(test_prefix_increment)44 TEST_CASE(test_prefix_increment)
45 {
46 const path testDir = StaticEnv::Dir;
47 const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
48 std::end( StaticEnv::RecDirIterationList));
49 const recursive_directory_iterator endIt{};
50
51 std::error_code ec;
52 recursive_directory_iterator it(testDir, ec);
53 TEST_REQUIRE(!ec);
54
55 std::set<path> unseen_entries = dir_contents;
56 while (!unseen_entries.empty()) {
57 TEST_REQUIRE(it != endIt);
58 const path entry = *it;
59 TEST_REQUIRE(unseen_entries.erase(entry) == 1);
60 recursive_directory_iterator& it_ref = ++it;
61 TEST_CHECK(&it_ref == &it);
62 }
63
64 TEST_CHECK(it == endIt);
65 }
66
TEST_CASE(test_postfix_increment)67 TEST_CASE(test_postfix_increment)
68 {
69 const path testDir = StaticEnv::Dir;
70 const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
71 std::end( StaticEnv::RecDirIterationList));
72 const recursive_directory_iterator endIt{};
73
74 std::error_code ec;
75 recursive_directory_iterator it(testDir, ec);
76 TEST_REQUIRE(!ec);
77
78 std::set<path> unseen_entries = dir_contents;
79 while (!unseen_entries.empty()) {
80 TEST_REQUIRE(it != endIt);
81 const path entry = *it;
82 TEST_REQUIRE(unseen_entries.erase(entry) == 1);
83 const path entry2 = *it++;
84 TEST_CHECK(entry2 == entry);
85 }
86 TEST_CHECK(it == endIt);
87 }
88
89
TEST_CASE(test_increment_method)90 TEST_CASE(test_increment_method)
91 {
92 const path testDir = StaticEnv::Dir;
93 const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
94 std::end( StaticEnv::RecDirIterationList));
95 const recursive_directory_iterator endIt{};
96
97 std::error_code ec;
98 recursive_directory_iterator it(testDir, ec);
99 TEST_REQUIRE(!ec);
100
101 std::set<path> unseen_entries = dir_contents;
102 while (!unseen_entries.empty()) {
103 TEST_REQUIRE(it != endIt);
104 const path entry = *it;
105 TEST_REQUIRE(unseen_entries.erase(entry) == 1);
106 recursive_directory_iterator& it_ref = it.increment(ec);
107 TEST_REQUIRE(!ec);
108 TEST_CHECK(&it_ref == &it);
109 }
110
111 TEST_CHECK(it == endIt);
112 }
113
TEST_CASE(test_follow_symlinks)114 TEST_CASE(test_follow_symlinks)
115 {
116 const path testDir = StaticEnv::Dir;
117 auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList;
118
119 const std::set<path> dir_contents(std::begin(IterList), std::end(IterList));
120 const recursive_directory_iterator endIt{};
121
122 std::error_code ec;
123 recursive_directory_iterator it(testDir,
124 directory_options::follow_directory_symlink, ec);
125 TEST_REQUIRE(!ec);
126
127 std::set<path> unseen_entries = dir_contents;
128 while (!unseen_entries.empty()) {
129 TEST_REQUIRE(it != endIt);
130 const path entry = *it;
131
132 TEST_REQUIRE(unseen_entries.erase(entry) == 1);
133 recursive_directory_iterator& it_ref = it.increment(ec);
134 TEST_REQUIRE(!ec);
135 TEST_CHECK(&it_ref == &it);
136 }
137 TEST_CHECK(it == endIt);
138 }
139
TEST_CASE(access_denied_on_recursion_test_case)140 TEST_CASE(access_denied_on_recursion_test_case)
141 {
142 using namespace fs;
143 scoped_test_env env;
144 const path testFiles[] = {
145 env.create_dir("dir1"),
146 env.create_dir("dir1/dir2"),
147 env.create_file("dir1/dir2/file1"),
148 env.create_file("dir1/file2")
149 };
150 const path startDir = testFiles[0];
151 const path permDeniedDir = testFiles[1];
152 const path otherFile = testFiles[3];
153 auto SkipEPerm = directory_options::skip_permission_denied;
154
155 // Change the permissions so we can no longer iterate
156 permissions(permDeniedDir, perms::none);
157
158 const recursive_directory_iterator endIt;
159
160 // Test that recursion resulting in a "EACCESS" error is not ignored
161 // by default.
162 {
163 std::error_code ec = GetTestEC();
164 recursive_directory_iterator it(startDir, ec);
165 TEST_REQUIRE(ec != GetTestEC());
166 TEST_REQUIRE(!ec);
167 while (it != endIt && it->path() != permDeniedDir)
168 ++it;
169 TEST_REQUIRE(it != endIt);
170 TEST_REQUIRE(*it == permDeniedDir);
171
172 it.increment(ec);
173 TEST_CHECK(ec);
174 TEST_CHECK(it == endIt);
175 }
176 // Same as above but test operator++().
177 {
178 std::error_code ec = GetTestEC();
179 recursive_directory_iterator it(startDir, ec);
180 TEST_REQUIRE(!ec);
181 while (it != endIt && it->path() != permDeniedDir)
182 ++it;
183 TEST_REQUIRE(it != endIt);
184 TEST_REQUIRE(*it == permDeniedDir);
185
186 TEST_REQUIRE_THROW(filesystem_error, ++it);
187 }
188 // Test that recursion resulting in a "EACCESS" error is ignored when the
189 // correct options are given to the constructor.
190 {
191 std::error_code ec = GetTestEC();
192 recursive_directory_iterator it(startDir, SkipEPerm, ec);
193 TEST_REQUIRE(!ec);
194 TEST_REQUIRE(it != endIt);
195
196 bool seenOtherFile = false;
197 if (*it == otherFile) {
198 ++it;
199 seenOtherFile = true;
200 TEST_REQUIRE (it != endIt);
201 }
202 TEST_REQUIRE(*it == permDeniedDir);
203
204 ec = GetTestEC();
205 it.increment(ec);
206 TEST_REQUIRE(!ec);
207
208 if (seenOtherFile) {
209 TEST_CHECK(it == endIt);
210 } else {
211 TEST_CHECK(it != endIt);
212 TEST_CHECK(*it == otherFile);
213 }
214 }
215 // Test that construction resulting in a "EACCESS" error is not ignored
216 // by default.
217 {
218 std::error_code ec;
219 recursive_directory_iterator it(permDeniedDir, ec);
220 TEST_REQUIRE(ec);
221 TEST_REQUIRE(it == endIt);
222 }
223 // Same as above but testing the throwing constructors
224 {
225 TEST_REQUIRE_THROW(filesystem_error,
226 recursive_directory_iterator(permDeniedDir));
227 }
228 // Test that construction resulting in a "EACCESS" error constructs the
229 // end iterator when the correct options are given.
230 {
231 std::error_code ec = GetTestEC();
232 recursive_directory_iterator it(permDeniedDir, SkipEPerm, ec);
233 TEST_REQUIRE(!ec);
234 TEST_REQUIRE(it == endIt);
235 }
236 }
237
238 // See llvm.org/PR35078
TEST_CASE(test_PR35078)239 TEST_CASE(test_PR35078)
240 {
241 using namespace fs;
242 scoped_test_env env;
243 const path testFiles[] = {
244 env.create_dir("dir1"),
245 env.create_dir("dir1/dir2"),
246 env.create_dir("dir1/dir2/dir3"),
247 env.create_file("dir1/file1"),
248 env.create_file("dir1/dir2/dir3/file2")
249 };
250 const path startDir = testFiles[0];
251 const path permDeniedDir = testFiles[1];
252 const path nestedDir = testFiles[2];
253 const path nestedFile = testFiles[3];
254
255 // Change the permissions so we can no longer iterate
256 permissions(permDeniedDir,
257 perms::group_exec|perms::owner_exec|perms::others_exec,
258 perm_options::remove);
259
260 const std::error_code eacess_ec =
261 std::make_error_code(std::errc::permission_denied);
262 std::error_code ec = GetTestEC();
263
264 const recursive_directory_iterator endIt;
265
266 auto SetupState = [&](bool AllowEAccess, bool& SeenFile3) {
267 SeenFile3 = false;
268 auto Opts = AllowEAccess ? directory_options::skip_permission_denied
269 : directory_options::none;
270 recursive_directory_iterator it(startDir, Opts, ec);
271 while (!ec && it != endIt && *it != nestedDir) {
272 if (*it == nestedFile)
273 SeenFile3 = true;
274 it.increment(ec);
275 }
276 return it;
277 };
278
279 {
280 bool SeenNestedFile = false;
281 recursive_directory_iterator it = SetupState(false, SeenNestedFile);
282 TEST_REQUIRE(it != endIt);
283 TEST_REQUIRE(*it == nestedDir);
284 ec = GetTestEC();
285 it.increment(ec);
286 TEST_CHECK(ec);
287 TEST_CHECK(ec == eacess_ec);
288 TEST_CHECK(it == endIt);
289 }
290 {
291 bool SeenNestedFile = false;
292 recursive_directory_iterator it = SetupState(true, SeenNestedFile);
293 TEST_REQUIRE(it != endIt);
294 TEST_REQUIRE(*it == nestedDir);
295 ec = GetTestEC();
296 it.increment(ec);
297 TEST_CHECK(!ec);
298 if (SeenNestedFile) {
299 TEST_CHECK(it == endIt);
300 } else {
301 TEST_REQUIRE(it != endIt);
302 TEST_CHECK(*it == nestedFile);
303 }
304 }
305 {
306 bool SeenNestedFile = false;
307 recursive_directory_iterator it = SetupState(false, SeenNestedFile);
308 TEST_REQUIRE(it != endIt);
309 TEST_REQUIRE(*it == nestedDir);
310
311 ExceptionChecker Checker(std::errc::permission_denied,
312 "recursive_directory_iterator::operator++()",
313 format_string("attempting recursion into \"%s\"",
314 nestedDir.native()));
315 TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it);
316 }
317 }
318
319
320 // See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink)321 TEST_CASE(test_PR35078_with_symlink)
322 {
323 using namespace fs;
324 scoped_test_env env;
325 const path testFiles[] = {
326 env.create_dir("dir1"),
327 env.create_file("dir1/file1"),
328 env.create_dir("sym_dir"),
329 env.create_dir("sym_dir/nested_sym_dir"),
330 env.create_symlink("sym_dir/nested_sym_dir", "dir1/dir2"),
331 env.create_dir("sym_dir/dir1"),
332 env.create_dir("sym_dir/dir1/dir2"),
333
334 };
335 // const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
336 const path startDir = testFiles[0];
337 const path nestedFile = testFiles[1];
338 const path permDeniedDir = testFiles[2];
339 const path symDir = testFiles[4];
340
341 // Change the permissions so we can no longer iterate
342 permissions(permDeniedDir,
343 perms::group_exec|perms::owner_exec|perms::others_exec,
344 perm_options::remove);
345
346 const std::error_code eacess_ec =
347 std::make_error_code(std::errc::permission_denied);
348 std::error_code ec = GetTestEC();
349
350 const recursive_directory_iterator endIt;
351
352 auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenFile3) {
353 SeenFile3 = false;
354 auto Opts = AllowEAccess ? directory_options::skip_permission_denied
355 : directory_options::none;
356 if (FollowSym)
357 Opts |= directory_options::follow_directory_symlink;
358 recursive_directory_iterator it(startDir, Opts, ec);
359 while (!ec && it != endIt && *it != symDir) {
360 if (*it == nestedFile)
361 SeenFile3 = true;
362 it.increment(ec);
363 }
364 return it;
365 };
366
367 struct {
368 bool SkipPermDenied;
369 bool FollowSymlinks;
370 bool ExpectSuccess;
371 } TestCases[] = {
372 // Passing cases
373 {false, false, true}, {true, true, true}, {true, false, true},
374 // Failing cases
375 {false, true, false}
376 };
377 for (auto TC : TestCases) {
378 bool SeenNestedFile = false;
379 recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
380 TC.FollowSymlinks,
381 SeenNestedFile);
382 TEST_REQUIRE(!ec);
383 TEST_REQUIRE(it != endIt);
384 TEST_REQUIRE(*it == symDir);
385 ec = GetTestEC();
386 it.increment(ec);
387 if (TC.ExpectSuccess) {
388 TEST_CHECK(!ec);
389 if (SeenNestedFile) {
390 TEST_CHECK(it == endIt);
391 } else {
392 TEST_REQUIRE(it != endIt);
393 TEST_CHECK(*it == nestedFile);
394 }
395 } else {
396 TEST_CHECK(ec);
397 TEST_CHECK(ec == eacess_ec);
398 TEST_CHECK(it == endIt);
399 }
400 }
401 }
402
403
404 // See llvm.org/PR35078
TEST_CASE(test_PR35078_with_symlink_file)405 TEST_CASE(test_PR35078_with_symlink_file)
406 {
407 using namespace fs;
408 scoped_test_env env;
409 const path testFiles[] = {
410 env.create_dir("dir1"),
411 env.create_dir("dir1/dir2"),
412 env.create_file("dir1/file2"),
413 env.create_dir("sym_dir"),
414 env.create_dir("sym_dir/sdir1"),
415 env.create_file("sym_dir/sdir1/sfile1"),
416 env.create_symlink("sym_dir/sdir1/sfile1", "dir1/dir2/file1")
417 };
418 const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]);
419 const path startDir = testFiles[0];
420 const path nestedDir = testFiles[1];
421 const path nestedFile = testFiles[2];
422 const path permDeniedDir = testFiles[3];
423 const path symFile = testFiles[TestFilesSize - 1];
424
425 // Change the permissions so we can no longer iterate
426 permissions(permDeniedDir,
427 perms::group_exec|perms::owner_exec|perms::others_exec,
428 perm_options::remove);
429
430 const std::error_code eacess_ec =
431 std::make_error_code(std::errc::permission_denied);
432 std::error_code ec = GetTestEC();
433
434 const recursive_directory_iterator EndIt;
435
436 auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenNestedFile) {
437 SeenNestedFile = false;
438 auto Opts = AllowEAccess ? directory_options::skip_permission_denied
439 : directory_options::none;
440 if (FollowSym)
441 Opts |= directory_options::follow_directory_symlink;
442 recursive_directory_iterator it(startDir, Opts, ec);
443 while (!ec && it != EndIt && *it != nestedDir) {
444 if (*it == nestedFile)
445 SeenNestedFile = true;
446 it.increment(ec);
447 }
448 return it;
449 };
450
451 struct {
452 bool SkipPermDenied;
453 bool FollowSymlinks;
454 bool ExpectSuccess;
455 } TestCases[] = {
456 // Passing cases
457 {false, false, true}, {true, true, true}, {true, false, true},
458 // Failing cases
459 {false, true, false}
460 };
461 for (auto TC : TestCases){
462 bool SeenNestedFile = false;
463 recursive_directory_iterator it = SetupState(TC.SkipPermDenied,
464 TC.FollowSymlinks,
465 SeenNestedFile);
466 TEST_REQUIRE(!ec);
467 TEST_REQUIRE(it != EndIt);
468 TEST_REQUIRE(*it == nestedDir);
469 ec = GetTestEC();
470 it.increment(ec);
471 TEST_REQUIRE(it != EndIt);
472 TEST_CHECK(!ec);
473 TEST_CHECK(*it == symFile);
474 ec = GetTestEC();
475 it.increment(ec);
476 if (TC.ExpectSuccess) {
477 if (!SeenNestedFile) {
478 TEST_CHECK(!ec);
479 TEST_REQUIRE(it != EndIt);
480 TEST_CHECK(*it == nestedFile);
481 ec = GetTestEC();
482 it.increment(ec);
483 }
484 TEST_CHECK(!ec);
485 TEST_CHECK(it == EndIt);
486 } else {
487 TEST_CHECK(ec);
488 TEST_CHECK(ec == eacess_ec);
489 TEST_CHECK(it == EndIt);
490 }
491 }
492 }
493
494
495 TEST_SUITE_END()
496