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