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