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 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 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 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 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 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 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 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 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 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