1 // Filesystem TS operations -*- C++ -*- 2 3 // Copyright (C) 2014-2022 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 <experimental/filesystem> 37 #include <functional> 38 #include <ostream> 39 #include <stack> 40 #include <ext/stdio_filebuf.h> 41 #include <stdlib.h> 42 #include <stdio.h> 43 #include <errno.h> 44 #include <limits.h> // PATH_MAX 45 #ifdef _GLIBCXX_HAVE_FCNTL_H 46 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW 47 #endif 48 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 49 # include <sys/stat.h> // stat, utimensat, fchmodat 50 #endif 51 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H 52 # include <sys/statvfs.h> // statvfs 53 #endif 54 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 55 # include <utime.h> // utime 56 #endif 57 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 58 # include <windows.h> 59 #endif 60 61 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \ 62 namespace experimental { namespace filesystem { 63 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } } 64 #include "ops-common.h" 65 66 #include <filesystem> // std::filesystem::remove_all 67 68 namespace fs = std::experimental::filesystem; 69 namespace posix = std::filesystem::__gnu_posix; 70 71 fs::path 72 fs::absolute(const path& p, const path& base) 73 { 74 const bool has_root_dir = p.has_root_directory(); 75 const bool has_root_name = p.has_root_name(); 76 path abs; 77 if (has_root_dir && has_root_name) 78 abs = p; 79 else 80 { 81 abs = base.is_absolute() ? base : absolute(base); 82 if (has_root_dir) 83 abs = abs.root_name() / p; 84 else if (has_root_name) 85 abs = p.root_name() / abs.root_directory() / abs.relative_path() 86 / p.relative_path(); 87 else 88 abs = abs / p; 89 } 90 return abs; 91 } 92 93 namespace 94 { 95 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 96 inline bool is_dot(wchar_t c) { return c == L'.'; } 97 #else 98 inline bool is_dot(char c) { return c == '.'; } 99 #endif 100 101 inline bool is_dot(const fs::path& path) 102 { 103 const auto& filename = path.native(); 104 return filename.size() == 1 && is_dot(filename[0]); 105 } 106 107 inline bool is_dotdot(const fs::path& path) 108 { 109 const auto& filename = path.native(); 110 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]); 111 } 112 113 struct free_as_in_malloc 114 { 115 void operator()(void* p) const { ::free(p); } 116 }; 117 118 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>; 119 } 120 121 fs::path 122 fs::canonical(const path& p, const path& base, error_code& ec) 123 { 124 const path pa = absolute(p, base); 125 path result; 126 127 #ifdef _GLIBCXX_USE_REALPATH 128 char_ptr buf{ nullptr }; 129 # if _XOPEN_VERSION < 700 130 // Not safe to call realpath(path, NULL) 131 using char_type = fs::path::value_type; 132 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) ); 133 # endif 134 if (char* rp = ::realpath(pa.c_str(), buf.get())) 135 { 136 if (buf == nullptr) 137 buf.reset(rp); 138 result.assign(rp); 139 ec.clear(); 140 return result; 141 } 142 if (errno != ENAMETOOLONG) 143 { 144 ec.assign(errno, std::generic_category()); 145 return result; 146 } 147 #endif 148 149 if (!exists(pa, ec)) 150 { 151 if (!ec) 152 ec = make_error_code(std::errc::no_such_file_or_directory); 153 return result; 154 } 155 // else: we know there are (currently) no unresolvable symlink loops 156 157 result = pa.root_path(); 158 159 deque<path> cmpts; 160 for (auto& f : pa.relative_path()) 161 cmpts.push_back(f); 162 163 int max_allowed_symlinks = 40; 164 165 while (!cmpts.empty() && !ec) 166 { 167 path f = std::move(cmpts.front()); 168 cmpts.pop_front(); 169 170 if (is_dot(f)) 171 { 172 if (!is_directory(result, ec) && !ec) 173 ec.assign(ENOTDIR, std::generic_category()); 174 } 175 else if (is_dotdot(f)) 176 { 177 auto parent = result.parent_path(); 178 if (parent.empty()) 179 result = pa.root_path(); 180 else 181 result.swap(parent); 182 } 183 else 184 { 185 result /= f; 186 187 if (is_symlink(result, ec)) 188 { 189 path link = read_symlink(result, ec); 190 if (!ec) 191 { 192 if (--max_allowed_symlinks == 0) 193 ec.assign(ELOOP, std::generic_category()); 194 else 195 { 196 if (link.is_absolute()) 197 { 198 result = link.root_path(); 199 link = link.relative_path(); 200 } 201 else 202 result.remove_filename(); 203 204 cmpts.insert(cmpts.begin(), link.begin(), link.end()); 205 } 206 } 207 } 208 } 209 } 210 211 if (ec || !exists(result, ec)) 212 result.clear(); 213 214 return result; 215 } 216 217 fs::path 218 fs::canonical(const path& p, error_code& ec) 219 { 220 path cur = current_path(ec); 221 if (ec.value()) 222 return {}; 223 return canonical(p, cur, ec); 224 } 225 226 fs::path 227 fs::canonical(const path& p, const path& base) 228 { 229 error_code ec; 230 path can = canonical(p, base, ec); 231 if (ec) 232 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base, 233 ec)); 234 return can; 235 } 236 237 void 238 fs::copy(const path& from, const path& to, copy_options options) 239 { 240 error_code ec; 241 copy(from, to, options, ec); 242 if (ec.value()) 243 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec)); 244 } 245 246 namespace 247 { 248 using std::filesystem::is_set; 249 250 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 251 using posix::stat_type; 252 253 using std::filesystem::is_not_found_errno; 254 using std::filesystem::file_time; 255 #endif // _GLIBCXX_HAVE_SYS_STAT_H 256 257 } // namespace 258 259 void 260 fs::copy(const path& from, const path& to, copy_options options, 261 error_code& ec) noexcept 262 { 263 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks); 264 const bool create_symlinks = is_set(options, copy_options::create_symlinks); 265 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks); 266 const bool use_lstat = create_symlinks || skip_symlinks; 267 268 file_status f, t; 269 stat_type from_st, to_st; 270 // _GLIBCXX_RESOLVE_LIB_DEFECTS 271 // 2681. filesystem::copy() cannot copy symlinks 272 if (use_lstat || copy_symlinks 273 ? posix::lstat(from.c_str(), &from_st) 274 : posix::stat(from.c_str(), &from_st)) 275 { 276 ec.assign(errno, std::generic_category()); 277 return; 278 } 279 if (use_lstat 280 ? posix::lstat(to.c_str(), &to_st) 281 : posix::stat(to.c_str(), &to_st)) 282 { 283 if (!is_not_found_errno(errno)) 284 { 285 ec.assign(errno, std::generic_category()); 286 return; 287 } 288 t = file_status{file_type::not_found}; 289 } 290 else 291 t = make_file_status(to_st); 292 f = make_file_status(from_st); 293 294 if (exists(t) && !is_other(t) && !is_other(f) 295 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino) 296 { 297 ec = std::make_error_code(std::errc::file_exists); 298 return; 299 } 300 if (is_other(f) || is_other(t)) 301 { 302 ec = std::make_error_code(std::errc::invalid_argument); 303 return; 304 } 305 if (is_directory(f) && is_regular_file(t)) 306 { 307 ec = std::make_error_code(std::errc::is_a_directory); 308 return; 309 } 310 311 if (is_symlink(f)) 312 { 313 if (skip_symlinks) 314 ec.clear(); 315 else if (!exists(t) && copy_symlinks) 316 copy_symlink(from, to, ec); 317 else 318 // Not clear what should be done here. 319 // "Otherwise report an error as specified in Error reporting (7)." 320 ec = std::make_error_code(std::errc::invalid_argument); 321 } 322 else if (is_regular_file(f)) 323 { 324 if (is_set(options, copy_options::directories_only)) 325 ec.clear(); 326 else if (create_symlinks) 327 create_symlink(from, to, ec); 328 else if (is_set(options, copy_options::create_hard_links)) 329 create_hard_link(from, to, ec); 330 else if (is_directory(t)) 331 do_copy_file(from.c_str(), (to / from.filename()).c_str(), 332 copy_file_options(options), &from_st, nullptr, ec); 333 else 334 { 335 auto ptr = exists(t) ? &to_st : &from_st; 336 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 337 &from_st, ptr, ec); 338 } 339 } 340 // _GLIBCXX_RESOLVE_LIB_DEFECTS 341 // 2682. filesystem::copy() won't create a symlink to a directory 342 else if (is_directory(f) && create_symlinks) 343 ec = std::make_error_code(errc::is_a_directory); 344 else if (is_directory(f) && (is_set(options, copy_options::recursive) 345 || options == copy_options::none)) 346 { 347 if (!exists(t)) 348 if (!create_directory(to, from, ec)) 349 return; 350 // set an unused bit in options to disable further recursion 351 if (!is_set(options, copy_options::recursive)) 352 options |= static_cast<copy_options>(4096); 353 for (const directory_entry& x : directory_iterator(from, ec)) 354 { 355 copy(x.path(), to/x.path().filename(), options, ec); 356 if (ec) 357 return; 358 } 359 } 360 // _GLIBCXX_RESOLVE_LIB_DEFECTS 361 // 2683. filesystem::copy() says "no effects" 362 else 363 ec.clear(); 364 } 365 366 bool 367 fs::copy_file(const path& from, const path& to, copy_options option) 368 { 369 error_code ec; 370 bool result = copy_file(from, to, option, ec); 371 if (ec.value()) 372 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, 373 ec)); 374 return result; 375 } 376 377 bool 378 fs::copy_file(const path& from, const path& to, copy_options options, 379 error_code& ec) 380 { 381 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 382 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 383 nullptr, nullptr, ec); 384 #else 385 ec = std::make_error_code(std::errc::function_not_supported); 386 return false; 387 #endif 388 } 389 390 391 void 392 fs::copy_symlink(const path& existing_symlink, const path& new_symlink) 393 { 394 error_code ec; 395 copy_symlink(existing_symlink, new_symlink, ec); 396 if (ec.value()) 397 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", 398 existing_symlink, new_symlink, ec)); 399 } 400 401 void 402 fs::copy_symlink(const path& existing_symlink, const path& new_symlink, 403 error_code& ec) noexcept 404 { 405 auto p = read_symlink(existing_symlink, ec); 406 if (ec.value()) 407 return; 408 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 409 if (is_directory(p)) 410 { 411 create_directory_symlink(p, new_symlink, ec); 412 return; 413 } 414 #endif 415 create_symlink(p, new_symlink, ec); 416 } 417 418 419 bool 420 fs::create_directories(const path& p) 421 { 422 error_code ec; 423 bool result = create_directories(p, ec); 424 if (ec.value()) 425 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, 426 ec)); 427 return result; 428 } 429 430 bool 431 fs::create_directories(const path& p, error_code& ec) 432 { 433 if (p.empty()) 434 { 435 ec = std::make_error_code(errc::invalid_argument); 436 return false; 437 } 438 439 file_status st = status(p, ec); 440 if (is_directory(st)) 441 return false; 442 else if (ec && !status_known(st)) 443 return false; 444 else if (exists(st)) 445 { 446 if (!ec) 447 ec = std::make_error_code(std::errc::not_a_directory); 448 return false; 449 } 450 451 std::stack<path> missing; 452 path pp = p; 453 454 while (!pp.empty() && status(pp, ec).type() == file_type::not_found) 455 { 456 ec.clear(); 457 const auto& filename = pp.filename(); 458 if (!is_dot(filename) && !is_dotdot(filename)) 459 { 460 missing.push(std::move(pp)); 461 pp = missing.top().parent_path(); 462 } 463 else 464 pp = pp.parent_path(); 465 } 466 467 if (ec || missing.empty()) 468 return false; 469 470 bool created; 471 do 472 { 473 const path& top = missing.top(); 474 created = create_directory(top, ec); 475 if (ec) 476 return false; 477 missing.pop(); 478 } 479 while (!missing.empty()); 480 481 return created; 482 } 483 484 namespace 485 { 486 bool 487 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) 488 { 489 bool created = false; 490 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 491 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); 492 if (posix::mkdir(p.c_str(), mode)) 493 { 494 const int err = errno; 495 if (err != EEXIST || !is_directory(p, ec)) 496 ec.assign(err, std::generic_category()); 497 } 498 else 499 { 500 ec.clear(); 501 created = true; 502 } 503 #else 504 ec = std::make_error_code(std::errc::function_not_supported); 505 #endif 506 return created; 507 } 508 } // namespace 509 510 bool 511 fs::create_directory(const path& p) 512 { 513 error_code ec; 514 bool result = create_directory(p, ec); 515 if (ec.value()) 516 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 517 ec)); 518 return result; 519 } 520 521 bool 522 fs::create_directory(const path& p, error_code& ec) noexcept 523 { 524 return create_dir(p, perms::all, ec); 525 } 526 527 528 bool 529 fs::create_directory(const path& p, const path& attributes) 530 { 531 error_code ec; 532 bool result = create_directory(p, attributes, ec); 533 if (ec.value()) 534 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 535 ec)); 536 return result; 537 } 538 539 bool 540 fs::create_directory(const path& p, const path& attributes, 541 error_code& ec) noexcept 542 { 543 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 544 stat_type st; 545 if (posix::stat(attributes.c_str(), &st)) 546 { 547 ec.assign(errno, std::generic_category()); 548 return false; 549 } 550 return create_dir(p, static_cast<perms>(st.st_mode), ec); 551 #else 552 ec = std::make_error_code(std::errc::function_not_supported); 553 return false; 554 #endif 555 } 556 557 558 void 559 fs::create_directory_symlink(const path& to, const path& new_symlink) 560 { 561 error_code ec; 562 create_directory_symlink(to, new_symlink, ec); 563 if (ec.value()) 564 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", 565 to, new_symlink, ec)); 566 } 567 568 void 569 fs::create_directory_symlink(const path& to, const path& new_symlink, 570 error_code& ec) noexcept 571 { 572 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 573 ec = std::make_error_code(std::errc::function_not_supported); 574 #else 575 create_symlink(to, new_symlink, ec); 576 #endif 577 } 578 579 580 void 581 fs::create_hard_link(const path& to, const path& new_hard_link) 582 { 583 error_code ec; 584 create_hard_link(to, new_hard_link, ec); 585 if (ec.value()) 586 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", 587 to, new_hard_link, ec)); 588 } 589 590 void 591 fs::create_hard_link(const path& to, const path& new_hard_link, 592 error_code& ec) noexcept 593 { 594 #ifdef _GLIBCXX_HAVE_LINK 595 if (::link(to.c_str(), new_hard_link.c_str())) 596 ec.assign(errno, std::generic_category()); 597 else 598 ec.clear(); 599 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 600 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL)) 601 ec.clear(); 602 else 603 ec = __last_system_error(); 604 #else 605 ec = std::make_error_code(std::errc::function_not_supported); 606 #endif 607 } 608 609 void 610 fs::create_symlink(const path& to, const path& new_symlink) 611 { 612 error_code ec; 613 create_symlink(to, new_symlink, ec); 614 if (ec.value()) 615 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", 616 to, new_symlink, ec)); 617 } 618 619 void 620 fs::create_symlink(const path& to, const path& new_symlink, 621 error_code& ec) noexcept 622 { 623 #ifdef _GLIBCXX_HAVE_SYMLINK 624 if (::symlink(to.c_str(), new_symlink.c_str())) 625 ec.assign(errno, std::generic_category()); 626 else 627 ec.clear(); 628 #else 629 ec = std::make_error_code(std::errc::function_not_supported); 630 #endif 631 } 632 633 fs::path 634 fs::current_path() 635 { 636 error_code ec; 637 path p = current_path(ec); 638 if (ec.value()) 639 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); 640 return p; 641 } 642 643 fs::path 644 fs::current_path(error_code& ec) 645 { 646 path p; 647 #ifdef _GLIBCXX_HAVE_UNISTD_H 648 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 649 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)}) 650 { 651 p.assign(cwd.get()); 652 ec.clear(); 653 } 654 else 655 ec.assign(errno, std::generic_category()); 656 #else 657 #ifdef _PC_PATH_MAX 658 long path_max = pathconf(".", _PC_PATH_MAX); 659 size_t size; 660 if (path_max == -1) 661 size = 1024; 662 else if (path_max > 10240) 663 size = 10240; 664 else 665 size = path_max; 666 #elif defined(PATH_MAX) 667 size_t size = PATH_MAX; 668 #else 669 size_t size = 1024; 670 #endif 671 for (char_ptr buf; p.empty(); size *= 2) 672 { 673 using char_type = fs::path::value_type; 674 buf.reset((char_type*)malloc(size * sizeof(char_type))); 675 if (buf) 676 { 677 if (getcwd(buf.get(), size)) 678 { 679 p.assign(buf.get()); 680 ec.clear(); 681 } 682 else if (errno != ERANGE) 683 { 684 ec.assign(errno, std::generic_category()); 685 return {}; 686 } 687 } 688 else 689 { 690 ec = std::make_error_code(std::errc::not_enough_memory); 691 return {}; 692 } 693 } 694 #endif // __GLIBC__ 695 #else // _GLIBCXX_HAVE_UNISTD_H 696 ec = std::make_error_code(std::errc::function_not_supported); 697 #endif 698 return p; 699 } 700 701 void 702 fs::current_path(const path& p) 703 { 704 error_code ec; 705 current_path(p, ec); 706 if (ec.value()) 707 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); 708 } 709 710 void 711 fs::current_path(const path& p, error_code& ec) noexcept 712 { 713 #ifdef _GLIBCXX_HAVE_UNISTD_H 714 if (posix::chdir(p.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 bool 724 fs::equivalent(const path& p1, const path& p2) 725 { 726 error_code ec; 727 auto result = equivalent(p1, p2, ec); 728 if (ec) 729 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", 730 p1, p2, ec)); 731 return result; 732 } 733 734 bool 735 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept 736 { 737 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 738 int err = 0; 739 file_status s1, s2; 740 stat_type st1, st2; 741 if (posix::stat(p1.c_str(), &st1) == 0) 742 s1 = make_file_status(st1); 743 else if (is_not_found_errno(errno)) 744 s1.type(file_type::not_found); 745 else 746 err = errno; 747 748 if (posix::stat(p2.c_str(), &st2) == 0) 749 s2 = make_file_status(st2); 750 else if (is_not_found_errno(errno)) 751 s2.type(file_type::not_found); 752 else 753 err = errno; 754 755 if (exists(s1) && exists(s2)) 756 { 757 if (is_other(s1) && is_other(s2)) 758 { 759 ec = std::__unsupported(); 760 return false; 761 } 762 ec.clear(); 763 if (is_other(s1) || is_other(s2)) 764 return false; 765 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; 766 } 767 else if (!exists(s1) && !exists(s2)) 768 ec = std::make_error_code(std::errc::no_such_file_or_directory); 769 else if (err) 770 ec.assign(err, std::generic_category()); 771 else 772 ec.clear(); 773 return false; 774 #else 775 ec = std::make_error_code(std::errc::function_not_supported); 776 #endif 777 return false; 778 } 779 780 std::uintmax_t 781 fs::file_size(const path& p) 782 { 783 error_code ec; 784 auto sz = file_size(p, ec); 785 if (ec.value()) 786 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); 787 return sz; 788 } 789 790 namespace 791 { 792 template<typename Accessor, typename T> 793 inline T 794 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) 795 { 796 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 797 stat_type st; 798 if (posix::stat(p.c_str(), &st)) 799 { 800 ec.assign(errno, std::generic_category()); 801 return deflt; 802 } 803 ec.clear(); 804 return f(st); 805 #else 806 ec = std::make_error_code(std::errc::function_not_supported); 807 return deflt; 808 #endif 809 } 810 } 811 812 std::uintmax_t 813 fs::file_size(const path& p, error_code& ec) noexcept 814 { 815 struct S 816 { 817 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } 818 S() : type(file_type::not_found) { } 819 file_type type; 820 uintmax_t size; 821 }; 822 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); 823 if (s.type == file_type::regular) 824 return s.size; 825 if (!ec) 826 { 827 if (s.type == file_type::directory) 828 ec = std::make_error_code(std::errc::is_a_directory); 829 else 830 ec = std::__unsupported(); 831 } 832 return -1; 833 } 834 835 std::uintmax_t 836 fs::hard_link_count(const path& p) 837 { 838 error_code ec; 839 auto count = hard_link_count(p, ec); 840 if (ec.value()) 841 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); 842 return count; 843 } 844 845 std::uintmax_t 846 fs::hard_link_count(const path& p, error_code& ec) noexcept 847 { 848 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), 849 static_cast<uintmax_t>(-1)); 850 } 851 852 bool 853 fs::is_empty(const path& p) 854 { 855 error_code ec; 856 bool e = is_empty(p, ec); 857 if (ec) 858 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", 859 p, ec)); 860 return e; 861 } 862 863 bool 864 fs::is_empty(const path& p, error_code& ec) noexcept 865 { 866 auto s = status(p, ec); 867 if (ec) 868 return false; 869 bool empty = fs::is_directory(s) 870 ? fs::directory_iterator(p, ec) == fs::directory_iterator() 871 : fs::file_size(p, ec) == 0; 872 return ec ? false : empty; 873 } 874 875 fs::file_time_type 876 fs::last_write_time(const path& p) 877 { 878 error_code ec; 879 auto t = last_write_time(p, ec); 880 if (ec.value()) 881 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); 882 return t; 883 } 884 885 fs::file_time_type 886 fs::last_write_time(const path& p, error_code& ec) noexcept 887 { 888 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); }, 889 file_time_type::min()); 890 } 891 892 void 893 fs::last_write_time(const path& p, file_time_type new_time) 894 { 895 error_code ec; 896 last_write_time(p, new_time, ec); 897 if (ec.value()) 898 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); 899 } 900 901 void 902 fs::last_write_time(const path& p __attribute__((__unused__)), 903 file_time_type new_time, error_code& ec) noexcept 904 { 905 auto d = new_time.time_since_epoch(); 906 auto s = chrono::duration_cast<chrono::seconds>(d); 907 #if _GLIBCXX_USE_UTIMENSAT 908 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); 909 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. 910 { 911 --s; 912 ns += chrono::seconds(1); 913 } 914 struct ::timespec ts[2]; 915 ts[0].tv_sec = 0; 916 ts[0].tv_nsec = UTIME_OMIT; 917 ts[1].tv_sec = static_cast<std::time_t>(s.count()); 918 ts[1].tv_nsec = static_cast<long>(ns.count()); 919 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) 920 ec.assign(errno, std::generic_category()); 921 else 922 ec.clear(); 923 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H 924 posix::utimbuf times; 925 times.modtime = s.count(); 926 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, 927 times.modtime); 928 if (posix::utime(p.c_str(), ×)) 929 ec.assign(errno, std::generic_category()); 930 else 931 ec.clear(); 932 #else 933 ec = std::make_error_code(std::errc::function_not_supported); 934 #endif 935 } 936 937 void 938 fs::permissions(const path& p, perms prms) 939 { 940 error_code ec; 941 permissions(p, prms, ec); 942 if (ec.value()) 943 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); 944 } 945 946 void 947 fs::permissions(const path& p, perms prms, error_code& ec) noexcept 948 { 949 const bool add = is_set(prms, perms::add_perms); 950 const bool remove = is_set(prms, perms::remove_perms); 951 const bool nofollow = is_set(prms, perms::symlink_nofollow); 952 if (add && remove) 953 { 954 ec = std::make_error_code(std::errc::invalid_argument); 955 return; 956 } 957 958 prms &= perms::mask; 959 960 file_status st; 961 if (add || remove || nofollow) 962 { 963 st = nofollow ? symlink_status(p, ec) : status(p, ec); 964 if (ec) 965 return; 966 auto curr = st.permissions(); 967 if (add) 968 prms |= curr; 969 else if (remove) 970 prms = curr & ~prms; 971 } 972 973 int err = 0; 974 #if _GLIBCXX_USE_FCHMODAT 975 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; 976 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) 977 err = errno; 978 #else 979 if (nofollow && is_symlink(st)) 980 ec = std::__unsupported(); 981 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms))) 982 err = errno; 983 #endif 984 985 if (err) 986 ec.assign(err, std::generic_category()); 987 else 988 ec.clear(); 989 } 990 991 fs::path 992 fs::read_symlink(const path& p) 993 { 994 error_code ec; 995 path tgt = read_symlink(p, ec); 996 if (ec.value()) 997 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); 998 return tgt; 999 } 1000 1001 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec) 1002 { 1003 path result; 1004 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) 1005 stat_type st; 1006 if (posix::lstat(p.c_str(), &st)) 1007 { 1008 ec.assign(errno, std::generic_category()); 1009 return result; 1010 } 1011 else if (!fs::is_symlink(make_file_status(st))) 1012 { 1013 ec.assign(EINVAL, std::generic_category()); 1014 return result; 1015 } 1016 1017 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1018 do 1019 { 1020 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1021 if (len == -1) 1022 { 1023 ec.assign(errno, std::generic_category()); 1024 return result; 1025 } 1026 else if (len == (ssize_t)buf.size()) 1027 { 1028 if (buf.size() > 4096) 1029 { 1030 ec.assign(ENAMETOOLONG, std::generic_category()); 1031 return result; 1032 } 1033 buf.resize(buf.size() * 2); 1034 } 1035 else 1036 { 1037 buf.resize(len); 1038 result.assign(buf); 1039 ec.clear(); 1040 break; 1041 } 1042 } 1043 while (true); 1044 #else 1045 ec = std::make_error_code(std::errc::function_not_supported); 1046 #endif 1047 return result; 1048 } 1049 1050 1051 bool 1052 fs::remove(const path& p) 1053 { 1054 error_code ec; 1055 bool result = fs::remove(p, ec); 1056 if (ec) 1057 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1058 return result; 1059 } 1060 1061 bool 1062 fs::remove(const path& p, error_code& ec) noexcept 1063 { 1064 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1065 auto st = symlink_status(p, ec); 1066 if (exists(st)) 1067 { 1068 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1069 || DeleteFileW(p.c_str())) 1070 { 1071 ec.clear(); 1072 return true; 1073 } 1074 else if (!ec) 1075 ec = __last_system_error(); 1076 } 1077 else if (status_known(st)) 1078 ec.clear(); 1079 #else 1080 if (::remove(p.c_str()) == 0) 1081 { 1082 ec.clear(); 1083 return true; 1084 } 1085 else if (errno == ENOENT) 1086 ec.clear(); 1087 else 1088 ec.assign(errno, std::generic_category()); 1089 #endif 1090 return false; 1091 } 1092 1093 1094 std::uintmax_t 1095 fs::remove_all(const path& p) 1096 { 1097 error_code ec; 1098 const auto result = remove_all(p, ec); 1099 if (ec) 1100 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1101 return result; 1102 } 1103 1104 std::uintmax_t 1105 fs::remove_all(const path& p, error_code& ec) 1106 { 1107 // Use the C++17 implementation. 1108 return std::filesystem::remove_all(p.native(), ec); 1109 } 1110 1111 void 1112 fs::rename(const path& from, const path& to) 1113 { 1114 error_code ec; 1115 rename(from, to, ec); 1116 if (ec.value()) 1117 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1118 } 1119 1120 void 1121 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1122 { 1123 if (posix::rename(from.c_str(), to.c_str())) 1124 ec.assign(errno, std::generic_category()); 1125 else 1126 ec.clear(); 1127 } 1128 1129 void 1130 fs::resize_file(const path& p, uintmax_t size) 1131 { 1132 error_code ec; 1133 resize_file(p, size, ec); 1134 if (ec.value()) 1135 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1136 } 1137 1138 void 1139 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1140 { 1141 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max())) 1142 ec.assign(EINVAL, std::generic_category()); 1143 else if (posix::truncate(p.c_str(), size)) 1144 ec.assign(errno, std::generic_category()); 1145 else 1146 ec.clear(); 1147 } 1148 1149 1150 fs::space_info 1151 fs::space(const path& p) 1152 { 1153 error_code ec; 1154 space_info s = space(p, ec); 1155 if (ec.value()) 1156 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1157 return s; 1158 } 1159 1160 fs::space_info 1161 fs::space(const path& p, error_code& ec) noexcept 1162 { 1163 space_info info = { 1164 static_cast<uintmax_t>(-1), 1165 static_cast<uintmax_t>(-1), 1166 static_cast<uintmax_t>(-1) 1167 }; 1168 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1169 path dir = absolute(p); 1170 dir.remove_filename(); 1171 auto str = dir.c_str(); 1172 #else 1173 auto str = p.c_str(); 1174 #endif 1175 fs::do_space(str, info.capacity, info.free, info.available, ec); 1176 return info; 1177 } 1178 1179 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1180 static bool has_trailing_slash(const fs::path& p) 1181 { 1182 wchar_t c = p.native().back(); 1183 return c == '/' || c == L'\\'; 1184 } 1185 #endif 1186 1187 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1188 fs::file_status 1189 fs::status(const fs::path& p, error_code& ec) noexcept 1190 { 1191 file_status status; 1192 1193 auto str = p.c_str(); 1194 1195 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1196 // stat() fails if there's a trailing slash (PR 88881) 1197 path p2; 1198 if (p.has_relative_path() && has_trailing_slash(p)) 1199 { 1200 __try 1201 { 1202 p2 = p.parent_path(); 1203 str = p2.c_str(); 1204 } 1205 __catch(const bad_alloc&) 1206 { 1207 ec = std::make_error_code(std::errc::not_enough_memory); 1208 return status; 1209 } 1210 str = p2.c_str(); 1211 } 1212 #endif 1213 1214 stat_type st; 1215 if (posix::stat(str, &st)) 1216 { 1217 int err = errno; 1218 ec.assign(err, std::generic_category()); 1219 if (is_not_found_errno(err)) 1220 status.type(file_type::not_found); 1221 #ifdef EOVERFLOW 1222 else if (err == EOVERFLOW) 1223 status.type(file_type::unknown); 1224 #endif 1225 } 1226 else 1227 { 1228 status = make_file_status(st); 1229 ec.clear(); 1230 } 1231 return status; 1232 } 1233 1234 fs::file_status 1235 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1236 { 1237 file_status status; 1238 1239 auto str = p.c_str(); 1240 1241 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1242 // stat() fails if there's a trailing slash (PR 88881) 1243 path p2; 1244 if (p.has_relative_path() && has_trailing_slash(p)) 1245 { 1246 __try 1247 { 1248 p2 = p.parent_path(); 1249 str = p2.c_str(); 1250 } 1251 __catch(const bad_alloc&) 1252 { 1253 ec = std::make_error_code(std::errc::not_enough_memory); 1254 return status; 1255 } 1256 str = p2.c_str(); 1257 } 1258 #endif 1259 1260 stat_type st; 1261 if (posix::lstat(str, &st)) 1262 { 1263 int err = errno; 1264 ec.assign(err, std::generic_category()); 1265 if (is_not_found_errno(err)) 1266 status.type(file_type::not_found); 1267 } 1268 else 1269 { 1270 status = make_file_status(st); 1271 ec.clear(); 1272 } 1273 return status; 1274 } 1275 #endif 1276 1277 fs::file_status 1278 fs::status(const fs::path& p) 1279 { 1280 std::error_code ec; 1281 auto result = status(p, ec); 1282 if (result.type() == file_type::none) 1283 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1284 return result; 1285 } 1286 1287 fs::file_status 1288 fs::symlink_status(const fs::path& p) 1289 { 1290 std::error_code ec; 1291 auto result = symlink_status(p, ec); 1292 if (result.type() == file_type::none) 1293 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1294 return result; 1295 } 1296 1297 fs::path 1298 fs::system_complete(const path& p) 1299 { 1300 error_code ec; 1301 path comp = system_complete(p, ec); 1302 if (ec.value()) 1303 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec)); 1304 return comp; 1305 } 1306 1307 fs::path 1308 fs::system_complete(const path& p, error_code& ec) 1309 { 1310 path base = current_path(ec); 1311 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1312 if (p.is_absolute() || !p.has_root_name() 1313 || p.root_name() == base.root_name()) 1314 return absolute(p, base); 1315 // else TODO 1316 ec = std::__unsupported(); 1317 return {}; 1318 #else 1319 if (ec.value()) 1320 return {}; 1321 return absolute(p, base); 1322 #endif 1323 } 1324 1325 fs::path 1326 fs::temp_directory_path() 1327 { 1328 error_code ec; 1329 path tmp = temp_directory_path(ec); 1330 if (ec.value()) 1331 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1332 return tmp; 1333 } 1334 1335 fs::path 1336 fs::temp_directory_path(error_code& ec) 1337 { 1338 path p = fs::get_temp_directory_from_env(ec); 1339 if (ec) 1340 return p; 1341 auto st = status(p, ec); 1342 if (ec) 1343 p.clear(); 1344 else if (!is_directory(st)) 1345 { 1346 p.clear(); 1347 ec = std::make_error_code(std::errc::not_a_directory); 1348 } 1349 return p; 1350 } 1351