1 #ifndef FILESYSTEM_TEST_HELPER_H 2 #define FILESYSTEM_TEST_HELPER_H 3 4 #include <filesystem> 5 6 #include <sys/stat.h> // for stat, mkdir, mkfifo 7 #ifndef _WIN32 8 #include <unistd.h> // for ftruncate, link, symlink, getcwd, chdir 9 #include <sys/statvfs.h> 10 #else 11 #include <io.h> 12 #include <direct.h> 13 #include <windows.h> // for CreateSymbolicLink, CreateHardLink 14 #endif 15 16 #include <cassert> 17 #include <cerrno> 18 #include <chrono> 19 #include <cstdint> 20 #include <cstdio> // for printf 21 #include <cstring> 22 #include <string> 23 #include <system_error> 24 #include <type_traits> 25 #include <vector> 26 27 #include "assert_macros.h" 28 #include "make_string.h" 29 #include "test_macros.h" 30 #include "format_string.h" 31 32 // For creating socket files 33 #if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(_WIN32) 34 # include <sys/socket.h> 35 # include <sys/un.h> 36 #endif 37 namespace fs = std::filesystem; 38 39 namespace utils { 40 #ifdef _WIN32 41 inline int mkdir(const char* path, int mode) { (void)mode; return ::_mkdir(path); } 42 inline int symlink(const char* oldname, const char* newname, bool is_dir) { 43 DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; 44 if (CreateSymbolicLinkA(newname, oldname, 45 flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) 46 return 0; 47 if (GetLastError() != ERROR_INVALID_PARAMETER) 48 return 1; 49 return !CreateSymbolicLinkA(newname, oldname, flags); 50 } 51 inline int link(const char *oldname, const char* newname) { 52 return !CreateHardLinkA(newname, oldname, NULL); 53 } 54 inline int setenv(const char *var, const char *val, int overwrite) { 55 (void)overwrite; 56 return ::_putenv((std::string(var) + "=" + std::string(val)).c_str()); 57 } 58 inline int unsetenv(const char *var) { 59 return ::_putenv((std::string(var) + "=").c_str()); 60 } 61 inline bool space(std::string path, std::uintmax_t &capacity, 62 std::uintmax_t &free, std::uintmax_t &avail) { 63 ULARGE_INTEGER FreeBytesAvailableToCaller, TotalNumberOfBytes, 64 TotalNumberOfFreeBytes; 65 if (!GetDiskFreeSpaceExA(path.c_str(), &FreeBytesAvailableToCaller, 66 &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) 67 return false; 68 capacity = TotalNumberOfBytes.QuadPart; 69 free = TotalNumberOfFreeBytes.QuadPart; 70 avail = FreeBytesAvailableToCaller.QuadPart; 71 assert(capacity > 0); 72 assert(free > 0); 73 assert(avail > 0); 74 return true; 75 } 76 #else 77 using ::mkdir; 78 inline int symlink(const char* oldname, const char* newname, bool is_dir) { (void)is_dir; return ::symlink(oldname, newname); } 79 using ::link; 80 using ::setenv; 81 using ::unsetenv; 82 inline bool space(std::string path, std::uintmax_t &capacity, 83 std::uintmax_t &free, std::uintmax_t &avail) { 84 struct statvfs expect; 85 if (::statvfs(path.c_str(), &expect) == -1) 86 return false; 87 assert(expect.f_bavail > 0); 88 assert(expect.f_bfree > 0); 89 assert(expect.f_bsize > 0); 90 assert(expect.f_blocks > 0); 91 assert(expect.f_frsize > 0); 92 auto do_mult = [&](std::uintmax_t val) { 93 std::uintmax_t fsize = expect.f_frsize; 94 std::uintmax_t new_val = val * fsize; 95 assert(new_val / fsize == val); // Test for overflow 96 return new_val; 97 }; 98 capacity = do_mult(expect.f_blocks); 99 free = do_mult(expect.f_bfree); 100 avail = do_mult(expect.f_bavail); 101 return true; 102 } 103 #endif 104 105 // N.B. libc might define some of the foo[64] identifiers using macros from 106 // foo64 -> foo or vice versa. 107 #if defined(_WIN32) 108 using off64_t = std::int64_t; 109 #elif defined(__MVS__) || defined(__LP64__) 110 using off64_t = ::off_t; 111 #else 112 using ::off64_t; 113 #endif 114 115 inline FILE* fopen64(const char* pathname, const char* mode) { 116 // Bionic does not distinguish between fopen and fopen64, but fopen64 117 // wasn't added until API 24. 118 #if defined(_WIN32) || defined(__MVS__) || defined(__LP64__) || defined(__BIONIC__) 119 return ::fopen(pathname, mode); 120 #else 121 return ::fopen64(pathname, mode); 122 #endif 123 } 124 125 inline int ftruncate64(int fd, off64_t length) { 126 #if defined(_WIN32) 127 // _chsize_s sets errno on failure and also returns the error number. 128 return ::_chsize_s(fd, length) ? -1 : 0; 129 #elif defined(__MVS__) || defined(__LP64__) 130 return ::ftruncate(fd, length); 131 #else 132 return ::ftruncate64(fd, length); 133 #endif 134 } 135 136 inline std::string getcwd() { 137 // Assume that path lengths are not greater than this. 138 // This should be fine for testing purposes. 139 char buf[4096]; 140 char* ret = ::getcwd(buf, sizeof(buf)); 141 assert(ret && "getcwd failed"); 142 return std::string(ret); 143 } 144 145 inline bool exists(std::string const& path) { 146 struct ::stat tmp; 147 return ::stat(path.c_str(), &tmp) == 0; 148 } 149 } // namespace utils 150 151 struct scoped_test_env 152 { 153 scoped_test_env() : test_root(available_cwd_path()) { 154 #ifdef _WIN32 155 // Windows mkdir can create multiple recursive directories 156 // if needed. 157 std::string cmd = "mkdir " + test_root.string(); 158 #else 159 std::string cmd = "mkdir -p " + test_root.string(); 160 #endif 161 int ret = std::system(cmd.c_str()); 162 assert(ret == 0); 163 164 // Ensure that the root_path is fully resolved, i.e. it contains no 165 // symlinks. The filesystem tests depend on that. We do this after 166 // creating the root_path, because `fs::canonical` requires the 167 // path to exist. 168 test_root = fs::canonical(test_root); 169 } 170 171 ~scoped_test_env() { 172 #ifdef _WIN32 173 std::string cmd = "rmdir /s /q " + test_root.string(); 174 int ret = std::system(cmd.c_str()); 175 assert(ret == 0); 176 #else 177 #if defined(__MVS__) 178 // The behaviour of chmod -R on z/OS prevents recursive 179 // permission change for directories that do not have read permission. 180 std::string cmd = "find " + test_root.string() + " -exec chmod 777 {} \\;"; 181 #else 182 std::string cmd = "chmod -R 777 " + test_root.string(); 183 #endif // defined(__MVS__) 184 int ret = std::system(cmd.c_str()); 185 # if !defined(_AIX) && !defined(__ANDROID__) 186 // On AIX the chmod command will return non-zero when trying to set 187 // the permissions on a directory that contains a bad symlink. This triggers 188 // the assert, despite being able to delete everything with the following 189 // `rm -r` command. 190 // 191 // Android's chmod was buggy in old OSs, but skipping this assert is 192 // sufficient to ensure that the `rm -rf` succeeds for almost all tests: 193 // - Android L: chmod aborts after one error 194 // - Android L and M: chmod -R tries to set permissions of a symlink 195 // target. 196 // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod 197 // onto old devices, re-enable this assert for devices running Android N 198 // and up, rewrite this chmod+rm in C or C++. 199 assert(ret == 0); 200 # endif 201 202 cmd = "rm -rf " + test_root.string(); 203 ret = std::system(cmd.c_str()); 204 assert(ret == 0); 205 #endif 206 } 207 208 scoped_test_env(scoped_test_env const &) = delete; 209 scoped_test_env & operator=(scoped_test_env const &) = delete; 210 211 fs::path make_env_path(std::string p) { return sanitize_path(p); } 212 213 std::string sanitize_path(std::string raw) { 214 assert(raw.find("..") == std::string::npos); 215 std::string root = test_root.string(); 216 if (root.compare(0, root.size(), raw, 0, root.size()) != 0) { 217 assert(raw.front() != '\\'); 218 fs::path tmp(test_root); 219 tmp /= raw; 220 return tmp.string(); 221 } 222 return raw; 223 } 224 225 // Purposefully using a size potentially larger than off_t here so we can 226 // test the behavior of libc++fs when it is built with _FILE_OFFSET_BITS=64 227 // but the caller is not (std::filesystem also uses uintmax_t rather than 228 // off_t). On a 32-bit system this allows us to create a file larger than 229 // 2GB. 230 std::string create_file(fs::path filename_path, std::uintmax_t size = 0) { 231 std::string filename = sanitize_path(filename_path.string()); 232 233 if (size > 234 static_cast<typename std::make_unsigned<utils::off64_t>::type>( 235 std::numeric_limits<utils::off64_t>::max())) { 236 std::fprintf(stderr, "create_file(%s, %ju) too large\n", 237 filename.c_str(), size); 238 std::abort(); 239 } 240 241 #if defined(_WIN32) || defined(__MVS__) 242 # define FOPEN_CLOEXEC_FLAG "" 243 #else 244 # define FOPEN_CLOEXEC_FLAG "e" 245 #endif 246 FILE* file = utils::fopen64(filename.c_str(), "w" FOPEN_CLOEXEC_FLAG); 247 if (file == nullptr) { 248 std::fprintf(stderr, "fopen %s failed: %s\n", filename.c_str(), 249 std::strerror(errno)); 250 std::abort(); 251 } 252 253 if (utils::ftruncate64( 254 fileno(file), static_cast<utils::off64_t>(size)) == -1) { 255 std::fprintf(stderr, "ftruncate %s %ju failed: %s\n", filename.c_str(), 256 size, std::strerror(errno)); 257 std::fclose(file); 258 std::abort(); 259 } 260 261 std::fclose(file); 262 return filename; 263 } 264 265 std::string create_dir(fs::path filename_path) { 266 std::string filename = filename_path.string(); 267 filename = sanitize_path(std::move(filename)); 268 int ret = utils::mkdir(filename.c_str(), 0777); // rwxrwxrwx mode 269 assert(ret == 0); 270 return filename; 271 } 272 273 std::string create_file_dir_symlink(fs::path source_path, 274 fs::path to_path, 275 bool sanitize_source = true, 276 bool is_dir = false) { 277 std::string source = source_path.string(); 278 std::string to = to_path.string(); 279 if (sanitize_source) 280 source = sanitize_path(std::move(source)); 281 to = sanitize_path(std::move(to)); 282 int ret = utils::symlink(source.c_str(), to.c_str(), is_dir); 283 assert(ret == 0); 284 return to; 285 } 286 287 std::string create_symlink(fs::path source_path, 288 fs::path to_path, 289 bool sanitize_source = true) { 290 return create_file_dir_symlink(source_path, to_path, sanitize_source, 291 false); 292 } 293 294 std::string create_directory_symlink(fs::path source_path, 295 fs::path to_path, 296 bool sanitize_source = true) { 297 return create_file_dir_symlink(source_path, to_path, sanitize_source, 298 true); 299 } 300 301 std::string create_hardlink(fs::path source_path, fs::path to_path) { 302 std::string source = source_path.string(); 303 std::string to = to_path.string(); 304 source = sanitize_path(std::move(source)); 305 to = sanitize_path(std::move(to)); 306 int ret = utils::link(source.c_str(), to.c_str()); 307 assert(ret == 0); 308 return to; 309 } 310 311 #ifndef _WIN32 312 std::string create_fifo(std::string file) { 313 file = sanitize_path(std::move(file)); 314 int ret = ::mkfifo(file.c_str(), 0666); // rw-rw-rw- mode 315 assert(ret == 0); 316 return file; 317 } 318 #endif 319 320 // Some platforms doesn't support socket files so we shouldn't even 321 // allow tests to call this unguarded. 322 #if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(_WIN32) 323 std::string create_socket(std::string file) { 324 file = sanitize_path(std::move(file)); 325 326 ::sockaddr_un address; 327 address.sun_family = AF_UNIX; 328 329 // If file.size() is too big, try to create a file directly inside 330 // /tmp to make sure file path is short enough. 331 // Android platform warns about tmpnam, since the problem does not appear 332 // on Android, let's not apply it for Android. 333 # if !defined(__ANDROID__) 334 if (file.size() > sizeof(address.sun_path)) { 335 file = std::tmpnam(nullptr); 336 } 337 # endif 338 assert(file.size() <= sizeof(address.sun_path)); 339 ::strncpy(address.sun_path, file.c_str(), sizeof(address.sun_path)); 340 int fd = ::socket(AF_UNIX, SOCK_STREAM, 0); 341 assert(::bind(fd, reinterpret_cast<::sockaddr*>(&address), sizeof(address)) == 0); 342 return file; 343 } 344 #endif 345 346 fs::path test_root; 347 348 private: 349 // This could potentially introduce a filesystem race if multiple 350 // scoped_test_envs were created concurrently in the same test (hence 351 // sharing the same cwd). However, it is fairly unlikely to happen as 352 // we generally don't use scoped_test_env from multiple threads, so 353 // this is deemed acceptable. 354 // The cwd.filename() itself isn't unique across all tests in the suite, 355 // so start the numbering from a hash of the full cwd, to avoid 356 // different tests interfering with each other. 357 static inline fs::path available_cwd_path() { 358 fs::path const cwd = utils::getcwd(); 359 fs::path const tmp = fs::temp_directory_path(); 360 std::string base = cwd.filename().string(); 361 std::size_t i = std::hash<std::string>()(cwd.string()); 362 fs::path p = tmp / (base + "-static_env." + std::to_string(i)); 363 while (utils::exists(p.string())) { 364 p = tmp / (base + "-static_env." + std::to_string(++i)); 365 } 366 return p; 367 } 368 }; 369 370 /// This class generates the following tree: 371 /// 372 /// static_test_env 373 /// |-- bad_symlink -> dne 374 /// |-- dir1 375 /// | |-- dir2 376 /// | | |-- afile3 377 /// | | |-- dir3 378 /// | | | `-- file5 379 /// | | |-- file4 380 /// | | `-- symlink_to_dir3 -> dir3 381 /// | `-- file1 382 /// | `-- file2 383 /// |-- empty_file 384 /// |-- non_empty_file 385 /// |-- symlink_to_dir -> dir1 386 /// `-- symlink_to_empty_file -> empty_file 387 /// 388 class static_test_env { 389 scoped_test_env env_; 390 public: 391 static_test_env() { 392 env_.create_symlink("dne", "bad_symlink", false); 393 env_.create_dir("dir1"); 394 env_.create_dir("dir1/dir2"); 395 env_.create_file("dir1/dir2/afile3"); 396 env_.create_dir("dir1/dir2/dir3"); 397 env_.create_file("dir1/dir2/dir3/file5"); 398 env_.create_file("dir1/dir2/file4"); 399 env_.create_directory_symlink("dir3", "dir1/dir2/symlink_to_dir3", false); 400 env_.create_file("dir1/file1"); 401 env_.create_file("dir1/file2", 42); 402 env_.create_file("empty_file"); 403 env_.create_file("non_empty_file", 42); 404 env_.create_directory_symlink("dir1", "symlink_to_dir", false); 405 env_.create_symlink("empty_file", "symlink_to_empty_file", false); 406 } 407 408 const fs::path Root = env_.test_root; 409 410 fs::path makePath(fs::path const& p) const { 411 // env_path is expected not to contain symlinks. 412 fs::path const& env_path = Root; 413 return env_path / p; 414 } 415 416 const std::vector<fs::path> TestFileList = { 417 makePath("empty_file"), 418 makePath("non_empty_file"), 419 makePath("dir1/file1"), 420 makePath("dir1/file2") 421 }; 422 423 const std::vector<fs::path> TestDirList = { 424 makePath("dir1"), 425 makePath("dir1/dir2"), 426 makePath("dir1/dir2/dir3") 427 }; 428 429 const fs::path File = TestFileList[0]; 430 const fs::path Dir = TestDirList[0]; 431 const fs::path Dir2 = TestDirList[1]; 432 const fs::path Dir3 = TestDirList[2]; 433 const fs::path SymlinkToFile = makePath("symlink_to_empty_file"); 434 const fs::path SymlinkToDir = makePath("symlink_to_dir"); 435 const fs::path BadSymlink = makePath("bad_symlink"); 436 const fs::path DNE = makePath("DNE"); 437 const fs::path EmptyFile = TestFileList[0]; 438 const fs::path NonEmptyFile = TestFileList[1]; 439 const fs::path CharFile = "/dev/null"; // Hopefully this exists 440 441 const std::vector<fs::path> DirIterationList = { 442 makePath("dir1/dir2"), 443 makePath("dir1/file1"), 444 makePath("dir1/file2") 445 }; 446 447 const std::vector<fs::path> DirIterationListDepth1 = { 448 makePath("dir1/dir2/afile3"), 449 makePath("dir1/dir2/dir3"), 450 makePath("dir1/dir2/symlink_to_dir3"), 451 makePath("dir1/dir2/file4"), 452 }; 453 454 const std::vector<fs::path> RecDirIterationList = { 455 makePath("dir1/dir2"), 456 makePath("dir1/file1"), 457 makePath("dir1/file2"), 458 makePath("dir1/dir2/afile3"), 459 makePath("dir1/dir2/dir3"), 460 makePath("dir1/dir2/symlink_to_dir3"), 461 makePath("dir1/dir2/file4"), 462 makePath("dir1/dir2/dir3/file5") 463 }; 464 465 const std::vector<fs::path> RecDirFollowSymlinksIterationList = { 466 makePath("dir1/dir2"), 467 makePath("dir1/file1"), 468 makePath("dir1/file2"), 469 makePath("dir1/dir2/afile3"), 470 makePath("dir1/dir2/dir3"), 471 makePath("dir1/dir2/file4"), 472 makePath("dir1/dir2/dir3/file5"), 473 makePath("dir1/dir2/symlink_to_dir3"), 474 makePath("dir1/dir2/symlink_to_dir3/file5"), 475 }; 476 }; 477 478 struct CWDGuard { 479 std::string oldCwd_; 480 CWDGuard() : oldCwd_(utils::getcwd()) { } 481 ~CWDGuard() { 482 int ret = ::chdir(oldCwd_.c_str()); 483 assert(ret == 0 && "chdir failed"); 484 } 485 486 CWDGuard(CWDGuard const&) = delete; 487 CWDGuard& operator=(CWDGuard const&) = delete; 488 }; 489 490 // We often need to test that the error_code was cleared if no error occurs 491 // this function returns an error_code which is set to an error that will 492 // never be returned by the filesystem functions. 493 inline std::error_code GetTestEC(unsigned Idx = 0) { 494 using std::errc; 495 auto GetErrc = [&]() { 496 switch (Idx) { 497 case 0: 498 return errc::address_family_not_supported; 499 case 1: 500 return errc::address_not_available; 501 case 2: 502 return errc::address_in_use; 503 case 3: 504 return errc::argument_list_too_long; 505 default: 506 assert(false && "Idx out of range"); 507 std::abort(); 508 } 509 }; 510 return std::make_error_code(GetErrc()); 511 } 512 513 inline bool ErrorIsImp(const std::error_code& ec, 514 std::vector<std::errc> const& errors) { 515 std::error_condition cond = ec.default_error_condition(); 516 for (auto errc : errors) { 517 if (cond.value() == static_cast<int>(errc)) 518 return true; 519 } 520 return false; 521 } 522 523 template <class... ErrcT> 524 inline bool ErrorIs(const std::error_code& ec, std::errc First, ErrcT... Rest) { 525 std::vector<std::errc> errors = {First, Rest...}; 526 return ErrorIsImp(ec, errors); 527 } 528 529 // Provide our own Sleep routine since std::this_thread::sleep_for is not 530 // available in single-threaded mode. 531 template <class Dur> void SleepFor(Dur dur) { 532 using namespace std::chrono; 533 #if !_LIBCPP_HAS_MONOTONIC_CLOCK 534 using Clock = system_clock; 535 #else 536 using Clock = steady_clock; 537 #endif 538 const auto wake_time = Clock::now() + dur; 539 while (Clock::now() < wake_time) 540 ; 541 } 542 543 inline fs::perms NormalizeExpectedPerms(fs::perms P) { 544 #ifdef _WIN32 545 // On Windows, fs::perms only maps down to one bit stored in the filesystem, 546 // a boolean readonly flag. 547 // Normalize permissions to the format it gets returned; all fs entries are 548 // read+exec for all users; writable ones also have the write bit set for 549 // all users. 550 P |= fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read; 551 P |= fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec; 552 fs::perms Write = 553 fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write; 554 if ((P & Write) != fs::perms::none) 555 P |= Write; 556 #endif 557 return P; 558 } 559 560 struct ExceptionChecker { 561 std::errc expected_err; 562 fs::path expected_path1; 563 fs::path expected_path2; 564 unsigned num_paths; 565 const char* func_name; 566 std::string opt_message; 567 568 explicit ExceptionChecker(std::errc first_err, const char* fun_name, 569 std::string opt_msg = {}) 570 : expected_err{first_err}, num_paths(0), func_name(fun_name), 571 opt_message(opt_msg) {} 572 explicit ExceptionChecker(fs::path p, std::errc first_err, 573 const char* fun_name, std::string opt_msg = {}) 574 : expected_err(first_err), expected_path1(p), num_paths(1), 575 func_name(fun_name), opt_message(opt_msg) {} 576 577 explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err, 578 const char* fun_name, std::string opt_msg = {}) 579 : expected_err(first_err), expected_path1(p1), expected_path2(p2), 580 num_paths(2), func_name(fun_name), opt_message(opt_msg) {} 581 582 void operator()(fs::filesystem_error const& Err) { 583 assert(ErrorIsImp(Err.code(), {expected_err})); 584 assert(Err.path1() == expected_path1); 585 assert(Err.path2() == expected_path2); 586 #ifndef _WIN32 587 // On Windows, the error strings are windows error code strings, and don't 588 // match textually with the strings generated for generic std::errc::*. 589 LIBCPP_ONLY(check_libcxx_string(Err)); 590 #endif 591 } 592 593 void check_libcxx_string(fs::filesystem_error const& Err) { 594 std::string message = std::make_error_code(expected_err).message(); 595 596 std::string additional_msg = ""; 597 if (!opt_message.empty()) { 598 additional_msg = opt_message + ": "; 599 } 600 auto transform_path = [](const fs::path& p) { 601 return "\"" + p.string() + "\""; 602 }; 603 std::string format = [&]() -> std::string { 604 switch (num_paths) { 605 case 0: 606 return format_string("filesystem error: in %s: %s%s", func_name, 607 additional_msg, message); 608 case 1: 609 return format_string("filesystem error: in %s: %s%s [%s]", func_name, 610 additional_msg, message, 611 transform_path(expected_path1).c_str()); 612 case 2: 613 return format_string("filesystem error: in %s: %s%s [%s] [%s]", 614 func_name, additional_msg, message, 615 transform_path(expected_path1).c_str(), 616 transform_path(expected_path2).c_str()); 617 default: 618 TEST_FAIL("unexpected case"); 619 return ""; 620 } 621 }(); 622 assert(format == Err.what()); 623 if (format != Err.what()) { 624 std::fprintf(stderr, "filesystem_error::what() does not match expected output:\n"); 625 std::fprintf(stderr, " expected: \"%s\"\n", format.c_str()); 626 std::fprintf(stderr, " actual: \"%s\"\n\n", Err.what()); 627 } 628 } 629 630 ExceptionChecker(ExceptionChecker const&) = delete; 631 ExceptionChecker& operator=(ExceptionChecker const&) = delete; 632 633 }; 634 635 inline fs::path GetWindowsInaccessibleDir() { 636 // Only makes sense on windows, but the code can be compiled for 637 // any platform. 638 const fs::path dir("C:\\System Volume Information"); 639 std::error_code ec; 640 const fs::path root("C:\\"); 641 for (const auto &ent : fs::directory_iterator(root, ec)) { 642 if (ent != dir) 643 continue; 644 // Basic sanity checks on the directory_entry 645 if (!ent.exists() || !ent.is_directory()) { 646 std::fprintf(stderr, "The expected inaccessible directory \"%s\" was found " 647 "but doesn't behave as expected, skipping tests " 648 "regarding it\n", dir.string().c_str()); 649 return fs::path(); 650 } 651 // Check that it indeed is inaccessible as expected 652 (void)fs::exists(ent, ec); 653 if (!ec) { 654 std::fprintf(stderr, "The expected inaccessible directory \"%s\" was found " 655 "but seems to be accessible, skipping tests " 656 "regarding it\n", dir.string().c_str()); 657 return fs::path(); 658 } 659 return ent; 660 } 661 std::fprintf(stderr, "No inaccessible directory \"%s\" found, skipping tests " 662 "regarding it\n", dir.string().c_str()); 663 return fs::path(); 664 } 665 666 #endif /* FILESYSTEM_TEST_HELPER_H */ 667