• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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