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