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