1 // Filesystem operations -*- C++ -*- 2 3 // Copyright (C) 2014-2024 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 #ifndef _GLIBCXX_USE_CXX11_ABI 26 # define _GLIBCXX_USE_CXX11_ABI 1 27 # define NEED_DO_COPY_FILE 28 # define NEED_DO_SPACE 29 #endif 30 #ifndef _GNU_SOURCE 31 // Cygwin needs this for secure_getenv 32 # define _GNU_SOURCE 1 33 #endif 34 35 #include <bits/largefile-config.h> 36 #include <filesystem> 37 #include <functional> 38 #include <ostream> 39 #include <stack> 40 #include <stdlib.h> 41 #include <stdio.h> 42 #include <errno.h> 43 #include <limits.h> // PATH_MAX 44 #ifdef _GLIBCXX_HAVE_FCNTL_H 45 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW 46 #endif 47 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 48 # include <sys/stat.h> // stat, utimensat, fchmodat 49 #endif 50 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H 51 # include <sys/statvfs.h> // statvfs 52 #endif 53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 54 # include <utime.h> // utime 55 #endif 56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 57 # define WIN32_LEAN_AND_MEAN 58 # include <windows.h> 59 #endif 60 61 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem { 62 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } 63 #include "../filesystem/ops-common.h" 64 65 #pragma GCC diagnostic ignored "-Wunused-parameter" 66 67 namespace fs = std::filesystem; 68 namespace posix = std::filesystem::__gnu_posix; 69 70 fs::path 71 fs::absolute(const path& p) 72 { 73 error_code ec; 74 path ret = absolute(p, ec); 75 if (ec) 76 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p, 77 ec)); 78 return ret; 79 } 80 81 fs::path 82 fs::absolute(const path& p, error_code& ec) 83 { 84 path ret; 85 if (p.empty()) 86 { 87 ec = make_error_code(std::errc::invalid_argument); 88 return ret; 89 } 90 ec.clear(); 91 if (p.is_absolute()) 92 { 93 ret = p; 94 return ret; 95 } 96 97 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 98 // s must remain null-terminated 99 wstring_view s = p.native(); 100 101 if (p.has_root_directory()) // implies !p.has_root_name() 102 { 103 // GetFullPathNameW("//") gives unwanted result (PR 88884). 104 // If there are multiple directory separators at the start, 105 // skip all but the last of them. 106 const auto pos = s.find_first_not_of(L"/\\"); 107 __glibcxx_assert(pos != 0); 108 s.remove_prefix(std::min(s.length(), pos) - 1); 109 } 110 111 uint32_t len = 1024; 112 wstring buf; 113 do 114 { 115 buf.__resize_and_overwrite(len, [&s, &len](wchar_t* p, unsigned n) { 116 len = GetFullPathNameW(s.data(), n, p, nullptr); 117 return len > n ? 0 : len; 118 }); 119 } 120 while (len > buf.size()); 121 122 if (len == 0) 123 ec = __last_system_error(); 124 else 125 ret = std::move(buf); 126 #else 127 ret = current_path(ec); 128 ret /= p; 129 #endif 130 return ret; 131 } 132 133 namespace 134 { 135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 136 inline bool is_dot(wchar_t c) { return c == L'.'; } 137 #else 138 inline bool is_dot(char c) { return c == '.'; } 139 #endif 140 141 inline bool is_dot(const fs::path& path) 142 { 143 const auto& filename = path.native(); 144 return filename.size() == 1 && is_dot(filename[0]); 145 } 146 147 inline bool is_dotdot(const fs::path& path) 148 { 149 const auto& filename = path.native(); 150 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); 151 } 152 153 struct free_as_in_malloc 154 { 155 void operator()(void* p) const { ::free(p); } 156 }; 157 158 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>; 159 } 160 161 fs::path 162 fs::canonical(const path& p, error_code& ec) 163 { 164 path result; 165 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 166 const path pa = absolute(p.lexically_normal(), ec); 167 #else 168 const path pa = absolute(p, ec); 169 #endif 170 if (ec) 171 return result; 172 173 #ifdef _GLIBCXX_USE_REALPATH 174 char_ptr buf{ nullptr }; 175 # if _XOPEN_VERSION < 700 176 // Not safe to call realpath(path, NULL) 177 using char_type = fs::path::value_type; 178 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) ); 179 # endif 180 if (char* rp = ::realpath(pa.c_str(), buf.get())) 181 { 182 if (buf == nullptr) 183 buf.reset(rp); 184 result.assign(rp); 185 ec.clear(); 186 return result; 187 } 188 if (errno != ENAMETOOLONG) 189 { 190 ec.assign(errno, std::generic_category()); 191 return result; 192 } 193 #endif 194 195 if (!exists(pa, ec)) 196 { 197 if (!ec) 198 ec = make_error_code(std::errc::no_such_file_or_directory); 199 return result; 200 } 201 // else: we know there are (currently) no unresolvable symlink loops 202 203 result = pa.root_path(); 204 205 deque<path> cmpts; 206 for (auto& f : pa.relative_path()) 207 cmpts.push_back(f); 208 209 int max_allowed_symlinks = 40; 210 211 while (!cmpts.empty() && !ec) 212 { 213 path f = std::move(cmpts.front()); 214 cmpts.pop_front(); 215 216 if (f.empty()) 217 { 218 // ignore empty element 219 } 220 else if (is_dot(f)) 221 { 222 if (!is_directory(result, ec) && !ec) 223 ec.assign(ENOTDIR, std::generic_category()); 224 } 225 else if (is_dotdot(f)) 226 { 227 auto parent = result.parent_path(); 228 if (parent.empty()) 229 result = pa.root_path(); 230 else 231 result.swap(parent); 232 } 233 else 234 { 235 result /= f; 236 237 if (is_symlink(result, ec)) 238 { 239 path link = read_symlink(result, ec); 240 if (!ec) 241 { 242 if (--max_allowed_symlinks == 0) 243 ec.assign(ELOOP, std::generic_category()); 244 else 245 { 246 if (link.is_absolute()) 247 { 248 result = link.root_path(); 249 link = link.relative_path(); 250 } 251 else 252 result = result.parent_path(); 253 254 cmpts.insert(cmpts.begin(), link.begin(), link.end()); 255 } 256 } 257 } 258 } 259 } 260 261 if (ec || !exists(result, ec)) 262 result.clear(); 263 264 return result; 265 } 266 267 fs::path 268 fs::canonical(const path& p) 269 { 270 error_code ec; 271 path res = canonical(p, ec); 272 if (ec) 273 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path", 274 p, ec)); 275 return res; 276 } 277 278 void 279 fs::copy(const path& from, const path& to, copy_options options) 280 { 281 error_code ec; 282 copy(from, to, options, ec); 283 if (ec) 284 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); 285 } 286 287 namespace std::filesystem 288 { 289 // Need this as there's no 'perm_options::none' enumerator. 290 static inline bool is_set(fs::perm_options obj, fs::perm_options bits) 291 { 292 return (obj & bits) != fs::perm_options{}; 293 } 294 } 295 296 namespace 297 { 298 struct internal_file_clock : fs::__file_clock 299 { 300 using __file_clock::_S_to_sys; 301 using __file_clock::_S_from_sys; 302 303 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 304 static fs::file_time_type 305 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept 306 { 307 const auto sys_time = fs::file_time(st, ec); 308 if (sys_time == sys_time.min()) 309 return fs::file_time_type::min(); 310 return _S_from_sys(sys_time); 311 } 312 #endif 313 }; 314 } 315 316 void 317 fs::copy(const path& from, const path& to, copy_options options, 318 error_code& ec) 319 { 320 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 321 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); 322 const bool create_symlinks = is_set(options, copy_options::create_symlinks); 323 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); 324 const bool use_lstat = create_symlinks || skip_symlinks; 325 326 file_status f, t; 327 stat_type from_st, to_st; 328 // _GLIBCXX_RESOLVE_LIB_DEFECTS 329 // 2681. filesystem::copy() cannot copy symlinks 330 if (use_lstat || copy_symlinks 331 ? posix::lstat(from.c_str(), &from_st) 332 : posix::stat(from.c_str(), &from_st)) 333 { 334 ec.assign(errno, std::generic_category()); 335 return; 336 } 337 if (use_lstat 338 ? posix::lstat(to.c_str(), &to_st) 339 : posix::stat(to.c_str(), &to_st)) 340 { 341 if (!is_not_found_errno(errno)) 342 { 343 ec.assign(errno, std::generic_category()); 344 return; 345 } 346 t = file_status{file_type::not_found}; 347 } 348 else 349 t = make_file_status(to_st); 350 f = make_file_status(from_st); 351 352 if (exists(t) && !is_other(t) && !is_other(f) 353 && fs::equiv_files(from.c_str(), from_st, to.c_str(), to_st, ec)) 354 { 355 ec = std::make_error_code(std::errc::file_exists); 356 return; 357 } 358 if (is_other(f) || is_other(t)) 359 { 360 ec = std::make_error_code(std::errc::invalid_argument); 361 return; 362 } 363 if (is_directory(f) && is_regular_file(t)) 364 { 365 ec = std::make_error_code(std::errc::is_a_directory); 366 return; 367 } 368 369 if (is_symlink(f)) 370 { 371 if (skip_symlinks) 372 ec.clear(); 373 else if (!exists(t) && copy_symlinks) 374 copy_symlink(from, to, ec); 375 else 376 // Not clear what should be done here. 377 // "Otherwise report an error as specified in Error reporting (7)." 378 ec = std::make_error_code(std::errc::invalid_argument); 379 } 380 else if (is_regular_file(f)) 381 { 382 if (is_set(options, copy_options::directories_only)) 383 ec.clear(); 384 else if (create_symlinks) 385 create_symlink(from, to, ec); 386 else if (is_set(options, copy_options::create_hard_links)) 387 create_hard_link(from, to, ec); 388 else if (is_directory(t)) 389 do_copy_file(from.c_str(), (to / from.filename()).c_str(), 390 copy_file_options(options), &from_st, nullptr, ec); 391 else 392 { 393 auto ptr = exists(t) ? &to_st : &from_st; 394 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 395 &from_st, ptr, ec); 396 } 397 } 398 // _GLIBCXX_RESOLVE_LIB_DEFECTS 399 // 2682. filesystem::copy() won't create a symlink to a directory 400 else if (is_directory(f) && create_symlinks) 401 ec = std::make_error_code(errc::is_a_directory); 402 else if (is_directory(f) && (is_set(options, copy_options::recursive) 403 || options == copy_options::none)) 404 { 405 if (!exists(t)) 406 if (!create_directory(to, from, ec)) 407 return; 408 // set an unused bit in options to disable further recursion 409 if (!is_set(options, copy_options::recursive)) 410 options |= static_cast<copy_options>(4096); 411 for (const directory_entry& x : directory_iterator(from, ec)) 412 { 413 copy(x.path(), to/x.path().filename(), options, ec); 414 if (ec) 415 return; 416 } 417 } 418 // _GLIBCXX_RESOLVE_LIB_DEFECTS 419 // 2683. filesystem::copy() says "no effects" 420 else 421 ec.clear(); 422 #else 423 ec = std::make_error_code(std::errc::function_not_supported); 424 #endif 425 } 426 427 bool 428 fs::copy_file(const path& from, const path& to, copy_options option) 429 { 430 error_code ec; 431 bool result = copy_file(from, to, option, ec); 432 if (ec) 433 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, 434 ec)); 435 return result; 436 } 437 438 bool 439 fs::copy_file(const path& from, const path& to, copy_options options, 440 error_code& ec) 441 { 442 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 443 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 444 nullptr, nullptr, ec); 445 #else 446 ec = std::make_error_code(std::errc::function_not_supported); 447 return false; 448 #endif 449 } 450 451 452 void 453 fs::copy_symlink(const path& existing_symlink, const path& new_symlink) 454 { 455 error_code ec; 456 copy_symlink(existing_symlink, new_symlink, ec); 457 if (ec) 458 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", 459 existing_symlink, new_symlink, ec)); 460 } 461 462 void 463 fs::copy_symlink(const path& existing_symlink, const path& new_symlink, 464 error_code& ec) noexcept 465 { 466 auto p = read_symlink(existing_symlink, ec); 467 if (ec) 468 return; 469 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 470 if (is_directory(p)) 471 { 472 create_directory_symlink(p, new_symlink, ec); 473 return; 474 } 475 #endif 476 create_symlink(p, new_symlink, ec); 477 } 478 479 480 bool 481 fs::create_directories(const path& p) 482 { 483 error_code ec; 484 bool result = create_directories(p, ec); 485 if (ec) 486 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, 487 ec)); 488 return result; 489 } 490 491 bool 492 fs::create_directories(const path& p, error_code& ec) 493 { 494 if (p.empty()) 495 { 496 ec = std::make_error_code(errc::invalid_argument); 497 return false; 498 } 499 500 file_status st = status(p, ec); 501 if (is_directory(st)) 502 return false; 503 else if (ec && !status_known(st)) 504 return false; 505 else if (exists(st)) 506 { 507 if (!ec) 508 ec = std::make_error_code(std::errc::not_a_directory); 509 return false; 510 } 511 512 __glibcxx_assert(st.type() == file_type::not_found); 513 // !exists(p) so there must be at least one non-existent component in p. 514 515 std::stack<path> missing; 516 path pp = p; 517 518 // Strip any trailing slash 519 if (pp.has_relative_path() && !pp.has_filename()) 520 pp = pp.parent_path(); 521 522 do 523 { 524 const auto& filename = pp.filename(); 525 if (is_dot(filename) || is_dotdot(filename)) 526 pp = pp.parent_path(); 527 else 528 { 529 missing.push(std::move(pp)); 530 if (missing.size() > 1000) // sanity check 531 { 532 ec = std::make_error_code(std::errc::filename_too_long); 533 return false; 534 } 535 pp = missing.top().parent_path(); 536 } 537 538 if (pp.empty()) 539 break; 540 541 st = status(pp, ec); 542 if (exists(st)) 543 { 544 if (ec) 545 return false; 546 if (!is_directory(st)) 547 { 548 ec = std::make_error_code(std::errc::not_a_directory); 549 return false; 550 } 551 } 552 553 if (ec && exists(st)) 554 return false; 555 } 556 while (st.type() == file_type::not_found); 557 558 __glibcxx_assert(!missing.empty()); 559 560 bool created; 561 do 562 { 563 const path& top = missing.top(); 564 created = create_directory(top, ec); 565 if (ec) 566 return false; 567 missing.pop(); 568 } 569 while (!missing.empty()); 570 571 return created; 572 } 573 574 namespace 575 { 576 bool 577 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) 578 { 579 bool created = false; 580 #if _GLIBCXX_USE_MKDIR 581 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); 582 if (posix::mkdir(p.c_str(), mode)) 583 { 584 const int err = errno; 585 if (err != EEXIST || !is_directory(p, ec)) 586 ec.assign(err, std::generic_category()); 587 } 588 else 589 { 590 ec.clear(); 591 created = true; 592 } 593 #else 594 ec = std::make_error_code(std::errc::function_not_supported); 595 #endif 596 return created; 597 } 598 } // namespace 599 600 bool 601 fs::create_directory(const path& p) 602 { 603 error_code ec; 604 bool result = create_directory(p, ec); 605 if (ec) 606 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 607 ec)); 608 return result; 609 } 610 611 bool 612 fs::create_directory(const path& p, error_code& ec) noexcept 613 { 614 return create_dir(p, perms::all, ec); 615 } 616 617 618 bool 619 fs::create_directory(const path& p, const path& attributes) 620 { 621 error_code ec; 622 bool result = create_directory(p, attributes, ec); 623 if (ec) 624 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 625 ec)); 626 return result; 627 } 628 629 bool 630 fs::create_directory(const path& p, const path& attributes, 631 error_code& ec) noexcept 632 { 633 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 634 stat_type st; 635 if (posix::stat(attributes.c_str(), &st)) 636 { 637 ec.assign(errno, std::generic_category()); 638 return false; 639 } 640 return create_dir(p, static_cast<perms>(st.st_mode), ec); 641 #else 642 ec = std::make_error_code(std::errc::function_not_supported); 643 return false; 644 #endif 645 } 646 647 648 void 649 fs::create_directory_symlink(const path& to, const path& new_symlink) 650 { 651 error_code ec; 652 create_directory_symlink(to, new_symlink, ec); 653 if (ec) 654 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", 655 to, new_symlink, ec)); 656 } 657 658 void 659 fs::create_directory_symlink(const path& to, const path& new_symlink, 660 error_code& ec) noexcept 661 { 662 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 663 ec = std::make_error_code(std::errc::function_not_supported); 664 #else 665 create_symlink(to, new_symlink, ec); 666 #endif 667 } 668 669 670 void 671 fs::create_hard_link(const path& to, const path& new_hard_link) 672 { 673 error_code ec; 674 create_hard_link(to, new_hard_link, ec); 675 if (ec) 676 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", 677 to, new_hard_link, ec)); 678 } 679 680 void 681 fs::create_hard_link(const path& to, const path& new_hard_link, 682 error_code& ec) noexcept 683 { 684 #ifdef _GLIBCXX_HAVE_LINK 685 if (::link(to.c_str(), new_hard_link.c_str())) 686 ec.assign(errno, std::generic_category()); 687 else 688 ec.clear(); 689 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 690 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL)) 691 ec.clear(); 692 else 693 ec = __last_system_error(); 694 #else 695 ec = std::make_error_code(std::errc::function_not_supported); 696 #endif 697 } 698 699 void 700 fs::create_symlink(const path& to, const path& new_symlink) 701 { 702 error_code ec; 703 create_symlink(to, new_symlink, ec); 704 if (ec) 705 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", 706 to, new_symlink, ec)); 707 } 708 709 void 710 fs::create_symlink(const path& to, const path& new_symlink, 711 error_code& ec) noexcept 712 { 713 #ifdef _GLIBCXX_HAVE_SYMLINK 714 if (::symlink(to.c_str(), new_symlink.c_str())) 715 ec.assign(errno, std::generic_category()); 716 else 717 ec.clear(); 718 #else 719 ec = std::make_error_code(std::errc::function_not_supported); 720 #endif 721 } 722 723 724 fs::path 725 fs::current_path() 726 { 727 error_code ec; 728 path p = current_path(ec); 729 if (ec) 730 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); 731 return p; 732 } 733 734 fs::path 735 fs::current_path(error_code& ec) 736 { 737 path p; 738 #if _GLIBCXX_USE_GETCWD 739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 740 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)}) 741 { 742 p.assign(cwd.get()); 743 ec.clear(); 744 } 745 else 746 ec.assign(errno, std::generic_category()); 747 #else 748 #ifdef _PC_PATH_MAX 749 long path_max = pathconf(".", _PC_PATH_MAX); 750 size_t size; 751 if (path_max == -1) 752 size = 1024; 753 else if (path_max > 10240) 754 size = 10240; 755 else 756 size = path_max; 757 #elif defined(PATH_MAX) 758 size_t size = PATH_MAX; 759 #else 760 size_t size = 1024; 761 #endif 762 for (char_ptr buf; p.empty(); size *= 2) 763 { 764 using char_type = fs::path::value_type; 765 buf.reset((char_type*)malloc(size * sizeof(char_type))); 766 if (buf) 767 { 768 if (getcwd(buf.get(), size)) 769 { 770 p.assign(buf.get()); 771 ec.clear(); 772 } 773 else if (errno != ERANGE) 774 { 775 ec.assign(errno, std::generic_category()); 776 return {}; 777 } 778 } 779 else 780 { 781 ec = std::make_error_code(std::errc::not_enough_memory); 782 return {}; 783 } 784 } 785 #endif // __GLIBC__ 786 #else // _GLIBCXX_USE_GETCWD 787 ec = std::make_error_code(std::errc::function_not_supported); 788 #endif 789 return p; 790 } 791 792 void 793 fs::current_path(const path& p) 794 { 795 error_code ec; 796 current_path(p, ec); 797 if (ec) 798 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); 799 } 800 801 void 802 fs::current_path(const path& p, error_code& ec) noexcept 803 { 804 #if _GLIBCXX_USE_CHDIR 805 if (posix::chdir(p.c_str())) 806 ec.assign(errno, std::generic_category()); 807 else 808 ec.clear(); 809 #else 810 ec = std::make_error_code(std::errc::function_not_supported); 811 #endif 812 } 813 814 bool 815 fs::equivalent(const path& p1, const path& p2) 816 { 817 error_code ec; 818 auto result = equivalent(p1, p2, ec); 819 if (ec) 820 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", 821 p1, p2, ec)); 822 return result; 823 } 824 825 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 826 namespace 827 { 828 // An RAII type that opens a handle for an existing file. 829 struct auto_win_file_handle 830 { 831 explicit 832 auto_win_file_handle(const wchar_t* p, std::error_code& ec) noexcept 833 : handle(CreateFileW(p, 0, 834 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 835 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)), 836 ec(ec) 837 { 838 if (handle == INVALID_HANDLE_VALUE) 839 ec = std::__last_system_error(); 840 } 841 842 ~auto_win_file_handle() 843 { if (*this) CloseHandle(handle); } 844 845 explicit operator bool() const noexcept 846 { return handle != INVALID_HANDLE_VALUE; } 847 848 bool get_info() noexcept 849 { 850 if (GetFileInformationByHandle(handle, &info)) 851 return true; 852 ec = std::__last_system_error(); 853 return false; 854 } 855 856 HANDLE handle; 857 BY_HANDLE_FILE_INFORMATION info; 858 // Like errno, we only set this on error and never clear it. 859 // This propagates an error_code to the caller when something goes wrong, 860 // but the caller should not assume a non-zero ec means an error happened 861 // unless they explicitly cleared it before passing it to our constructor. 862 std::error_code& ec; 863 }; 864 } 865 #endif 866 867 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 868 #ifdef NEED_DO_COPY_FILE // Only define this once, not in cow-fs_ops.o too 869 bool 870 fs::equiv_files([[maybe_unused]] const char_type* p1, const stat_type& st1, 871 [[maybe_unused]] const char_type* p2, const stat_type& st2, 872 [[maybe_unused]] error_code& ec) 873 { 874 #if ! _GLIBCXX_FILESYSTEM_IS_WINDOWS 875 // For POSIX the device ID and inode number uniquely identify a file. 876 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; 877 #else 878 // For Windows st_ino is not set, so can't be used to distinguish files. 879 // We can compare modes and device IDs as a cheap initial check: 880 if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev) 881 return false; 882 883 // Use GetFileInformationByHandle to get more info about the files. 884 if (auto_win_file_handle h1{p1, ec}) 885 if (auto_win_file_handle h2{p2, ec}) 886 if (h1.get_info() && h2.get_info()) 887 return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber 888 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh 889 && h1.info.nFileIndexLow == h2.info.nFileIndexLow; 890 return false; 891 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS 892 } 893 #endif // NEED_DO_COPY_FILE 894 #endif // _GLIBCXX_HAVE_SYS_STAT_H 895 896 bool 897 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept 898 { 899 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 900 int err = 0; 901 file_status s1, s2; 902 stat_type st1, st2; 903 if (posix::stat(p1.c_str(), &st1) == 0) 904 s1 = make_file_status(st1); 905 else if (is_not_found_errno(errno)) 906 s1.type(file_type::not_found); 907 else 908 err = errno; 909 910 if (posix::stat(p2.c_str(), &st2) == 0) 911 s2 = make_file_status(st2); 912 else if (is_not_found_errno(errno)) 913 s2.type(file_type::not_found); 914 else 915 err = errno; 916 917 if (err) 918 ec.assign(err, std::generic_category()); 919 else if (!exists(s1) || !exists(s2)) 920 ec = std::make_error_code(std::errc::no_such_file_or_directory); 921 else 922 { 923 ec.clear(); 924 if (s1.type() == s2.type()) 925 return fs::equiv_files(p1.c_str(), st1, p2.c_str(), st2, ec); 926 } 927 return false; 928 #else 929 ec = std::make_error_code(std::errc::function_not_supported); 930 #endif 931 return false; 932 } 933 934 std::uintmax_t 935 fs::file_size(const path& p) 936 { 937 error_code ec; 938 auto sz = file_size(p, ec); 939 if (ec) 940 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); 941 return sz; 942 } 943 944 namespace 945 { 946 template<typename Accessor, typename T> 947 inline T 948 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) 949 { 950 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 951 posix::stat_type st; 952 if (posix::stat(p.c_str(), &st)) 953 { 954 ec.assign(errno, std::generic_category()); 955 return deflt; 956 } 957 ec.clear(); 958 return f(st); 959 #else 960 ec = std::make_error_code(std::errc::function_not_supported); 961 return deflt; 962 #endif 963 } 964 } 965 966 std::uintmax_t 967 fs::file_size(const path& p, error_code& ec) noexcept 968 { 969 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 970 struct S 971 { 972 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } 973 S() : type(file_type::not_found) { } 974 file_type type; 975 uintmax_t size; 976 }; 977 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); 978 if (s.type == file_type::regular) 979 return s.size; 980 if (!ec) 981 { 982 if (s.type == file_type::directory) 983 ec = std::make_error_code(std::errc::is_a_directory); 984 else 985 ec = std::__unsupported(); 986 } 987 #else 988 ec = std::make_error_code(std::errc::function_not_supported); 989 #endif 990 return -1; 991 } 992 993 std::uintmax_t 994 fs::hard_link_count(const path& p) 995 { 996 error_code ec; 997 auto count = hard_link_count(p, ec); 998 if (ec) 999 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); 1000 return count; 1001 } 1002 1003 std::uintmax_t 1004 fs::hard_link_count(const path& p, error_code& ec) noexcept 1005 { 1006 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1007 auto_win_file_handle h(p.c_str(), ec); 1008 if (h && h.get_info()) 1009 { 1010 ec.clear(); 1011 return static_cast<uintmax_t>(h.info.nNumberOfLinks); 1012 } 1013 return static_cast<uintmax_t>(-1); 1014 #elif defined _GLIBCXX_HAVE_SYS_STAT_H 1015 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), 1016 static_cast<uintmax_t>(-1)); 1017 #else 1018 ec = std::make_error_code(std::errc::function_not_supported); 1019 return static_cast<uintmax_t>(-1); 1020 #endif 1021 } 1022 1023 bool 1024 fs::is_empty(const path& p) 1025 { 1026 error_code ec; 1027 bool e = is_empty(p, ec); 1028 if (ec) 1029 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", 1030 p, ec)); 1031 return e; 1032 } 1033 1034 bool 1035 fs::is_empty(const path& p, error_code& ec) 1036 { 1037 auto s = status(p, ec); 1038 if (ec) 1039 return false; 1040 bool empty = fs::is_directory(s) 1041 ? fs::directory_iterator(p, ec) == fs::directory_iterator() 1042 : fs::file_size(p, ec) == 0; 1043 return ec ? false : empty; 1044 } 1045 1046 fs::file_time_type 1047 fs::last_write_time(const path& p) 1048 { 1049 error_code ec; 1050 auto t = last_write_time(p, ec); 1051 if (ec) 1052 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); 1053 return t; 1054 } 1055 1056 fs::file_time_type 1057 fs::last_write_time(const path& p, error_code& ec) noexcept 1058 { 1059 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1060 return do_stat(p, ec, 1061 [&ec](const auto& st) { 1062 return internal_file_clock::from_stat(st, ec); 1063 }, 1064 file_time_type::min()); 1065 #else 1066 ec = std::make_error_code(std::errc::function_not_supported); 1067 return file_time_type::min(); 1068 #endif 1069 } 1070 1071 void 1072 fs::last_write_time(const path& p, file_time_type new_time) 1073 { 1074 error_code ec; 1075 last_write_time(p, new_time, ec); 1076 if (ec) 1077 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); 1078 } 1079 1080 void 1081 fs::last_write_time(const path& p, 1082 file_time_type new_time, error_code& ec) noexcept 1083 { 1084 auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch(); 1085 auto s = chrono::duration_cast<chrono::seconds>(d); 1086 #if _GLIBCXX_USE_UTIMENSAT 1087 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); 1088 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. 1089 { 1090 --s; 1091 ns += chrono::seconds(1); 1092 } 1093 struct ::timespec ts[2]; 1094 ts[0].tv_sec = 0; 1095 ts[0].tv_nsec = UTIME_OMIT; 1096 ts[1].tv_sec = static_cast<std::time_t>(s.count()); 1097 ts[1].tv_nsec = static_cast<long>(ns.count()); 1098 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) 1099 ec.assign(errno, std::generic_category()); 1100 else 1101 ec.clear(); 1102 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H 1103 posix::utimbuf times; 1104 times.modtime = s.count(); 1105 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, 1106 times.modtime); 1107 if (posix::utime(p.c_str(), ×)) 1108 ec.assign(errno, std::generic_category()); 1109 else 1110 ec.clear(); 1111 #else 1112 ec = std::make_error_code(std::errc::function_not_supported); 1113 #endif 1114 } 1115 1116 void 1117 fs::permissions(const path& p, perms prms, perm_options opts) 1118 { 1119 error_code ec; 1120 permissions(p, prms, opts, ec); 1121 if (ec) 1122 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); 1123 } 1124 1125 void 1126 fs::permissions(const path& p, perms prms, perm_options opts, 1127 error_code& ec) noexcept 1128 { 1129 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD 1130 const bool replace = is_set(opts, perm_options::replace); 1131 const bool add = is_set(opts, perm_options::add); 1132 const bool remove = is_set(opts, perm_options::remove); 1133 const bool nofollow = is_set(opts, perm_options::nofollow); 1134 if (((int)replace + (int)add + (int)remove) != 1) 1135 { 1136 ec = std::make_error_code(std::errc::invalid_argument); 1137 return; 1138 } 1139 1140 prms &= perms::mask; 1141 1142 file_status st; 1143 if (add || remove || nofollow) 1144 { 1145 st = nofollow ? symlink_status(p, ec) : status(p, ec); 1146 if (ec) 1147 return; 1148 auto curr = st.permissions(); 1149 if (add) 1150 prms |= curr; 1151 else if (remove) 1152 prms = curr & ~prms; 1153 } 1154 1155 int err = 0; 1156 #if _GLIBCXX_USE_FCHMODAT 1157 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; 1158 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) 1159 err = errno; 1160 #else 1161 if (nofollow && is_symlink(st)) 1162 ec = std::__unsupported(); 1163 else if (posix::chmod(p.c_str(), static_cast<posix::mode_t>(prms))) 1164 err = errno; 1165 #endif 1166 1167 if (err) 1168 ec.assign(err, std::generic_category()); 1169 else 1170 ec.clear(); 1171 #else 1172 ec = std::make_error_code(std::errc::function_not_supported); 1173 #endif 1174 } 1175 1176 fs::path 1177 fs::proximate(const path& p, const path& base) 1178 { 1179 return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); 1180 } 1181 1182 fs::path 1183 fs::proximate(const path& p, const path& base, error_code& ec) 1184 { 1185 path result; 1186 const auto p2 = weakly_canonical(p, ec); 1187 if (!ec) 1188 { 1189 const auto base2 = weakly_canonical(base, ec); 1190 if (!ec) 1191 result = p2.lexically_proximate(base2); 1192 } 1193 return result; 1194 } 1195 1196 fs::path 1197 fs::read_symlink(const path& p) 1198 { 1199 error_code ec; 1200 path tgt = read_symlink(p, ec); 1201 if (ec) 1202 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); 1203 return tgt; 1204 } 1205 1206 fs::path fs::read_symlink(const path& p, error_code& ec) 1207 { 1208 path result; 1209 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) 1210 stat_type st; 1211 if (posix::lstat(p.c_str(), &st)) 1212 { 1213 ec.assign(errno, std::generic_category()); 1214 return result; 1215 } 1216 else if (!fs::is_symlink(make_file_status(st))) 1217 { 1218 ec.assign(EINVAL, std::generic_category()); 1219 return result; 1220 } 1221 1222 std::string buf; 1223 size_t bufsz = st.st_size ? st.st_size + 1 : 128; 1224 do 1225 { 1226 ssize_t len; 1227 buf.__resize_and_overwrite(bufsz, [&p, &len](char* ptr, size_t n) { 1228 len = ::readlink(p.c_str(), ptr, n); 1229 return size_t(len) < n ? len : 0; 1230 }); 1231 if (buf.size()) 1232 { 1233 result.assign(std::move(buf)); 1234 ec.clear(); 1235 break; 1236 } 1237 else if (len == -1) 1238 { 1239 ec.assign(errno, std::generic_category()); 1240 return result; 1241 } 1242 else if (bufsz > 4096) 1243 { 1244 ec.assign(ENAMETOOLONG, std::generic_category()); 1245 return result; 1246 } 1247 else 1248 bufsz *= 2; 1249 } 1250 while (true); 1251 #else 1252 ec = std::make_error_code(std::errc::function_not_supported); 1253 #endif 1254 return result; 1255 } 1256 1257 fs::path 1258 fs::relative(const path& p, const path& base) 1259 { 1260 return weakly_canonical(p).lexically_relative(weakly_canonical(base)); 1261 } 1262 1263 fs::path 1264 fs::relative(const path& p, const path& base, error_code& ec) 1265 { 1266 auto result = weakly_canonical(p, ec); 1267 fs::path cbase; 1268 if (!ec) 1269 cbase = weakly_canonical(base, ec); 1270 if (!ec) 1271 result = result.lexically_relative(cbase); 1272 if (ec) 1273 result.clear(); 1274 return result; 1275 } 1276 1277 bool 1278 fs::remove(const path& p) 1279 { 1280 error_code ec; 1281 const bool result = fs::remove(p, ec); 1282 if (ec) 1283 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1284 return result; 1285 } 1286 1287 bool 1288 fs::remove(const path& p, error_code& ec) noexcept 1289 { 1290 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1291 auto st = symlink_status(p, ec); 1292 if (exists(st)) 1293 { 1294 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1295 || DeleteFileW(p.c_str())) 1296 { 1297 ec.clear(); 1298 return true; 1299 } 1300 else if (!ec) 1301 ec = __last_system_error(); 1302 } 1303 else if (status_known(st)) 1304 ec.clear(); 1305 #else 1306 if (::remove(p.c_str()) == 0) 1307 { 1308 ec.clear(); 1309 return true; 1310 } 1311 else if (errno == ENOENT) 1312 ec.clear(); 1313 else 1314 ec.assign(errno, std::generic_category()); 1315 #endif 1316 return false; 1317 } 1318 1319 std::uintmax_t 1320 fs::remove_all(const path& p) 1321 { 1322 error_code ec; 1323 uintmax_t count = 0; 1324 recursive_directory_iterator dir(p, directory_options{64|128}, ec); 1325 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() 1326 { 1327 case 0: 1328 // Iterate over the directory removing everything. 1329 { 1330 const recursive_directory_iterator end; 1331 while (dir != end) 1332 { 1333 dir.__erase(); // throws on error 1334 ++count; 1335 } 1336 } 1337 // Directory is empty now, will remove it below. 1338 break; 1339 #ifndef __AVR__ 1340 case ENOENT: 1341 // Our work here is done. 1342 return 0; 1343 case ENOTDIR: 1344 case ELOOP: // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink. 1345 #if defined __FreeBSD__ || defined __DragonFly__ 1346 case EMLINK: // Used instead of ELOOP 1347 #endif 1348 #if defined __NetBSD__ && defined EFTYPE 1349 case EFTYPE: // Used instead of ELOOP 1350 #endif 1351 // Not a directory, will remove below. 1352 break; 1353 #endif 1354 default: 1355 // An error occurred. 1356 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1357 } 1358 1359 // Remove p itself, which is either a non-directory or is now empty. 1360 return count + fs::remove(p); 1361 } 1362 1363 std::uintmax_t 1364 fs::remove_all(const path& p, error_code& ec) 1365 { 1366 uintmax_t count = 0; 1367 recursive_directory_iterator dir(p, directory_options{64|128}, ec); 1368 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() 1369 { 1370 case 0: 1371 // Iterate over the directory removing everything. 1372 { 1373 const recursive_directory_iterator end; 1374 while (dir != end) 1375 { 1376 dir.__erase(&ec); 1377 if (ec) 1378 return -1; 1379 ++count; 1380 } 1381 } 1382 // Directory is empty now, will remove it below. 1383 break; 1384 #ifndef __AVR__ 1385 case ENOENT: 1386 // Our work here is done. 1387 ec.clear(); 1388 return 0; 1389 case ENOTDIR: 1390 case ELOOP: // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink. 1391 #if defined __FreeBSD__ || defined __DragonFly__ 1392 case EMLINK: // Used instead of ELOOP 1393 #endif 1394 #if defined __NetBSD__ && defined EFTYPE 1395 case EFTYPE: // Used instead of ELOOP 1396 #endif 1397 // Not a directory, will remove below. 1398 break; 1399 #endif 1400 default: 1401 // An error occurred. 1402 return -1; 1403 } 1404 1405 // Remove p itself, which is either a non-directory or is now empty. 1406 if (int last = fs::remove(p, ec); !ec) 1407 return count + last; 1408 return -1; 1409 } 1410 1411 void 1412 fs::rename(const path& from, const path& to) 1413 { 1414 error_code ec; 1415 rename(from, to, ec); 1416 if (ec) 1417 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1418 } 1419 1420 void 1421 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1422 { 1423 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1424 const auto to_status = fs::status(to, ec); 1425 if (to_status.type() == file_type::not_found) 1426 ec.clear(); 1427 else if (ec) 1428 return; 1429 1430 if (fs::exists(to_status)) 1431 { 1432 const auto from_status = fs::status(from, ec); 1433 if (ec) 1434 return; 1435 1436 if (fs::is_directory(to_status)) 1437 { 1438 if (!fs::is_directory(from_status)) 1439 { 1440 // Cannot rename a non-directory over an existing directory. 1441 ec = std::make_error_code(std::errc::is_a_directory); 1442 return; 1443 } 1444 } 1445 else if (fs::is_directory(from_status)) 1446 { 1447 // Cannot rename a directory over an existing non-directory. 1448 ec = std::make_error_code(std::errc::not_a_directory); 1449 return; 1450 } 1451 } 1452 #endif 1453 if (posix::rename(from.c_str(), to.c_str())) 1454 ec.assign(errno, std::generic_category()); 1455 else 1456 ec.clear(); 1457 } 1458 1459 void 1460 fs::resize_file(const path& p, uintmax_t size) 1461 { 1462 error_code ec; 1463 resize_file(p, size, ec); 1464 if (ec) 1465 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1466 } 1467 1468 void 1469 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1470 { 1471 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max())) 1472 ec.assign(EINVAL, std::generic_category()); 1473 else if (posix::truncate(p.c_str(), size)) 1474 ec.assign(errno, std::generic_category()); 1475 else 1476 ec.clear(); 1477 } 1478 1479 1480 fs::space_info 1481 fs::space(const path& p) 1482 { 1483 error_code ec; 1484 space_info s = space(p, ec); 1485 if (ec) 1486 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1487 return s; 1488 } 1489 1490 fs::space_info 1491 fs::space(const path& p, error_code& ec) noexcept 1492 { 1493 space_info info = { 1494 static_cast<uintmax_t>(-1), 1495 static_cast<uintmax_t>(-1), 1496 static_cast<uintmax_t>(-1) 1497 }; 1498 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1499 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1500 path dir = absolute(p); 1501 dir.remove_filename(); 1502 auto str = dir.c_str(); 1503 #else 1504 auto str = p.c_str(); 1505 #endif 1506 1507 do_space(str, info.capacity, info.free, info.available, ec); 1508 #endif // _GLIBCXX_HAVE_SYS_STAT_H 1509 1510 return info; 1511 } 1512 1513 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1514 fs::file_status 1515 fs::status(const fs::path& p, error_code& ec) noexcept 1516 { 1517 file_status status; 1518 auto str = p.c_str(); 1519 1520 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1521 // stat() fails if there's a trailing slash (PR 88881) 1522 path p2; 1523 if (p.has_relative_path() && !p.has_filename()) 1524 { 1525 __try 1526 { 1527 p2 = p.parent_path(); 1528 str = p2.c_str(); 1529 } 1530 __catch(const bad_alloc&) 1531 { 1532 ec = std::make_error_code(std::errc::not_enough_memory); 1533 return status; 1534 } 1535 str = p2.c_str(); 1536 } 1537 #endif 1538 1539 stat_type st; 1540 if (posix::stat(str, &st)) 1541 { 1542 int err = errno; 1543 ec.assign(err, std::generic_category()); 1544 if (is_not_found_errno(err)) 1545 status.type(file_type::not_found); 1546 #ifdef EOVERFLOW 1547 else if (err == EOVERFLOW) 1548 status.type(file_type::unknown); 1549 #endif 1550 } 1551 else 1552 { 1553 status = make_file_status(st); 1554 ec.clear(); 1555 } 1556 return status; 1557 } 1558 1559 fs::file_status 1560 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1561 { 1562 file_status status; 1563 auto str = p.c_str(); 1564 1565 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1566 // stat() fails if there's a trailing slash (PR 88881) 1567 path p2; 1568 if (p.has_relative_path() && !p.has_filename()) 1569 { 1570 __try 1571 { 1572 p2 = p.parent_path(); 1573 str = p2.c_str(); 1574 } 1575 __catch(const bad_alloc&) 1576 { 1577 ec = std::make_error_code(std::errc::not_enough_memory); 1578 return status; 1579 } 1580 str = p2.c_str(); 1581 } 1582 #endif 1583 1584 stat_type st; 1585 if (posix::lstat(str, &st)) 1586 { 1587 int err = errno; 1588 ec.assign(err, std::generic_category()); 1589 if (is_not_found_errno(err)) 1590 status.type(file_type::not_found); 1591 } 1592 else 1593 { 1594 status = make_file_status(st); 1595 ec.clear(); 1596 } 1597 return status; 1598 } 1599 #endif 1600 1601 fs::file_status 1602 fs::status(const fs::path& p) 1603 { 1604 std::error_code ec; 1605 auto result = status(p, ec); 1606 if (result.type() == file_type::none) 1607 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1608 return result; 1609 } 1610 1611 fs::file_status 1612 fs::symlink_status(const fs::path& p) 1613 { 1614 std::error_code ec; 1615 auto result = symlink_status(p, ec); 1616 if (result.type() == file_type::none) 1617 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1618 return result; 1619 } 1620 1621 fs::path 1622 fs::temp_directory_path() 1623 { 1624 error_code ec; 1625 path p = fs::get_temp_directory_from_env(ec); 1626 if (!ec) 1627 { 1628 auto st = status(p, ec); 1629 if (!ec && !is_directory(st)) 1630 ec = std::make_error_code(std::errc::not_a_directory); 1631 } 1632 if (ec) 1633 { 1634 if (p.empty()) 1635 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1636 else 1637 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec)); 1638 } 1639 return p; 1640 } 1641 1642 fs::path 1643 fs::temp_directory_path(error_code& ec) 1644 { 1645 path p = fs::get_temp_directory_from_env(ec); 1646 if (!ec) 1647 { 1648 auto st = status(p, ec); 1649 if (ec) 1650 p.clear(); 1651 else if (!is_directory(st)) 1652 { 1653 p.clear(); 1654 ec = std::make_error_code(std::errc::not_a_directory); 1655 } 1656 } 1657 return p; 1658 } 1659 1660 fs::path 1661 fs::weakly_canonical(const path& p) 1662 { 1663 path result; 1664 if (exists(status(p))) 1665 return canonical(p); 1666 1667 path tmp; 1668 auto iter = p.begin(), end = p.end(); 1669 // find leading elements of p that exist: 1670 while (iter != end) 1671 { 1672 tmp = result / *iter; 1673 if (exists(status(tmp))) 1674 swap(result, tmp); 1675 else 1676 break; 1677 ++iter; 1678 } 1679 // canonicalize: 1680 if (!result.empty()) 1681 result = canonical(result); 1682 // append the non-existing elements: 1683 while (iter != end) 1684 result /= *iter++; 1685 // normalize: 1686 return result.lexically_normal(); 1687 } 1688 1689 fs::path 1690 fs::weakly_canonical(const path& p, error_code& ec) 1691 { 1692 path result; 1693 file_status st = status(p, ec); 1694 if (exists(st)) 1695 return canonical(p, ec); 1696 else if (status_known(st)) 1697 ec.clear(); 1698 else 1699 return result; 1700 1701 path tmp; 1702 auto iter = p.begin(), end = p.end(); 1703 // find leading elements of p that exist: 1704 while (iter != end) 1705 { 1706 tmp = result / *iter; 1707 st = status(tmp, ec); 1708 if (exists(st)) 1709 swap(result, tmp); 1710 else 1711 { 1712 if (status_known(st)) 1713 ec.clear(); 1714 break; 1715 } 1716 ++iter; 1717 } 1718 // canonicalize: 1719 if (!ec && !result.empty()) 1720 result = canonical(result, ec); 1721 if (ec) 1722 result.clear(); 1723 else 1724 { 1725 // append the non-existing elements: 1726 while (iter != end) 1727 result /= *iter++; 1728 // normalize: 1729 result = result.lexically_normal(); 1730 } 1731 return result; 1732 } 1733