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 66namespace fs = std::experimental::filesystem; 67 68fs::path 69fs::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 90namespace 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 118fs::path 119fs::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 209fs::path 210fs::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 218fs::path 219fs::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 229void 230fs::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 238namespace 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 248namespace 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 466void 467fs::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 559bool 560fs::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 570bool 571fs::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 583void 584fs::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 593void 594fs::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 611bool 612fs::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 622bool 623fs::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 658namespace 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 686bool 687fs::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 697bool 698fs::create_directory(const path& p, error_code& ec) noexcept 699{ 700 return create_dir(p, perms::all, ec); 701} 702 703 704bool 705fs::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 715bool 716fs::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 734void 735fs::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 744void 745fs::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 756void 757fs::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 766void 767fs::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 780void 781fs::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 790void 791fs::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 805fs::path 806fs::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 815fs::path 816fs::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 866void 867fs::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 875void 876fs::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 888bool 889fs::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 899bool 900fs::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 928std::uintmax_t 929fs::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 938namespace 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 960std::uintmax_t 961fs::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 983std::uintmax_t 984fs::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 993std::uintmax_t 994fs::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 1000bool 1001fs::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 1008bool 1009fs::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 1019fs::file_time_type 1020fs::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 1029fs::file_time_type 1030fs::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 1036void 1037fs::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 1045void 1046fs::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 1076void 1077fs::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 1085void 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 1097fs::path 1098fs::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 1107fs::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 1131bool 1132fs::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 1141bool 1142fs::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 1158std::uintmax_t 1159fs::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 1168std::uintmax_t 1169fs::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 1181void 1182fs::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 1190void 1191fs::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 1199void 1200fs::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 1208void 1209fs::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 1224fs::space_info 1225fs::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 1234fs::space_info 1235fs::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 1262fs::file_status 1263fs::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 1282fs::file_status 1283fs::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 1303fs::file_status 1304fs::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 1313fs::file_status 1314fs::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 1323fs::path 1324fs::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 1333fs::path 1334fs::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 1351fs::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 1360fs::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