1// Filesystem TS operations -*- C++ -*- 2 3// Copyright (C) 2014-2020 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, ec)) 348 { 349 copy(x.path(), to/x.path().filename(), options, ec); 350 if (ec) 351 return; 352 } 353 } 354 // _GLIBCXX_RESOLVE_LIB_DEFECTS 355 // 2683. filesystem::copy() says "no effects" 356 else 357 ec.clear(); 358} 359 360bool 361fs::copy_file(const path& from, const path& to, copy_options option) 362{ 363 error_code ec; 364 bool result = copy_file(from, to, option, ec); 365 if (ec.value()) 366 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to, 367 ec)); 368 return result; 369} 370 371bool 372fs::copy_file(const path& from, const path& to, copy_options options, 373 error_code& ec) 374{ 375#ifdef _GLIBCXX_HAVE_SYS_STAT_H 376 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options), 377 nullptr, nullptr, ec); 378#else 379 ec = std::make_error_code(std::errc::not_supported); 380 return false; 381#endif 382} 383 384 385void 386fs::copy_symlink(const path& existing_symlink, const path& new_symlink) 387{ 388 error_code ec; 389 copy_symlink(existing_symlink, new_symlink, ec); 390 if (ec.value()) 391 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink", 392 existing_symlink, new_symlink, ec)); 393} 394 395void 396fs::copy_symlink(const path& existing_symlink, const path& new_symlink, 397 error_code& ec) noexcept 398{ 399 auto p = read_symlink(existing_symlink, ec); 400 if (ec.value()) 401 return; 402#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 403 if (is_directory(p)) 404 { 405 create_directory_symlink(p, new_symlink, ec); 406 return; 407 } 408#endif 409 create_symlink(p, new_symlink, ec); 410} 411 412 413bool 414fs::create_directories(const path& p) 415{ 416 error_code ec; 417 bool result = create_directories(p, ec); 418 if (ec.value()) 419 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p, 420 ec)); 421 return result; 422} 423 424bool 425fs::create_directories(const path& p, error_code& ec) 426{ 427 if (p.empty()) 428 { 429 ec = std::make_error_code(errc::invalid_argument); 430 return false; 431 } 432 433 file_status st = status(p, ec); 434 if (is_directory(st)) 435 return false; 436 else if (ec && !status_known(st)) 437 return false; 438 else if (exists(st)) 439 { 440 if (!ec) 441 ec = std::make_error_code(std::errc::not_a_directory); 442 return false; 443 } 444 445 std::stack<path> missing; 446 path pp = p; 447 448 while (!pp.empty() && status(pp, ec).type() == file_type::not_found) 449 { 450 ec.clear(); 451 const auto& filename = pp.filename(); 452 if (!is_dot(filename) && !is_dotdot(filename)) 453 { 454 missing.push(std::move(pp)); 455 pp = missing.top().parent_path(); 456 } 457 else 458 pp = pp.parent_path(); 459 } 460 461 if (ec || missing.empty()) 462 return false; 463 464 bool created; 465 do 466 { 467 const path& top = missing.top(); 468 created = create_directory(top, ec); 469 if (ec) 470 return false; 471 missing.pop(); 472 } 473 while (!missing.empty()); 474 475 return created; 476} 477 478namespace 479{ 480 bool 481 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec) 482 { 483 bool created = false; 484#ifdef _GLIBCXX_HAVE_SYS_STAT_H 485 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm); 486 if (posix::mkdir(p.c_str(), mode)) 487 { 488 const int err = errno; 489 if (err != EEXIST || !is_directory(p, ec)) 490 ec.assign(err, std::generic_category()); 491 } 492 else 493 { 494 ec.clear(); 495 created = true; 496 } 497#else 498 ec = std::make_error_code(std::errc::not_supported); 499#endif 500 return created; 501 } 502} // namespace 503 504bool 505fs::create_directory(const path& p) 506{ 507 error_code ec; 508 bool result = create_directory(p, ec); 509 if (ec.value()) 510 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 511 ec)); 512 return result; 513} 514 515bool 516fs::create_directory(const path& p, error_code& ec) noexcept 517{ 518 return create_dir(p, perms::all, ec); 519} 520 521 522bool 523fs::create_directory(const path& p, const path& attributes) 524{ 525 error_code ec; 526 bool result = create_directory(p, attributes, ec); 527 if (ec.value()) 528 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p, 529 ec)); 530 return result; 531} 532 533bool 534fs::create_directory(const path& p, const path& attributes, 535 error_code& ec) noexcept 536{ 537#ifdef _GLIBCXX_HAVE_SYS_STAT_H 538 stat_type st; 539 if (posix::stat(attributes.c_str(), &st)) 540 { 541 ec.assign(errno, std::generic_category()); 542 return false; 543 } 544 return create_dir(p, static_cast<perms>(st.st_mode), ec); 545#else 546 ec = std::make_error_code(std::errc::not_supported); 547 return false; 548#endif 549} 550 551 552void 553fs::create_directory_symlink(const path& to, const path& new_symlink) 554{ 555 error_code ec; 556 create_directory_symlink(to, new_symlink, ec); 557 if (ec.value()) 558 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink", 559 to, new_symlink, ec)); 560} 561 562void 563fs::create_directory_symlink(const path& to, const path& new_symlink, 564 error_code& ec) noexcept 565{ 566#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 567 ec = std::make_error_code(std::errc::not_supported); 568#else 569 create_symlink(to, new_symlink, ec); 570#endif 571} 572 573 574void 575fs::create_hard_link(const path& to, const path& new_hard_link) 576{ 577 error_code ec; 578 create_hard_link(to, new_hard_link, ec); 579 if (ec.value()) 580 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link", 581 to, new_hard_link, ec)); 582} 583 584void 585fs::create_hard_link(const path& to, const path& new_hard_link, 586 error_code& ec) noexcept 587{ 588#ifdef _GLIBCXX_HAVE_LINK 589 if (::link(to.c_str(), new_hard_link.c_str())) 590 ec.assign(errno, std::generic_category()); 591 else 592 ec.clear(); 593#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 594 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL)) 595 ec.clear(); 596 else 597 ec.assign((int)GetLastError(), system_category()); 598#else 599 ec = std::make_error_code(std::errc::not_supported); 600#endif 601} 602 603void 604fs::create_symlink(const path& to, const path& new_symlink) 605{ 606 error_code ec; 607 create_symlink(to, new_symlink, ec); 608 if (ec.value()) 609 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink", 610 to, new_symlink, ec)); 611} 612 613void 614fs::create_symlink(const path& to, const path& new_symlink, 615 error_code& ec) noexcept 616{ 617#ifdef _GLIBCXX_HAVE_SYMLINK 618 if (::symlink(to.c_str(), new_symlink.c_str())) 619 ec.assign(errno, std::generic_category()); 620 else 621 ec.clear(); 622#else 623 ec = std::make_error_code(std::errc::not_supported); 624#endif 625} 626 627fs::path 628fs::current_path() 629{ 630 error_code ec; 631 path p = current_path(ec); 632 if (ec.value()) 633 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec)); 634 return p; 635} 636 637fs::path 638fs::current_path(error_code& ec) 639{ 640 path p; 641#ifdef _GLIBCXX_HAVE_UNISTD_H 642#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 643 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)}) 644 { 645 p.assign(cwd.get()); 646 ec.clear(); 647 } 648 else 649 ec.assign(errno, std::generic_category()); 650#else 651#ifdef _PC_PATH_MAX 652 long path_max = pathconf(".", _PC_PATH_MAX); 653 size_t size; 654 if (path_max == -1) 655 size = 1024; 656 else if (path_max > 10240) 657 size = 10240; 658 else 659 size = path_max; 660#elif defined(PATH_MAX) 661 size_t size = PATH_MAX; 662#else 663 size_t size = 1024; 664#endif 665 for (char_ptr buf; p.empty(); size *= 2) 666 { 667 using char_type = fs::path::value_type; 668 buf.reset((char_type*)malloc(size * sizeof(char_type))); 669 if (buf) 670 { 671 if (getcwd(buf.get(), size)) 672 { 673 p.assign(buf.get()); 674 ec.clear(); 675 } 676 else if (errno != ERANGE) 677 { 678 ec.assign(errno, std::generic_category()); 679 return {}; 680 } 681 } 682 else 683 { 684 ec = std::make_error_code(std::errc::not_enough_memory); 685 return {}; 686 } 687 } 688#endif // __GLIBC__ 689#else // _GLIBCXX_HAVE_UNISTD_H 690 ec = std::make_error_code(std::errc::not_supported); 691#endif 692 return p; 693} 694 695void 696fs::current_path(const path& p) 697{ 698 error_code ec; 699 current_path(p, ec); 700 if (ec.value()) 701 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec)); 702} 703 704void 705fs::current_path(const path& p, error_code& ec) noexcept 706{ 707#ifdef _GLIBCXX_HAVE_UNISTD_H 708 if (posix::chdir(p.c_str())) 709 ec.assign(errno, std::generic_category()); 710 else 711 ec.clear(); 712#else 713 ec = std::make_error_code(std::errc::not_supported); 714#endif 715} 716 717bool 718fs::equivalent(const path& p1, const path& p2) 719{ 720 error_code ec; 721 auto result = equivalent(p1, p2, ec); 722 if (ec) 723 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", 724 p1, p2, ec)); 725 return result; 726} 727 728bool 729fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept 730{ 731#ifdef _GLIBCXX_HAVE_SYS_STAT_H 732 int err = 0; 733 file_status s1, s2; 734 stat_type st1, st2; 735 if (posix::stat(p1.c_str(), &st1) == 0) 736 s1 = make_file_status(st1); 737 else if (is_not_found_errno(errno)) 738 s1.type(file_type::not_found); 739 else 740 err = errno; 741 742 if (posix::stat(p2.c_str(), &st2) == 0) 743 s2 = make_file_status(st2); 744 else if (is_not_found_errno(errno)) 745 s2.type(file_type::not_found); 746 else 747 err = errno; 748 749 if (exists(s1) && exists(s2)) 750 { 751 if (is_other(s1) && is_other(s2)) 752 { 753 ec = std::make_error_code(std::errc::not_supported); 754 return false; 755 } 756 ec.clear(); 757 if (is_other(s1) || is_other(s2)) 758 return false; 759 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; 760 } 761 else if (!exists(s1) && !exists(s2)) 762 ec = std::make_error_code(std::errc::no_such_file_or_directory); 763 else if (err) 764 ec.assign(err, std::generic_category()); 765 else 766 ec.clear(); 767 return false; 768#else 769 ec = std::make_error_code(std::errc::not_supported); 770#endif 771 return false; 772} 773 774std::uintmax_t 775fs::file_size(const path& p) 776{ 777 error_code ec; 778 auto sz = file_size(p, ec); 779 if (ec.value()) 780 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec)); 781 return sz; 782} 783 784namespace 785{ 786 template<typename Accessor, typename T> 787 inline T 788 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt) 789 { 790#ifdef _GLIBCXX_HAVE_SYS_STAT_H 791 stat_type st; 792 if (posix::stat(p.c_str(), &st)) 793 { 794 ec.assign(errno, std::generic_category()); 795 return deflt; 796 } 797 ec.clear(); 798 return f(st); 799#else 800 ec = std::make_error_code(std::errc::not_supported); 801 return deflt; 802#endif 803 } 804} 805 806std::uintmax_t 807fs::file_size(const path& p, error_code& ec) noexcept 808{ 809 struct S 810 { 811 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { } 812 S() : type(file_type::not_found) { } 813 file_type type; 814 uintmax_t size; 815 }; 816 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{}); 817 if (s.type == file_type::regular) 818 return s.size; 819 if (!ec) 820 { 821 if (s.type == file_type::directory) 822 ec = std::make_error_code(std::errc::is_a_directory); 823 else 824 ec = std::make_error_code(std::errc::not_supported); 825 } 826 return -1; 827} 828 829std::uintmax_t 830fs::hard_link_count(const path& p) 831{ 832 error_code ec; 833 auto count = hard_link_count(p, ec); 834 if (ec.value()) 835 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec)); 836 return count; 837} 838 839std::uintmax_t 840fs::hard_link_count(const path& p, error_code& ec) noexcept 841{ 842 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), 843 static_cast<uintmax_t>(-1)); 844} 845 846bool 847fs::is_empty(const path& p) 848{ 849 error_code ec; 850 bool e = is_empty(p, ec); 851 if (ec) 852 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", 853 p, ec)); 854 return e; 855} 856 857bool 858fs::is_empty(const path& p, error_code& ec) noexcept 859{ 860 auto s = status(p, ec); 861 if (ec) 862 return false; 863 bool empty = fs::is_directory(s) 864 ? fs::directory_iterator(p, ec) == fs::directory_iterator() 865 : fs::file_size(p, ec) == 0; 866 return ec ? false : empty; 867} 868 869fs::file_time_type 870fs::last_write_time(const path& p) 871{ 872 error_code ec; 873 auto t = last_write_time(p, ec); 874 if (ec.value()) 875 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec)); 876 return t; 877} 878 879fs::file_time_type 880fs::last_write_time(const path& p, error_code& ec) noexcept 881{ 882 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); }, 883 file_time_type::min()); 884} 885 886void 887fs::last_write_time(const path& p, file_time_type new_time) 888{ 889 error_code ec; 890 last_write_time(p, new_time, ec); 891 if (ec.value()) 892 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec)); 893} 894 895void 896fs::last_write_time(const path& p __attribute__((__unused__)), 897 file_time_type new_time, error_code& ec) noexcept 898{ 899 auto d = new_time.time_since_epoch(); 900 auto s = chrono::duration_cast<chrono::seconds>(d); 901#if _GLIBCXX_USE_UTIMENSAT 902 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); 903 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. 904 { 905 --s; 906 ns += chrono::seconds(1); 907 } 908 struct ::timespec ts[2]; 909 ts[0].tv_sec = 0; 910 ts[0].tv_nsec = UTIME_OMIT; 911 ts[1].tv_sec = static_cast<std::time_t>(s.count()); 912 ts[1].tv_nsec = static_cast<long>(ns.count()); 913 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0)) 914 ec.assign(errno, std::generic_category()); 915 else 916 ec.clear(); 917#elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H 918 posix::utimbuf times; 919 times.modtime = s.count(); 920 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; }, 921 times.modtime); 922 if (posix::utime(p.c_str(), ×)) 923 ec.assign(errno, std::generic_category()); 924 else 925 ec.clear(); 926#else 927 ec = std::make_error_code(std::errc::not_supported); 928#endif 929} 930 931void 932fs::permissions(const path& p, perms prms) 933{ 934 error_code ec; 935 permissions(p, prms, ec); 936 if (ec.value()) 937 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); 938} 939 940void 941fs::permissions(const path& p, perms prms, error_code& ec) noexcept 942{ 943 const bool add = is_set(prms, perms::add_perms); 944 const bool remove = is_set(prms, perms::remove_perms); 945 const bool nofollow = is_set(prms, perms::symlink_nofollow); 946 if (add && remove) 947 { 948 ec = std::make_error_code(std::errc::invalid_argument); 949 return; 950 } 951 952 prms &= perms::mask; 953 954 file_status st; 955 if (add || remove || nofollow) 956 { 957 st = nofollow ? symlink_status(p, ec) : status(p, ec); 958 if (ec) 959 return; 960 auto curr = st.permissions(); 961 if (add) 962 prms |= curr; 963 else if (remove) 964 prms = curr & ~prms; 965 } 966 967 int err = 0; 968#if _GLIBCXX_USE_FCHMODAT 969 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; 970 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) 971 err = errno; 972#else 973 if (nofollow && is_symlink(st)) 974 ec = std::make_error_code(std::errc::operation_not_supported); 975 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms))) 976 err = errno; 977#endif 978 979 if (err) 980 ec.assign(err, std::generic_category()); 981 else 982 ec.clear(); 983} 984 985fs::path 986fs::read_symlink(const path& p) 987{ 988 error_code ec; 989 path tgt = read_symlink(p, ec); 990 if (ec.value()) 991 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec)); 992 return tgt; 993} 994 995fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec) 996{ 997 path result; 998#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H) 999 stat_type st; 1000 if (posix::lstat(p.c_str(), &st)) 1001 { 1002 ec.assign(errno, std::generic_category()); 1003 return result; 1004 } 1005 else if (!fs::is_symlink(make_file_status(st))) 1006 { 1007 ec.assign(EINVAL, std::generic_category()); 1008 return result; 1009 } 1010 1011 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1012 do 1013 { 1014 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1015 if (len == -1) 1016 { 1017 ec.assign(errno, std::generic_category()); 1018 return result; 1019 } 1020 else if (len == (ssize_t)buf.size()) 1021 { 1022 if (buf.size() > 4096) 1023 { 1024 ec.assign(ENAMETOOLONG, std::generic_category()); 1025 return result; 1026 } 1027 buf.resize(buf.size() * 2); 1028 } 1029 else 1030 { 1031 buf.resize(len); 1032 result.assign(buf); 1033 ec.clear(); 1034 break; 1035 } 1036 } 1037 while (true); 1038#else 1039 ec = std::make_error_code(std::errc::not_supported); 1040#endif 1041 return result; 1042} 1043 1044 1045bool 1046fs::remove(const path& p) 1047{ 1048 error_code ec; 1049 bool result = fs::remove(p, ec); 1050 if (ec) 1051 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1052 return result; 1053} 1054 1055bool 1056fs::remove(const path& p, error_code& ec) noexcept 1057{ 1058#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1059 auto st = symlink_status(p, ec); 1060 if (exists(st)) 1061 { 1062 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1063 || DeleteFileW(p.c_str())) 1064 { 1065 ec.clear(); 1066 return true; 1067 } 1068 else if (!ec) 1069 ec.assign((int)GetLastError(), system_category()); 1070 } 1071 else if (status_known(st)) 1072 ec.clear(); 1073#else 1074 if (::remove(p.c_str()) == 0) 1075 { 1076 ec.clear(); 1077 return true; 1078 } 1079 else if (errno == ENOENT) 1080 ec.clear(); 1081 else 1082 ec.assign(errno, std::generic_category()); 1083#endif 1084 return false; 1085} 1086 1087 1088std::uintmax_t 1089fs::remove_all(const path& p) 1090{ 1091 error_code ec; 1092 const auto result = remove_all(p, ec); 1093 if (ec) 1094 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1095 return result; 1096} 1097 1098std::uintmax_t 1099fs::remove_all(const path& p, error_code& ec) 1100{ 1101 const auto s = symlink_status(p, ec); 1102 if (!status_known(s)) 1103 return -1; 1104 1105 ec.clear(); 1106 if (s.type() == file_type::not_found) 1107 return 0; 1108 1109 uintmax_t count = 0; 1110 if (s.type() == file_type::directory) 1111 { 1112 directory_iterator d(p, ec), end; 1113 while (!ec && d != end) 1114 { 1115 const auto removed = fs::remove_all(d->path(), ec); 1116 if (removed == numeric_limits<uintmax_t>::max()) 1117 return -1; 1118 count += removed; 1119 d.increment(ec); 1120 if (ec) 1121 return -1; 1122 } 1123 } 1124 1125 if (fs::remove(p, ec)) 1126 ++count; 1127 return ec ? -1 : count; 1128} 1129 1130void 1131fs::rename(const path& from, const path& to) 1132{ 1133 error_code ec; 1134 rename(from, to, ec); 1135 if (ec.value()) 1136 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1137} 1138 1139void 1140fs::rename(const path& from, const path& to, error_code& ec) noexcept 1141{ 1142 if (posix::rename(from.c_str(), to.c_str())) 1143 ec.assign(errno, std::generic_category()); 1144 else 1145 ec.clear(); 1146} 1147 1148void 1149fs::resize_file(const path& p, uintmax_t size) 1150{ 1151 error_code ec; 1152 resize_file(p, size, ec); 1153 if (ec.value()) 1154 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1155} 1156 1157void 1158fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1159{ 1160#ifdef _GLIBCXX_HAVE_UNISTD_H 1161 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) 1162 ec.assign(EINVAL, std::generic_category()); 1163 else if (posix::truncate(p.c_str(), size)) 1164 ec.assign(errno, std::generic_category()); 1165 else 1166 ec.clear(); 1167#else 1168 ec = std::make_error_code(std::errc::not_supported); 1169#endif 1170} 1171 1172 1173fs::space_info 1174fs::space(const path& p) 1175{ 1176 error_code ec; 1177 space_info s = space(p, ec); 1178 if (ec.value()) 1179 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1180 return s; 1181} 1182 1183fs::space_info 1184fs::space(const path& p, error_code& ec) noexcept 1185{ 1186 space_info info = { 1187 static_cast<uintmax_t>(-1), 1188 static_cast<uintmax_t>(-1), 1189 static_cast<uintmax_t>(-1) 1190 }; 1191#if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1192 path dir = absolute(p); 1193 dir.remove_filename(); 1194 auto str = dir.c_str(); 1195#else 1196 auto str = p.c_str(); 1197#endif 1198 fs::do_space(str, info.capacity, info.free, info.available, ec); 1199 return info; 1200} 1201 1202#ifdef _GLIBCXX_HAVE_SYS_STAT_H 1203fs::file_status 1204fs::status(const fs::path& p, error_code& ec) noexcept 1205{ 1206 file_status status; 1207 stat_type st; 1208 if (posix::stat(p.c_str(), &st)) 1209 { 1210 int err = errno; 1211 ec.assign(err, std::generic_category()); 1212 if (is_not_found_errno(err)) 1213 status.type(file_type::not_found); 1214#ifdef EOVERFLOW 1215 else if (err == EOVERFLOW) 1216 status.type(file_type::unknown); 1217#endif 1218 } 1219 else 1220 { 1221 status = make_file_status(st); 1222 ec.clear(); 1223 } 1224 return status; 1225} 1226 1227fs::file_status 1228fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1229{ 1230 file_status status; 1231 stat_type st; 1232 if (posix::lstat(p.c_str(), &st)) 1233 { 1234 int err = errno; 1235 ec.assign(err, std::generic_category()); 1236 if (is_not_found_errno(err)) 1237 status.type(file_type::not_found); 1238 } 1239 else 1240 { 1241 status = make_file_status(st); 1242 ec.clear(); 1243 } 1244 return status; 1245} 1246#endif 1247 1248fs::file_status 1249fs::status(const fs::path& p) 1250{ 1251 std::error_code ec; 1252 auto result = status(p, ec); 1253 if (result.type() == file_type::none) 1254 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1255 return result; 1256} 1257 1258fs::file_status 1259fs::symlink_status(const fs::path& p) 1260{ 1261 std::error_code ec; 1262 auto result = symlink_status(p, ec); 1263 if (result.type() == file_type::none) 1264 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1265 return result; 1266} 1267 1268fs::path 1269fs::system_complete(const path& p) 1270{ 1271 error_code ec; 1272 path comp = system_complete(p, ec); 1273 if (ec.value()) 1274 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec)); 1275 return comp; 1276} 1277 1278fs::path 1279fs::system_complete(const path& p, error_code& ec) 1280{ 1281 path base = current_path(ec); 1282#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1283 if (p.is_absolute() || !p.has_root_name() 1284 || p.root_name() == base.root_name()) 1285 return absolute(p, base); 1286 // else TODO 1287 ec = std::make_error_code(std::errc::not_supported); 1288 return {}; 1289#else 1290 if (ec.value()) 1291 return {}; 1292 return absolute(p, base); 1293#endif 1294} 1295 1296fs::path fs::temp_directory_path() 1297{ 1298 error_code ec; 1299 path tmp = temp_directory_path(ec); 1300 if (ec.value()) 1301 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1302 return tmp; 1303} 1304 1305fs::path fs::temp_directory_path(error_code& ec) 1306{ 1307 path p; 1308#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1309 unsigned len = 1024; 1310 std::wstring buf; 1311 do 1312 { 1313 buf.resize(len); 1314 len = GetTempPathW(buf.size(), buf.data()); 1315 } while (len > buf.size()); 1316 1317 if (len == 0) 1318 { 1319 ec.assign((int)GetLastError(), std::system_category()); 1320 return p; 1321 } 1322 buf.resize(len); 1323 p = std::move(buf); 1324#else 1325 const char* tmpdir = nullptr; 1326 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; 1327 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) 1328 tmpdir = ::getenv(*e); 1329 p = tmpdir ? tmpdir : "/tmp"; 1330 auto st = status(p, ec); 1331 if (ec) 1332 p.clear(); 1333 else if (!is_directory(st)) 1334 { 1335 p.clear(); 1336 ec = std::make_error_code(std::errc::not_a_directory); 1337 } 1338#endif 1339 return p; 1340} 1341 1342