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 62 namespace fs = std::experimental::filesystem; 63 namespace posix = std::filesystem::__gnu_posix; 64 65 fs::path absolute(const path & p,const path & base)66 fs::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 87 namespace 88 { 89 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS is_dot(wchar_t c)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 is_dot(const fs::path & path)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 is_dotdot(const fs::path & path)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 { operator ()__anon536881820111::free_as_in_malloc109 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 115 fs::path canonical(const path & p,const path & base,error_code & ec)116 fs::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 211 fs::path canonical(const path & p,error_code & ec)212 fs::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 220 fs::path canonical(const path & p,const path & base)221 fs::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 231 void copy(const path & from,const path & to,copy_options options)232 fs::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 240 namespace 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 253 void copy(const path & from,const path & to,copy_options options,error_code & ec)254 fs::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 356 bool copy_file(const path & from,const path & to,copy_options option)357 fs::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 367 bool copy_file(const path & from,const path & to,copy_options options,error_code & ec)368 fs::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 381 void copy_symlink(const path & existing_symlink,const path & new_symlink)382 fs::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 391 void copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)392 fs::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 409 bool create_directories(const path & p)410 fs::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 420 bool create_directories(const path & p,error_code & ec)421 fs::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 = 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 474 namespace 475 { 476 bool create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)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 500 bool create_directory(const path & p)501 fs::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 511 bool create_directory(const path & p,error_code & ec)512 fs::create_directory(const path& p, error_code& ec) noexcept 513 { 514 return create_dir(p, perms::all, ec); 515 } 516 517 518 bool create_directory(const path & p,const path & attributes)519 fs::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 529 bool create_directory(const path & p,const path & attributes,error_code & ec)530 fs::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 548 void create_directory_symlink(const path & to,const path & new_symlink)549 fs::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 558 void create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)559 fs::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 570 void create_hard_link(const path & to,const path & new_hard_link)571 fs::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 580 void create_hard_link(const path & to,const path & new_hard_link,error_code & ec)581 fs::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(), system_category()); 594 #else 595 ec = std::make_error_code(std::errc::not_supported); 596 #endif 597 } 598 599 void create_symlink(const path & to,const path & new_symlink)600 fs::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 609 void create_symlink(const path & to,const path & new_symlink,error_code & ec)610 fs::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 623 fs::path current_path()624 fs::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 633 fs::path current_path(error_code & ec)634 fs::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 691 void current_path(const path & p)692 fs::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 700 void current_path(const path & p,error_code & ec)701 fs::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 713 bool equivalent(const path & p1,const path & p2)714 fs::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 724 bool equivalent(const path & p1,const path & p2,error_code & ec)725 fs::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 770 std::uintmax_t file_size(const path & p)771 fs::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 780 namespace 781 { 782 template<typename Accessor, typename T> 783 inline T do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)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 802 std::uintmax_t file_size(const path & p,error_code & ec)803 fs::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 825 std::uintmax_t hard_link_count(const path & p)826 fs::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 835 std::uintmax_t hard_link_count(const path & p,error_code & ec)836 fs::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 842 bool is_empty(const path & p)843 fs::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 853 bool is_empty(const path & p,error_code & ec)854 fs::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 865 fs::file_time_type last_write_time(const path & p)866 fs::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 875 fs::file_time_type last_write_time(const path & p,error_code & ec)876 fs::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 882 void last_write_time(const path & p,file_time_type new_time)883 fs::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 891 void last_write_time(const path & p,file_time_type new_time,error_code & ec)892 fs::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 927 void permissions(const path & p,perms prms)928 fs::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 936 void permissions(const path & p,perms prms,error_code & ec)937 fs::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 981 fs::path read_symlink(const path & p)982 fs::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 read_symlink(const path & p,error_code & ec)991 fs::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 (posix::lstat(p.c_str(), &st)) 997 { 998 ec.assign(errno, std::generic_category()); 999 return result; 1000 } 1001 else if (!fs::is_symlink(make_file_status(st))) 1002 { 1003 ec.assign(EINVAL, std::generic_category()); 1004 return result; 1005 } 1006 1007 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0'); 1008 do 1009 { 1010 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size()); 1011 if (len == -1) 1012 { 1013 ec.assign(errno, std::generic_category()); 1014 return result; 1015 } 1016 else if (len == (ssize_t)buf.size()) 1017 { 1018 if (buf.size() > 4096) 1019 { 1020 ec.assign(ENAMETOOLONG, std::generic_category()); 1021 return result; 1022 } 1023 buf.resize(buf.size() * 2); 1024 } 1025 else 1026 { 1027 buf.resize(len); 1028 result.assign(buf); 1029 ec.clear(); 1030 break; 1031 } 1032 } 1033 while (true); 1034 #else 1035 ec = std::make_error_code(std::errc::not_supported); 1036 #endif 1037 return result; 1038 } 1039 1040 1041 bool remove(const path & p)1042 fs::remove(const path& p) 1043 { 1044 error_code ec; 1045 bool result = fs::remove(p, ec); 1046 if (ec) 1047 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec)); 1048 return result; 1049 } 1050 1051 bool remove(const path & p,error_code & ec)1052 fs::remove(const path& p, error_code& ec) noexcept 1053 { 1054 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1055 auto st = symlink_status(p, ec); 1056 if (exists(st)) 1057 { 1058 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str())) 1059 || DeleteFileW(p.c_str())) 1060 { 1061 ec.clear(); 1062 return true; 1063 } 1064 else if (!ec) 1065 ec.assign((int)GetLastError(), system_category()); 1066 } 1067 else if (status_known(st)) 1068 ec.clear(); 1069 #else 1070 if (::remove(p.c_str()) == 0) 1071 { 1072 ec.clear(); 1073 return true; 1074 } 1075 else if (errno == ENOENT) 1076 ec.clear(); 1077 else 1078 ec.assign(errno, std::generic_category()); 1079 #endif 1080 return false; 1081 } 1082 1083 1084 std::uintmax_t remove_all(const path & p)1085 fs::remove_all(const path& p) 1086 { 1087 error_code ec; 1088 const auto result = remove_all(p, ec); 1089 if (ec) 1090 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); 1091 return result; 1092 } 1093 1094 std::uintmax_t remove_all(const path & p,error_code & ec)1095 fs::remove_all(const path& p, error_code& ec) noexcept 1096 { 1097 const auto s = symlink_status(p, ec); 1098 if (!status_known(s)) 1099 return -1; 1100 1101 ec.clear(); 1102 if (s.type() == file_type::not_found) 1103 return 0; 1104 1105 uintmax_t count = 0; 1106 if (s.type() == file_type::directory) 1107 { 1108 directory_iterator d(p, ec), end; 1109 while (!ec && d != end) 1110 { 1111 const auto removed = fs::remove_all(d->path(), ec); 1112 if (removed == numeric_limits<uintmax_t>::max()) 1113 return -1; 1114 count += removed; 1115 d.increment(ec); 1116 if (ec) 1117 return -1; 1118 } 1119 } 1120 1121 if (fs::remove(p, ec)) 1122 ++count; 1123 return ec ? -1 : count; 1124 } 1125 1126 void rename(const path & from,const path & to)1127 fs::rename(const path& from, const path& to) 1128 { 1129 error_code ec; 1130 rename(from, to, ec); 1131 if (ec.value()) 1132 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec)); 1133 } 1134 1135 void rename(const path & from,const path & to,error_code & ec)1136 fs::rename(const path& from, const path& to, error_code& ec) noexcept 1137 { 1138 if (posix::rename(from.c_str(), to.c_str())) 1139 ec.assign(errno, std::generic_category()); 1140 else 1141 ec.clear(); 1142 } 1143 1144 void resize_file(const path & p,uintmax_t size)1145 fs::resize_file(const path& p, uintmax_t size) 1146 { 1147 error_code ec; 1148 resize_file(p, size, ec); 1149 if (ec.value()) 1150 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec)); 1151 } 1152 1153 void resize_file(const path & p,uintmax_t size,error_code & ec)1154 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept 1155 { 1156 #ifdef _GLIBCXX_HAVE_UNISTD_H 1157 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max())) 1158 ec.assign(EINVAL, std::generic_category()); 1159 else if (posix::truncate(p.c_str(), size)) 1160 ec.assign(errno, std::generic_category()); 1161 else 1162 ec.clear(); 1163 #else 1164 ec = std::make_error_code(std::errc::not_supported); 1165 #endif 1166 } 1167 1168 1169 fs::space_info space(const path & p)1170 fs::space(const path& p) 1171 { 1172 error_code ec; 1173 space_info s = space(p, ec); 1174 if (ec.value()) 1175 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec)); 1176 return s; 1177 } 1178 1179 fs::space_info space(const path & p,error_code & ec)1180 fs::space(const path& p, error_code& ec) noexcept 1181 { 1182 space_info info = { 1183 static_cast<uintmax_t>(-1), 1184 static_cast<uintmax_t>(-1), 1185 static_cast<uintmax_t>(-1) 1186 }; 1187 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 1188 path dir = absolute(p); 1189 dir.remove_filename(); 1190 auto str = dir.c_str(); 1191 #else 1192 auto str = p.c_str(); 1193 #endif 1194 fs::do_space(str, info.capacity, info.free, info.available, ec); 1195 return info; 1196 } 1197 1198 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 1199 fs::file_status status(const fs::path & p,error_code & ec)1200 fs::status(const fs::path& p, error_code& ec) noexcept 1201 { 1202 file_status status; 1203 stat_type st; 1204 if (posix::stat(p.c_str(), &st)) 1205 { 1206 int err = errno; 1207 ec.assign(err, std::generic_category()); 1208 if (is_not_found_errno(err)) 1209 status.type(file_type::not_found); 1210 #ifdef EOVERFLOW 1211 else if (err == EOVERFLOW) 1212 status.type(file_type::unknown); 1213 #endif 1214 } 1215 else 1216 { 1217 status = make_file_status(st); 1218 ec.clear(); 1219 } 1220 return status; 1221 } 1222 1223 fs::file_status symlink_status(const fs::path & p,std::error_code & ec)1224 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept 1225 { 1226 file_status status; 1227 stat_type st; 1228 if (posix::lstat(p.c_str(), &st)) 1229 { 1230 int err = errno; 1231 ec.assign(err, std::generic_category()); 1232 if (is_not_found_errno(err)) 1233 status.type(file_type::not_found); 1234 } 1235 else 1236 { 1237 status = make_file_status(st); 1238 ec.clear(); 1239 } 1240 return status; 1241 } 1242 #endif 1243 1244 fs::file_status status(const fs::path & p)1245 fs::status(const fs::path& p) 1246 { 1247 std::error_code ec; 1248 auto result = status(p, ec); 1249 if (result.type() == file_type::none) 1250 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec)); 1251 return result; 1252 } 1253 1254 fs::file_status symlink_status(const fs::path & p)1255 fs::symlink_status(const fs::path& p) 1256 { 1257 std::error_code ec; 1258 auto result = symlink_status(p, ec); 1259 if (result.type() == file_type::none) 1260 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec)); 1261 return result; 1262 } 1263 1264 fs::path system_complete(const path & p)1265 fs::system_complete(const path& p) 1266 { 1267 error_code ec; 1268 path comp = system_complete(p, ec); 1269 if (ec.value()) 1270 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec)); 1271 return comp; 1272 } 1273 1274 fs::path system_complete(const path & p,error_code & ec)1275 fs::system_complete(const path& p, error_code& ec) 1276 { 1277 path base = current_path(ec); 1278 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1279 if (p.is_absolute() || !p.has_root_name() 1280 || p.root_name() == base.root_name()) 1281 return absolute(p, base); 1282 // else TODO 1283 ec = std::make_error_code(std::errc::not_supported); 1284 return {}; 1285 #else 1286 if (ec.value()) 1287 return {}; 1288 return absolute(p, base); 1289 #endif 1290 } 1291 temp_directory_path()1292 fs::path fs::temp_directory_path() 1293 { 1294 error_code ec; 1295 path tmp = temp_directory_path(ec); 1296 if (ec.value()) 1297 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec)); 1298 return tmp; 1299 } 1300 temp_directory_path(error_code & ec)1301 fs::path fs::temp_directory_path(error_code& ec) 1302 { 1303 path p; 1304 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1305 unsigned len = 1024; 1306 std::wstring buf; 1307 do 1308 { 1309 buf.resize(len); 1310 len = GetTempPathW(buf.size(), buf.data()); 1311 } while (len > buf.size()); 1312 1313 if (len == 0) 1314 { 1315 ec.assign((int)GetLastError(), std::system_category()); 1316 return p; 1317 } 1318 buf.resize(len); 1319 p = std::move(buf); 1320 #else 1321 const char* tmpdir = nullptr; 1322 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr }; 1323 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) 1324 tmpdir = ::getenv(*e); 1325 p = tmpdir ? tmpdir : "/tmp"; 1326 auto st = status(p, ec); 1327 if (ec) 1328 p.clear(); 1329 else if (!is_directory(st)) 1330 { 1331 p.clear(); 1332 ec = std::make_error_code(std::errc::not_a_directory); 1333 } 1334 #endif 1335 return p; 1336 } 1337 1338