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