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 #if _GLIBCXX_USE_MKDIR 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 #if _GLIBCXX_USE_GETCWD 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 #if _GLIBCXX_USE_CHDIR 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 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD 950 const bool add = is_set(prms, perms::add_perms); 951 const bool remove = is_set(prms, perms::remove_perms); 952 const bool nofollow = is_set(prms, perms::symlink_nofollow); 953 if (add && remove) 954 { 955 ec = std::make_error_code(std::errc::invalid_argument); 956 return; 957 } 958 959 prms &= perms::mask; 960 961 file_status st; 962 if (add || remove || nofollow) 963 { 964 st = nofollow ? symlink_status(p, ec) : status(p, ec); 965 if (ec) 966 return; 967 auto curr = st.permissions(); 968 if (add) 969 prms |= curr; 970 else if (remove) 971 prms = curr & ~prms; 972 } 973 974 int err = 0; 975 #if _GLIBCXX_USE_FCHMODAT 976 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; 977 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) 978 err = errno; 979 #else 980 if (nofollow && is_symlink(st)) 981 ec = std::__unsupported(); 982 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms))) 983 err = errno; 984 #endif 985 986 if (err) 987 ec.assign(err, std::generic_category()); 988 else 989 ec.clear(); 990 #else 991 ec = std::make_error_code(std::errc::function_not_supported); 992 #endif 993 } 994 995 fs::path 996 fs::read_symlink(const path& p) 997 { 998 error_code ec; 999 path tgt = read_symlink(p, ec); 1000 if (ec.value()) 1001 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); 1002 return tgt; 1003 } 1004 1005 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec) 1006 { 1007 path result; 1008 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) 1009 stat_type st; 1010 if (posix::lstat(p.c_str(), &st)) 1011 { 1012 ec.assign(errno, std::generic_category()); 1013 return result; 1014 } 1015 else if (!fs::is_symlink(make_file_status(st))) 1016 { 1017 ec.assign(EINVAL, std::generic_category()); 1018 return result; 1019 } 1020 1021 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1022 do 1023 { 1024 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1025 if (len == -1) 1026 { 1027 ec.assign(errno, std::generic_category()); 1028 return result; 1029 } 1030 else if (len == (ssize_t)buf.size()) 1031 { 1032 if (buf.size() > 4096) 1033 { 1034 ec.assign(ENAMETOOLONG, std::generic_category()); 1035 return result; 1036 } 1037 buf.resize(buf.size() * 2); 1038 } 1039 else 1040 { 1041 buf.resize(len); 1042 result.assign(buf); 1043 ec.clear(); 1044 break; 1045 } 1046 } 1047 while (true); 1048 #else 1049 ec = std::make_error_code(std::errc::function_not_supported); 1050 #endif 1051 return result; 1052 } 1053 1054 1055 bool 1056 fs::remove(const path& p) 1057 { 1058 error_code ec; 1059 bool result = fs::remove(p, ec); 1060 if (ec) 1061 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1062 return result; 1063 } 1064 1065 bool 1066 fs::remove(const path& p, error_code& ec) noexcept 1067 { 1068 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1069 auto st = symlink_status(p, ec); 1070 if (exists(st)) 1071 { 1072 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1073 || DeleteFileW(p.c_str())) 1074 { 1075 ec.clear(); 1076 return true; 1077 } 1078 else if (!ec) 1079 ec = __last_system_error(); 1080 } 1081 else if (status_known(st)) 1082 ec.clear(); 1083 #else 1084 if (::remove(p.c_str()) == 0) 1085 { 1086 ec.clear(); 1087 return true; 1088 } 1089 else if (errno == ENOENT) 1090 ec.clear(); 1091 else 1092 ec.assign(errno, std::generic_category()); 1093 #endif 1094 return false; 1095 } 1096 1097 1098 std::uintmax_t 1099 fs::remove_all(const path& p) 1100 { 1101 error_code ec; 1102 const auto result = remove_all(p, ec); 1103 if (ec) 1104 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1105 return result; 1106 } 1107 1108 std::uintmax_t 1109 fs::remove_all(const path& p, error_code& ec) 1110 { 1111 // Use the C++17 implementation. 1112 return std::filesystem::remove_all(p.native(), ec); 1113 } 1114 1115 void 1116 fs::rename(const path& from, const path& to) 1117 { 1118 error_code ec; 1119 rename(from, to, ec); 1120 if (ec.value()) 1121 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1122 } 1123 1124 void 1125 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1126 { 1127 if (posix::rename(from.c_str(), to.c_str())) 1128 ec.assign(errno, std::generic_category()); 1129 else 1130 ec.clear(); 1131 } 1132 1133 void 1134 fs::resize_file(const path& p, uintmax_t size) 1135 { 1136 error_code ec; 1137 resize_file(p, size, ec); 1138 if (ec.value()) 1139 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1140 } 1141 1142 void 1143 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1144 { 1145 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max())) 1146 ec.assign(EINVAL, std::generic_category()); 1147 else if (posix::truncate(p.c_str(), size)) 1148 ec.assign(errno, std::generic_category()); 1149 else 1150 ec.clear(); 1151 } 1152 1153 1154 fs::space_info 1155 fs::space(const path& p) 1156 { 1157 error_code ec; 1158 space_info s = space(p, ec); 1159 if (ec.value()) 1160 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1161 return s; 1162 } 1163 1164 fs::space_info 1165 fs::space(const path& p, error_code& ec) noexcept 1166 { 1167 space_info info = { 1168 static_cast<uintmax_t>(-1), 1169 static_cast<uintmax_t>(-1), 1170 static_cast<uintmax_t>(-1) 1171 }; 1172 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1173 path dir = absolute(p); 1174 dir.remove_filename(); 1175 auto str = dir.c_str(); 1176 #else 1177 auto str = p.c_str(); 1178 #endif 1179 fs::do_space(str, info.capacity, info.free, info.available, ec); 1180 return info; 1181 } 1182 1183 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1184 static bool has_trailing_slash(const fs::path& p) 1185 { 1186 wchar_t c = p.native().back(); 1187 return c == '/' || c == L'\\'; 1188 } 1189 #endif 1190 1191 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1192 fs::file_status 1193 fs::status(const fs::path& p, error_code& ec) noexcept 1194 { 1195 file_status status; 1196 1197 auto str = p.c_str(); 1198 1199 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1200 // stat() fails if there's a trailing slash (PR 88881) 1201 path p2; 1202 if (p.has_relative_path() && has_trailing_slash(p)) 1203 { 1204 __try 1205 { 1206 p2 = p.parent_path(); 1207 str = p2.c_str(); 1208 } 1209 __catch(const bad_alloc&) 1210 { 1211 ec = std::make_error_code(std::errc::not_enough_memory); 1212 return status; 1213 } 1214 str = p2.c_str(); 1215 } 1216 #endif 1217 1218 stat_type st; 1219 if (posix::stat(str, &st)) 1220 { 1221 int err = errno; 1222 ec.assign(err, std::generic_category()); 1223 if (is_not_found_errno(err)) 1224 status.type(file_type::not_found); 1225 #ifdef EOVERFLOW 1226 else if (err == EOVERFLOW) 1227 status.type(file_type::unknown); 1228 #endif 1229 } 1230 else 1231 { 1232 status = make_file_status(st); 1233 ec.clear(); 1234 } 1235 return status; 1236 } 1237 1238 fs::file_status 1239 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1240 { 1241 file_status status; 1242 1243 auto str = p.c_str(); 1244 1245 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1246 // stat() fails if there's a trailing slash (PR 88881) 1247 path p2; 1248 if (p.has_relative_path() && has_trailing_slash(p)) 1249 { 1250 __try 1251 { 1252 p2 = p.parent_path(); 1253 str = p2.c_str(); 1254 } 1255 __catch(const bad_alloc&) 1256 { 1257 ec = std::make_error_code(std::errc::not_enough_memory); 1258 return status; 1259 } 1260 str = p2.c_str(); 1261 } 1262 #endif 1263 1264 stat_type st; 1265 if (posix::lstat(str, &st)) 1266 { 1267 int err = errno; 1268 ec.assign(err, std::generic_category()); 1269 if (is_not_found_errno(err)) 1270 status.type(file_type::not_found); 1271 } 1272 else 1273 { 1274 status = make_file_status(st); 1275 ec.clear(); 1276 } 1277 return status; 1278 } 1279 #endif 1280 1281 fs::file_status 1282 fs::status(const fs::path& p) 1283 { 1284 std::error_code ec; 1285 auto result = status(p, ec); 1286 if (result.type() == file_type::none) 1287 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1288 return result; 1289 } 1290 1291 fs::file_status 1292 fs::symlink_status(const fs::path& p) 1293 { 1294 std::error_code ec; 1295 auto result = symlink_status(p, ec); 1296 if (result.type() == file_type::none) 1297 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1298 return result; 1299 } 1300 1301 fs::path 1302 fs::system_complete(const path& p) 1303 { 1304 error_code ec; 1305 path comp = system_complete(p, ec); 1306 if (ec.value()) 1307 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec)); 1308 return comp; 1309 } 1310 1311 fs::path 1312 fs::system_complete(const path& p, error_code& ec) 1313 { 1314 path base = current_path(ec); 1315 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1316 if (p.is_absolute() || !p.has_root_name() 1317 || p.root_name() == base.root_name()) 1318 return absolute(p, base); 1319 // else TODO 1320 ec = std::__unsupported(); 1321 return {}; 1322 #else 1323 if (ec.value()) 1324 return {}; 1325 return absolute(p, base); 1326 #endif 1327 } 1328 1329 fs::path 1330 fs::temp_directory_path() 1331 { 1332 error_code ec; 1333 path tmp = temp_directory_path(ec); 1334 if (ec.value()) 1335 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1336 return tmp; 1337 } 1338 1339 fs::path 1340 fs::temp_directory_path(error_code& ec) 1341 { 1342 path p = fs::get_temp_directory_from_env(ec); 1343 if (ec) 1344 return p; 1345 auto st = status(p, ec); 1346 if (ec) 1347 p.clear(); 1348 else if (!is_directory(st)) 1349 { 1350 p.clear(); 1351 ec = std::make_error_code(std::errc::not_a_directory); 1352 } 1353 return p; 1354 } 1355