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