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