Home | History | Annotate | Line # | Download | only in c++17
      1 // Filesystem operations -*- C++ -*-
      2 
      3 // Copyright (C) 2014-2024 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 #ifndef _GNU_SOURCE
     31 // Cygwin needs this for secure_getenv
     32 # define _GNU_SOURCE 1
     33 #endif
     34 
     35 #include <bits/largefile-config.h>
     36 #include <filesystem>
     37 #include <functional>
     38 #include <ostream>
     39 #include <stack>
     40 #include <stdlib.h>
     41 #include <stdio.h>
     42 #include <errno.h>
     43 #include <limits.h>  // PATH_MAX
     44 #ifdef _GLIBCXX_HAVE_FCNTL_H
     45 # include <fcntl.h>  // AT_FDCWD, AT_SYMLINK_NOFOLLOW
     46 #endif
     47 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
     48 #  include <sys/stat.h>   // stat, utimensat, fchmodat
     49 #endif
     50 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
     51 # include <sys/statvfs.h> // statvfs
     52 #endif
     53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
     54 # include <utime.h> // utime
     55 #endif
     56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
     57 # define WIN32_LEAN_AND_MEAN
     58 # include <windows.h>
     59 #endif
     60 
     61 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
     62 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
     63 #include "../filesystem/ops-common.h"
     64 
     65 #pragma GCC diagnostic ignored "-Wunused-parameter"
     66 
     67 namespace fs = std::filesystem;
     68 namespace posix = std::filesystem::__gnu_posix;
     69 
     70 fs::path
     71 fs::absolute(const path& p)
     72 {
     73   error_code ec;
     74   path ret = absolute(p, ec);
     75   if (ec)
     76     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
     77 					     ec));
     78   return ret;
     79 }
     80 
     81 fs::path
     82 fs::absolute(const path& p, error_code& ec)
     83 {
     84   path ret;
     85   if (p.empty())
     86     {
     87       ec = make_error_code(std::errc::invalid_argument);
     88       return ret;
     89     }
     90   ec.clear();
     91   if (p.is_absolute())
     92     {
     93       ret = p;
     94       return ret;
     95     }
     96 
     97 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
     98   // s must remain null-terminated
     99   wstring_view s = p.native();
    100 
    101   if (p.has_root_directory()) // implies !p.has_root_name()
    102     {
    103       // GetFullPathNameW("//") gives unwanted result (PR 88884).
    104       // If there are multiple directory separators at the start,
    105       // skip all but the last of them.
    106       const auto pos = s.find_first_not_of(L"/\\");
    107       __glibcxx_assert(pos != 0);
    108       s.remove_prefix(std::min(s.length(), pos) - 1);
    109     }
    110 
    111   uint32_t len = 1024;
    112   wstring buf;
    113   do
    114     {
    115       buf.__resize_and_overwrite(len, [&s, &len](wchar_t* p, unsigned n) {
    116 	len = GetFullPathNameW(s.data(), n, p, nullptr);
    117 	return len > n ? 0 : len;
    118       });
    119     }
    120   while (len > buf.size());
    121 
    122   if (len == 0)
    123     ec = __last_system_error();
    124   else
    125     ret = std::move(buf);
    126 #else
    127   ret = current_path(ec);
    128   ret /= p;
    129 #endif
    130   return ret;
    131 }
    132 
    133 namespace
    134 {
    135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
    136   inline bool is_dot(wchar_t c) { return c == L'.'; }
    137 #else
    138   inline bool is_dot(char c) { return c == '.'; }
    139 #endif
    140 
    141   inline bool is_dot(const fs::path& path)
    142   {
    143     const auto& filename = path.native();
    144     return filename.size() == 1 && is_dot(filename[0]);
    145   }
    146 
    147   inline bool is_dotdot(const fs::path& path)
    148   {
    149     const auto& filename = path.native();
    150     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
    151   }
    152 
    153   struct free_as_in_malloc
    154   {
    155     void operator()(void* p) const { ::free(p); }
    156   };
    157 
    158   using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
    159 }
    160 
    161 fs::path
    162 fs::canonical(const path& p, error_code& ec)
    163 {
    164   path result;
    165 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
    166   const path pa = absolute(p.lexically_normal(), ec);
    167 #else
    168   const path pa = absolute(p, ec);
    169 #endif
    170   if (ec)
    171     return result;
    172 
    173 #ifdef _GLIBCXX_USE_REALPATH
    174   char_ptr buf{ nullptr };
    175 # if _XOPEN_VERSION < 700
    176   // Not safe to call realpath(path, NULL)
    177   using char_type = fs::path::value_type;
    178   buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
    179 # endif
    180   if (char* rp = ::realpath(pa.c_str(), buf.get()))
    181     {
    182       if (buf == nullptr)
    183 	buf.reset(rp);
    184       result.assign(rp);
    185       ec.clear();
    186       return result;
    187     }
    188   if (errno != ENAMETOOLONG)
    189     {
    190       ec.assign(errno, std::generic_category());
    191       return result;
    192     }
    193 #endif
    194 
    195   if (!exists(pa, ec))
    196     {
    197       if (!ec)
    198 	ec = make_error_code(std::errc::no_such_file_or_directory);
    199       return result;
    200     }
    201   // else: we know there are (currently) no unresolvable symlink loops
    202 
    203   result = pa.root_path();
    204 
    205   deque<path> cmpts;
    206   for (auto& f : pa.relative_path())
    207     cmpts.push_back(f);
    208 
    209   int max_allowed_symlinks = 40;
    210 
    211   while (!cmpts.empty() && !ec)
    212     {
    213       path f = std::move(cmpts.front());
    214       cmpts.pop_front();
    215 
    216       if (f.empty())
    217 	{
    218 	  // ignore empty element
    219 	}
    220       else if (is_dot(f))
    221 	{
    222 	  if (!is_directory(result, ec) && !ec)
    223 	    ec.assign(ENOTDIR, std::generic_category());
    224 	}
    225       else if (is_dotdot(f))
    226 	{
    227 	  auto parent = result.parent_path();
    228 	  if (parent.empty())
    229 	    result = pa.root_path();
    230 	  else
    231 	    result.swap(parent);
    232 	}
    233       else
    234 	{
    235 	  result /= f;
    236 
    237 	  if (is_symlink(result, ec))
    238 	    {
    239 	      path link = read_symlink(result, ec);
    240 	      if (!ec)
    241 		{
    242 		  if (--max_allowed_symlinks == 0)
    243 		    ec.assign(ELOOP, std::generic_category());
    244 		  else
    245 		    {
    246 		      if (link.is_absolute())
    247 			{
    248 			  result = link.root_path();
    249 			  link = link.relative_path();
    250 			}
    251 		      else
    252 			result = result.parent_path();
    253 
    254 		      cmpts.insert(cmpts.begin(), link.begin(), link.end());
    255 		    }
    256 		}
    257 	    }
    258 	}
    259     }
    260 
    261   if (ec || !exists(result, ec))
    262     result.clear();
    263 
    264   return result;
    265 }
    266 
    267 fs::path
    268 fs::canonical(const path& p)
    269 {
    270   error_code ec;
    271   path res = canonical(p, ec);
    272   if (ec)
    273     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
    274 					     p, ec));
    275   return res;
    276 }
    277 
    278 void
    279 fs::copy(const path& from, const path& to, copy_options options)
    280 {
    281   error_code ec;
    282   copy(from, to, options, ec);
    283   if (ec)
    284     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
    285 }
    286 
    287 namespace std::filesystem
    288 {
    289   // Need this as there's no 'perm_options::none' enumerator.
    290   static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
    291   {
    292     return (obj & bits) != fs::perm_options{};
    293   }
    294 }
    295 
    296 namespace
    297 {
    298   struct internal_file_clock : fs::__file_clock
    299   {
    300     using __file_clock::_S_to_sys;
    301     using __file_clock::_S_from_sys;
    302 
    303 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    304     static fs::file_time_type
    305     from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
    306     {
    307       const auto sys_time = fs::file_time(st, ec);
    308       if (sys_time == sys_time.min())
    309 	return fs::file_time_type::min();
    310       return _S_from_sys(sys_time);
    311     }
    312 #endif
    313   };
    314 }
    315 
    316 void
    317 fs::copy(const path& from, const path& to, copy_options options,
    318 	 error_code& ec)
    319 {
    320 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    321   const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
    322   const bool create_symlinks = is_set(options, copy_options::create_symlinks);
    323   const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
    324   const bool use_lstat = create_symlinks || skip_symlinks;
    325 
    326   file_status f, t;
    327   stat_type from_st, to_st;
    328   // _GLIBCXX_RESOLVE_LIB_DEFECTS
    329   // 2681. filesystem::copy() cannot copy symlinks
    330   if (use_lstat || copy_symlinks
    331       ? posix::lstat(from.c_str(), &from_st)
    332       : posix::stat(from.c_str(), &from_st))
    333     {
    334       ec.assign(errno, std::generic_category());
    335       return;
    336     }
    337   if (use_lstat
    338       ? posix::lstat(to.c_str(), &to_st)
    339       : posix::stat(to.c_str(), &to_st))
    340     {
    341       if (!is_not_found_errno(errno))
    342 	{
    343 	  ec.assign(errno, std::generic_category());
    344 	  return;
    345 	}
    346       t = file_status{file_type::not_found};
    347     }
    348   else
    349     t = make_file_status(to_st);
    350   f = make_file_status(from_st);
    351 
    352   if (exists(t) && !is_other(t) && !is_other(f)
    353       && fs::equiv_files(from.c_str(), from_st, to.c_str(), to_st, ec))
    354     {
    355       ec = std::make_error_code(std::errc::file_exists);
    356       return;
    357     }
    358   if (is_other(f) || is_other(t))
    359     {
    360       ec = std::make_error_code(std::errc::invalid_argument);
    361       return;
    362     }
    363   if (is_directory(f) && is_regular_file(t))
    364     {
    365       ec = std::make_error_code(std::errc::is_a_directory);
    366       return;
    367     }
    368 
    369   if (is_symlink(f))
    370     {
    371       if (skip_symlinks)
    372 	ec.clear();
    373       else if (!exists(t) && copy_symlinks)
    374 	copy_symlink(from, to, ec);
    375       else
    376 	// Not clear what should be done here.
    377 	// "Otherwise report an error as specified in Error reporting (7)."
    378 	ec = std::make_error_code(std::errc::invalid_argument);
    379     }
    380   else if (is_regular_file(f))
    381     {
    382       if (is_set(options, copy_options::directories_only))
    383 	ec.clear();
    384       else if (create_symlinks)
    385 	create_symlink(from, to, ec);
    386       else if (is_set(options, copy_options::create_hard_links))
    387 	create_hard_link(from, to, ec);
    388       else if (is_directory(t))
    389 	do_copy_file(from.c_str(), (to / from.filename()).c_str(),
    390 		     copy_file_options(options), &from_st, nullptr, ec);
    391       else
    392 	{
    393 	  auto ptr = exists(t) ? &to_st : &from_st;
    394 	  do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
    395 		       &from_st, ptr, ec);
    396 	}
    397     }
    398   // _GLIBCXX_RESOLVE_LIB_DEFECTS
    399   // 2682. filesystem::copy() won't create a symlink to a directory
    400   else if (is_directory(f) && create_symlinks)
    401     ec = std::make_error_code(errc::is_a_directory);
    402   else if (is_directory(f) && (is_set(options, copy_options::recursive)
    403 			       || options == copy_options::none))
    404     {
    405       if (!exists(t))
    406 	if (!create_directory(to, from, ec))
    407 	  return;
    408       // set an unused bit in options to disable further recursion
    409       if (!is_set(options, copy_options::recursive))
    410 	options |= static_cast<copy_options>(4096);
    411       for (const directory_entry& x : directory_iterator(from, ec))
    412 	{
    413 	  copy(x.path(), to/x.path().filename(), options, ec);
    414 	  if (ec)
    415 	    return;
    416 	}
    417     }
    418   // _GLIBCXX_RESOLVE_LIB_DEFECTS
    419   // 2683. filesystem::copy() says "no effects"
    420   else
    421     ec.clear();
    422 #else
    423   ec = std::make_error_code(std::errc::function_not_supported);
    424 #endif
    425 }
    426 
    427 bool
    428 fs::copy_file(const path& from, const path& to, copy_options option)
    429 {
    430   error_code ec;
    431   bool result = copy_file(from, to, option, ec);
    432   if (ec)
    433     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
    434 					     ec));
    435   return result;
    436 }
    437 
    438 bool
    439 fs::copy_file(const path& from, const path& to, copy_options options,
    440 	      error_code& ec)
    441 {
    442 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    443   return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
    444 		      nullptr, nullptr, ec);
    445 #else
    446   ec = std::make_error_code(std::errc::function_not_supported);
    447   return false;
    448 #endif
    449 }
    450 
    451 
    452 void
    453 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
    454 {
    455   error_code ec;
    456   copy_symlink(existing_symlink, new_symlink, ec);
    457   if (ec)
    458     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
    459 	  existing_symlink, new_symlink, ec));
    460 }
    461 
    462 void
    463 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
    464 		 error_code& ec) noexcept
    465 {
    466   auto p = read_symlink(existing_symlink, ec);
    467   if (ec)
    468     return;
    469 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
    470   if (is_directory(p))
    471     {
    472       create_directory_symlink(p, new_symlink, ec);
    473       return;
    474     }
    475 #endif
    476   create_symlink(p, new_symlink, ec);
    477 }
    478 
    479 
    480 bool
    481 fs::create_directories(const path& p)
    482 {
    483   error_code ec;
    484   bool result = create_directories(p, ec);
    485   if (ec)
    486     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
    487 					     ec));
    488   return result;
    489 }
    490 
    491 bool
    492 fs::create_directories(const path& p, error_code& ec)
    493 {
    494   if (p.empty())
    495     {
    496       ec = std::make_error_code(errc::invalid_argument);
    497       return false;
    498     }
    499 
    500   file_status st = status(p, ec);
    501   if (is_directory(st))
    502     return false;
    503   else if (ec && !status_known(st))
    504     return false;
    505   else if (exists(st))
    506     {
    507       if (!ec)
    508 	ec = std::make_error_code(std::errc::not_a_directory);
    509       return false;
    510     }
    511 
    512   __glibcxx_assert(st.type() == file_type::not_found);
    513   // !exists(p) so there must be at least one non-existent component in p.
    514 
    515   std::stack<path> missing;
    516   path pp = p;
    517 
    518   // Strip any trailing slash
    519   if (pp.has_relative_path() && !pp.has_filename())
    520     pp = pp.parent_path();
    521 
    522   do
    523     {
    524       const auto& filename = pp.filename();
    525       if (is_dot(filename) || is_dotdot(filename))
    526 	pp = pp.parent_path();
    527       else
    528 	{
    529 	  missing.push(std::move(pp));
    530 	  if (missing.size() > 1000) // sanity check
    531 	    {
    532 	      ec = std::make_error_code(std::errc::filename_too_long);
    533 	      return false;
    534 	    }
    535 	  pp = missing.top().parent_path();
    536 	}
    537 
    538       if (pp.empty())
    539 	break;
    540 
    541       st = status(pp, ec);
    542       if (exists(st))
    543 	{
    544 	  if (ec)
    545 	    return false;
    546 	  if (!is_directory(st))
    547 	    {
    548 	      ec = std::make_error_code(std::errc::not_a_directory);
    549 	      return false;
    550 	    }
    551 	}
    552 
    553       if (ec && exists(st))
    554 	return false;
    555     }
    556   while (st.type() == file_type::not_found);
    557 
    558   __glibcxx_assert(!missing.empty());
    559 
    560   bool created;
    561   do
    562     {
    563       const path& top = missing.top();
    564       created = create_directory(top, ec);
    565       if (ec)
    566 	return false;
    567       missing.pop();
    568     }
    569   while (!missing.empty());
    570 
    571   return created;
    572 }
    573 
    574 namespace
    575 {
    576   bool
    577   create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
    578   {
    579     bool created = false;
    580 #if _GLIBCXX_USE_MKDIR
    581     posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
    582     if (posix::mkdir(p.c_str(), mode))
    583       {
    584 	const int err = errno;
    585 	if (err != EEXIST || !is_directory(p, ec))
    586 	  ec.assign(err, std::generic_category());
    587       }
    588     else
    589       {
    590 	ec.clear();
    591 	created = true;
    592       }
    593 #else
    594     ec = std::make_error_code(std::errc::function_not_supported);
    595 #endif
    596     return created;
    597   }
    598 } // namespace
    599 
    600 bool
    601 fs::create_directory(const path& p)
    602 {
    603   error_code ec;
    604   bool result = create_directory(p, ec);
    605   if (ec)
    606     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
    607 	  ec));
    608   return result;
    609 }
    610 
    611 bool
    612 fs::create_directory(const path& p, error_code& ec) noexcept
    613 {
    614   return create_dir(p, perms::all, ec);
    615 }
    616 
    617 
    618 bool
    619 fs::create_directory(const path& p, const path& attributes)
    620 {
    621   error_code ec;
    622   bool result = create_directory(p, attributes, ec);
    623   if (ec)
    624     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
    625 	  ec));
    626   return result;
    627 }
    628 
    629 bool
    630 fs::create_directory(const path& p, const path& attributes,
    631 		     error_code& ec) noexcept
    632 {
    633 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    634   stat_type st;
    635   if (posix::stat(attributes.c_str(), &st))
    636     {
    637       ec.assign(errno, std::generic_category());
    638       return false;
    639     }
    640   return create_dir(p, static_cast<perms>(st.st_mode), ec);
    641 #else
    642   ec = std::make_error_code(std::errc::function_not_supported);
    643   return false;
    644 #endif
    645 }
    646 
    647 
    648 void
    649 fs::create_directory_symlink(const path& to, const path& new_symlink)
    650 {
    651   error_code ec;
    652   create_directory_symlink(to, new_symlink, ec);
    653   if (ec)
    654     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
    655 	  to, new_symlink, ec));
    656 }
    657 
    658 void
    659 fs::create_directory_symlink(const path& to, const path& new_symlink,
    660 			     error_code& ec) noexcept
    661 {
    662 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
    663   ec = std::make_error_code(std::errc::function_not_supported);
    664 #else
    665   create_symlink(to, new_symlink, ec);
    666 #endif
    667 }
    668 
    669 
    670 void
    671 fs::create_hard_link(const path& to, const path& new_hard_link)
    672 {
    673   error_code ec;
    674   create_hard_link(to, new_hard_link, ec);
    675   if (ec)
    676     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
    677 					     to, new_hard_link, ec));
    678 }
    679 
    680 void
    681 fs::create_hard_link(const path& to, const path& new_hard_link,
    682 		     error_code& ec) noexcept
    683 {
    684 #ifdef _GLIBCXX_HAVE_LINK
    685   if (::link(to.c_str(), new_hard_link.c_str()))
    686     ec.assign(errno, std::generic_category());
    687   else
    688     ec.clear();
    689 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
    690   if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
    691     ec.clear();
    692   else
    693     ec = __last_system_error();
    694 #else
    695   ec = std::make_error_code(std::errc::function_not_supported);
    696 #endif
    697 }
    698 
    699 void
    700 fs::create_symlink(const path& to, const path& new_symlink)
    701 {
    702   error_code ec;
    703   create_symlink(to, new_symlink, ec);
    704   if (ec)
    705     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
    706 	  to, new_symlink, ec));
    707 }
    708 
    709 void
    710 fs::create_symlink(const path& to, const path& new_symlink,
    711 		   error_code& ec) noexcept
    712 {
    713 #ifdef _GLIBCXX_HAVE_SYMLINK
    714   if (::symlink(to.c_str(), new_symlink.c_str()))
    715     ec.assign(errno, std::generic_category());
    716   else
    717     ec.clear();
    718 #else
    719   ec = std::make_error_code(std::errc::function_not_supported);
    720 #endif
    721 }
    722 
    723 
    724 fs::path
    725 fs::current_path()
    726 {
    727   error_code ec;
    728   path p = current_path(ec);
    729   if (ec)
    730     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
    731   return p;
    732 }
    733 
    734 fs::path
    735 fs::current_path(error_code& ec)
    736 {
    737   path p;
    738 #if _GLIBCXX_USE_GETCWD
    739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
    740   if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
    741     {
    742       p.assign(cwd.get());
    743       ec.clear();
    744     }
    745   else
    746     ec.assign(errno, std::generic_category());
    747 #else
    748 #ifdef _PC_PATH_MAX
    749   long path_max = pathconf(".", _PC_PATH_MAX);
    750   size_t size;
    751   if (path_max == -1)
    752       size = 1024;
    753   else if (path_max > 10240)
    754       size = 10240;
    755   else
    756       size = path_max;
    757 #elif defined(PATH_MAX)
    758   size_t size = PATH_MAX;
    759 #else
    760   size_t size = 1024;
    761 #endif
    762   for (char_ptr buf; p.empty(); size *= 2)
    763     {
    764       using char_type = fs::path::value_type;
    765       buf.reset((char_type*)malloc(size * sizeof(char_type)));
    766       if (buf)
    767 	{
    768 	  if (getcwd(buf.get(), size))
    769 	    {
    770 	      p.assign(buf.get());
    771 	      ec.clear();
    772 	    }
    773 	  else if (errno != ERANGE)
    774 	    {
    775 	      ec.assign(errno, std::generic_category());
    776 	      return {};
    777 	    }
    778 	}
    779       else
    780 	{
    781 	  ec = std::make_error_code(std::errc::not_enough_memory);
    782 	  return {};
    783 	}
    784     }
    785 #endif  // __GLIBC__
    786 #else   // _GLIBCXX_USE_GETCWD
    787   ec = std::make_error_code(std::errc::function_not_supported);
    788 #endif
    789   return p;
    790 }
    791 
    792 void
    793 fs::current_path(const path& p)
    794 {
    795   error_code ec;
    796   current_path(p, ec);
    797   if (ec)
    798     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
    799 }
    800 
    801 void
    802 fs::current_path(const path& p, error_code& ec) noexcept
    803 {
    804 #if _GLIBCXX_USE_CHDIR
    805   if (posix::chdir(p.c_str()))
    806     ec.assign(errno, std::generic_category());
    807   else
    808     ec.clear();
    809 #else
    810   ec = std::make_error_code(std::errc::function_not_supported);
    811 #endif
    812 }
    813 
    814 bool
    815 fs::equivalent(const path& p1, const path& p2)
    816 {
    817   error_code ec;
    818   auto result = equivalent(p1, p2, ec);
    819   if (ec)
    820     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
    821 	  p1, p2, ec));
    822   return result;
    823 }
    824 
    825 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
    826 namespace
    827 {
    828   // An RAII type that opens a handle for an existing file.
    829   struct auto_win_file_handle
    830   {
    831     explicit
    832     auto_win_file_handle(const wchar_t* p, std::error_code& ec) noexcept
    833     : handle(CreateFileW(p, 0,
    834 			 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    835 			 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)),
    836       ec(ec)
    837     {
    838       if (handle == INVALID_HANDLE_VALUE)
    839 	ec = std::__last_system_error();
    840     }
    841 
    842     ~auto_win_file_handle()
    843     { if (*this) CloseHandle(handle); }
    844 
    845     explicit operator bool() const noexcept
    846     { return handle != INVALID_HANDLE_VALUE; }
    847 
    848     bool get_info() noexcept
    849     {
    850       if (GetFileInformationByHandle(handle, &info))
    851 	return true;
    852       ec = std::__last_system_error();
    853       return false;
    854     }
    855 
    856     HANDLE handle;
    857     BY_HANDLE_FILE_INFORMATION info;
    858     // Like errno, we only set this on error and never clear it.
    859     // This propagates an error_code to the caller when something goes wrong,
    860     // but the caller should not assume a non-zero ec means an error happened
    861     // unless they explicitly cleared it before passing it to our constructor.
    862     std::error_code& ec;
    863   };
    864 }
    865 #endif
    866 
    867 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    868 #ifdef NEED_DO_COPY_FILE // Only define this once, not in cow-fs_ops.o too
    869 bool
    870 fs::equiv_files([[maybe_unused]] const char_type* p1, const stat_type& st1,
    871 		[[maybe_unused]] const char_type* p2, const stat_type& st2,
    872 		[[maybe_unused]] error_code& ec)
    873 {
    874 #if ! _GLIBCXX_FILESYSTEM_IS_WINDOWS
    875   // For POSIX the device ID and inode number uniquely identify a file.
    876   return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
    877 #else
    878   // For Windows st_ino is not set, so can't be used to distinguish files.
    879   // We can compare modes and device IDs as a cheap initial check:
    880   if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev)
    881     return false;
    882 
    883   // Use GetFileInformationByHandle to get more info about the files.
    884   if (auto_win_file_handle h1{p1, ec})
    885     if (auto_win_file_handle h2{p2, ec})
    886       if (h1.get_info() && h2.get_info())
    887 	return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber
    888 		 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh
    889 		 && h1.info.nFileIndexLow == h2.info.nFileIndexLow;
    890   return false;
    891 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
    892 }
    893 #endif // NEED_DO_COPY_FILE
    894 #endif // _GLIBCXX_HAVE_SYS_STAT_H
    895 
    896 bool
    897 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
    898 {
    899 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    900   int err = 0;
    901   file_status s1, s2;
    902   stat_type st1, st2;
    903   if (posix::stat(p1.c_str(), &st1) == 0)
    904     s1 = make_file_status(st1);
    905   else if (is_not_found_errno(errno))
    906     s1.type(file_type::not_found);
    907   else
    908     err = errno;
    909 
    910   if (posix::stat(p2.c_str(), &st2) == 0)
    911     s2 = make_file_status(st2);
    912   else if (is_not_found_errno(errno))
    913     s2.type(file_type::not_found);
    914   else
    915     err = errno;
    916 
    917   if (err)
    918     ec.assign(err, std::generic_category());
    919   else if (!exists(s1) || !exists(s2))
    920     ec = std::make_error_code(std::errc::no_such_file_or_directory);
    921   else
    922     {
    923       ec.clear();
    924       if (s1.type() == s2.type())
    925 	return fs::equiv_files(p1.c_str(), st1, p2.c_str(), st2, ec);
    926     }
    927   return false;
    928 #else
    929   ec = std::make_error_code(std::errc::function_not_supported);
    930 #endif
    931   return false;
    932 }
    933 
    934 std::uintmax_t
    935 fs::file_size(const path& p)
    936 {
    937   error_code ec;
    938   auto sz = file_size(p, ec);
    939   if (ec)
    940     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
    941   return sz;
    942 }
    943 
    944 namespace
    945 {
    946   template<typename Accessor, typename T>
    947     inline T
    948     do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
    949     {
    950 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    951       posix::stat_type st;
    952       if (posix::stat(p.c_str(), &st))
    953 	{
    954 	  ec.assign(errno, std::generic_category());
    955 	  return deflt;
    956 	}
    957       ec.clear();
    958       return f(st);
    959 #else
    960       ec = std::make_error_code(std::errc::function_not_supported);
    961       return deflt;
    962 #endif
    963     }
    964 }
    965 
    966 std::uintmax_t
    967 fs::file_size(const path& p, error_code& ec) noexcept
    968 {
    969 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    970   struct S
    971   {
    972     S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
    973     S() : type(file_type::not_found) { }
    974     file_type type;
    975     uintmax_t size;
    976   };
    977   auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
    978   if (s.type == file_type::regular)
    979     return s.size;
    980   if (!ec)
    981     {
    982       if (s.type == file_type::directory)
    983 	ec = std::make_error_code(std::errc::is_a_directory);
    984       else
    985 	ec = std::__unsupported();
    986     }
    987 #else
    988   ec = std::make_error_code(std::errc::function_not_supported);
    989 #endif
    990   return -1;
    991 }
    992 
    993 std::uintmax_t
    994 fs::hard_link_count(const path& p)
    995 {
    996   error_code ec;
    997   auto count = hard_link_count(p, ec);
    998   if (ec)
    999     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
   1000   return count;
   1001 }
   1002 
   1003 std::uintmax_t
   1004 fs::hard_link_count(const path& p, error_code& ec) noexcept
   1005 {
   1006 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1007   auto_win_file_handle h(p.c_str(), ec);
   1008   if (h && h.get_info())
   1009     {
   1010       ec.clear();
   1011       return static_cast<uintmax_t>(h.info.nNumberOfLinks);
   1012     }
   1013   return static_cast<uintmax_t>(-1);
   1014 #elif defined _GLIBCXX_HAVE_SYS_STAT_H
   1015   return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
   1016 		 static_cast<uintmax_t>(-1));
   1017 #else
   1018   ec = std::make_error_code(std::errc::function_not_supported);
   1019   return static_cast<uintmax_t>(-1);
   1020 #endif
   1021 }
   1022 
   1023 bool
   1024 fs::is_empty(const path& p)
   1025 {
   1026   error_code ec;
   1027   bool e = is_empty(p, ec);
   1028   if (ec)
   1029     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
   1030 					     p, ec));
   1031   return e;
   1032 }
   1033 
   1034 bool
   1035 fs::is_empty(const path& p, error_code& ec)
   1036 {
   1037   auto s = status(p, ec);
   1038   if (ec)
   1039     return false;
   1040   bool empty = fs::is_directory(s)
   1041     ? fs::directory_iterator(p, ec) == fs::directory_iterator()
   1042     : fs::file_size(p, ec) == 0;
   1043   return ec ? false : empty;
   1044 }
   1045 
   1046 fs::file_time_type
   1047 fs::last_write_time(const path& p)
   1048 {
   1049   error_code ec;
   1050   auto t = last_write_time(p, ec);
   1051   if (ec)
   1052     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
   1053   return t;
   1054 }
   1055 
   1056 fs::file_time_type
   1057 fs::last_write_time(const path& p, error_code& ec) noexcept
   1058 {
   1059 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   1060   return do_stat(p, ec,
   1061 		 [&ec](const auto& st) {
   1062 		     return internal_file_clock::from_stat(st, ec);
   1063 		 },
   1064 		 file_time_type::min());
   1065 #else
   1066   ec = std::make_error_code(std::errc::function_not_supported);
   1067   return file_time_type::min();
   1068 #endif
   1069 }
   1070 
   1071 void
   1072 fs::last_write_time(const path& p, file_time_type new_time)
   1073 {
   1074   error_code ec;
   1075   last_write_time(p, new_time, ec);
   1076   if (ec)
   1077     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
   1078 }
   1079 
   1080 void
   1081 fs::last_write_time(const path& p,
   1082 		    file_time_type new_time, error_code& ec) noexcept
   1083 {
   1084   auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
   1085   auto s = chrono::duration_cast<chrono::seconds>(d);
   1086 #if _GLIBCXX_USE_UTIMENSAT
   1087   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
   1088   if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
   1089     {
   1090       --s;
   1091       ns += chrono::seconds(1);
   1092     }
   1093   struct ::timespec ts[2];
   1094   ts[0].tv_sec = 0;
   1095   ts[0].tv_nsec = UTIME_OMIT;
   1096   ts[1].tv_sec = static_cast<std::time_t>(s.count());
   1097   ts[1].tv_nsec = static_cast<long>(ns.count());
   1098   if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
   1099     ec.assign(errno, std::generic_category());
   1100   else
   1101     ec.clear();
   1102 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
   1103   posix::utimbuf times;
   1104   times.modtime = s.count();
   1105   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
   1106 			 times.modtime);
   1107   if (posix::utime(p.c_str(), &times))
   1108     ec.assign(errno, std::generic_category());
   1109   else
   1110     ec.clear();
   1111 #else
   1112   ec = std::make_error_code(std::errc::function_not_supported);
   1113 #endif
   1114 }
   1115 
   1116 void
   1117 fs::permissions(const path& p, perms prms, perm_options opts)
   1118 {
   1119   error_code ec;
   1120   permissions(p, prms, opts, ec);
   1121   if (ec)
   1122     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
   1123 }
   1124 
   1125 void
   1126 fs::permissions(const path& p, perms prms, perm_options opts,
   1127 		error_code& ec) noexcept
   1128 {
   1129 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD
   1130   const bool replace = is_set(opts, perm_options::replace);
   1131   const bool add = is_set(opts, perm_options::add);
   1132   const bool remove = is_set(opts, perm_options::remove);
   1133   const bool nofollow = is_set(opts, perm_options::nofollow);
   1134   if (((int)replace + (int)add + (int)remove) != 1)
   1135     {
   1136       ec = std::make_error_code(std::errc::invalid_argument);
   1137       return;
   1138     }
   1139 
   1140   prms &= perms::mask;
   1141 
   1142   file_status st;
   1143   if (add || remove || nofollow)
   1144     {
   1145       st = nofollow ? symlink_status(p, ec) : status(p, ec);
   1146       if (ec)
   1147 	return;
   1148       auto curr = st.permissions();
   1149       if (add)
   1150 	prms |= curr;
   1151       else if (remove)
   1152 	prms = curr & ~prms;
   1153     }
   1154 
   1155   int err = 0;
   1156 #if _GLIBCXX_USE_FCHMODAT
   1157   const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
   1158   if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
   1159     err = errno;
   1160 #else
   1161   if (nofollow && is_symlink(st))
   1162     ec = std::__unsupported();
   1163   else if (posix::chmod(p.c_str(), static_cast<posix::mode_t>(prms)))
   1164     err = errno;
   1165 #endif
   1166 
   1167   if (err)
   1168     ec.assign(err, std::generic_category());
   1169   else
   1170     ec.clear();
   1171 #else
   1172   ec = std::make_error_code(std::errc::function_not_supported);
   1173 #endif
   1174 }
   1175 
   1176 fs::path
   1177 fs::proximate(const path& p, const path& base)
   1178 {
   1179   return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
   1180 }
   1181 
   1182 fs::path
   1183 fs::proximate(const path& p, const path& base, error_code& ec)
   1184 {
   1185   path result;
   1186   const auto p2 = weakly_canonical(p, ec);
   1187   if (!ec)
   1188     {
   1189       const auto base2 = weakly_canonical(base, ec);
   1190       if (!ec)
   1191 	result = p2.lexically_proximate(base2);
   1192     }
   1193   return result;
   1194 }
   1195 
   1196 fs::path
   1197 fs::read_symlink(const path& p)
   1198 {
   1199   error_code ec;
   1200   path tgt = read_symlink(p, ec);
   1201   if (ec)
   1202     _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
   1203   return tgt;
   1204 }
   1205 
   1206 fs::path fs::read_symlink(const path& p, error_code& ec)
   1207 {
   1208   path result;
   1209 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
   1210   stat_type st;
   1211   if (posix::lstat(p.c_str(), &st))
   1212     {
   1213       ec.assign(errno, std::generic_category());
   1214       return result;
   1215     }
   1216   else if (!fs::is_symlink(make_file_status(st)))
   1217     {
   1218       ec.assign(EINVAL, std::generic_category());
   1219       return result;
   1220     }
   1221 
   1222   std::string buf;
   1223   size_t bufsz = st.st_size ? st.st_size + 1 : 128;
   1224   do
   1225     {
   1226       ssize_t len;
   1227       buf.__resize_and_overwrite(bufsz, [&p, &len](char* ptr, size_t n) {
   1228 	len = ::readlink(p.c_str(), ptr, n);
   1229 	return size_t(len) < n ? len : 0;
   1230       });
   1231       if (buf.size())
   1232 	{
   1233 	  result.assign(std::move(buf));
   1234 	  ec.clear();
   1235 	  break;
   1236 	}
   1237       else if (len == -1)
   1238 	{
   1239 	  ec.assign(errno, std::generic_category());
   1240 	  return result;
   1241 	}
   1242       else if (bufsz > 4096)
   1243 	{
   1244 	  ec.assign(ENAMETOOLONG, std::generic_category());
   1245 	  return result;
   1246 	}
   1247       else
   1248 	bufsz *= 2;
   1249     }
   1250   while (true);
   1251 #else
   1252   ec = std::make_error_code(std::errc::function_not_supported);
   1253 #endif
   1254   return result;
   1255 }
   1256 
   1257 fs::path
   1258 fs::relative(const path& p, const path& base)
   1259 {
   1260   return weakly_canonical(p).lexically_relative(weakly_canonical(base));
   1261 }
   1262 
   1263 fs::path
   1264 fs::relative(const path& p, const path& base, error_code& ec)
   1265 {
   1266   auto result = weakly_canonical(p, ec);
   1267   fs::path cbase;
   1268   if (!ec)
   1269     cbase = weakly_canonical(base, ec);
   1270   if (!ec)
   1271     result = result.lexically_relative(cbase);
   1272   if (ec)
   1273     result.clear();
   1274   return result;
   1275 }
   1276 
   1277 bool
   1278 fs::remove(const path& p)
   1279 {
   1280   error_code ec;
   1281   const bool result = fs::remove(p, ec);
   1282   if (ec)
   1283     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
   1284   return result;
   1285 }
   1286 
   1287 bool
   1288 fs::remove(const path& p, error_code& ec) noexcept
   1289 {
   1290 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1291   auto st = symlink_status(p, ec);
   1292   if (exists(st))
   1293     {
   1294       if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
   1295 	  || DeleteFileW(p.c_str()))
   1296 	{
   1297 	  ec.clear();
   1298 	  return true;
   1299 	}
   1300       else if (!ec)
   1301 	ec = __last_system_error();
   1302     }
   1303   else if (status_known(st))
   1304     ec.clear();
   1305 #else
   1306   if (::remove(p.c_str()) == 0)
   1307     {
   1308       ec.clear();
   1309       return true;
   1310     }
   1311   else if (errno == ENOENT)
   1312     ec.clear();
   1313   else
   1314     ec.assign(errno, std::generic_category());
   1315 #endif
   1316   return false;
   1317 }
   1318 
   1319 std::uintmax_t
   1320 fs::remove_all(const path& p)
   1321 {
   1322   error_code ec;
   1323   uintmax_t count = 0;
   1324   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
   1325   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
   1326   {
   1327   case 0:
   1328     // Iterate over the directory removing everything.
   1329     {
   1330       const recursive_directory_iterator end;
   1331       while (dir != end)
   1332 	{
   1333 	  dir.__erase(); // throws on error
   1334 	  ++count;
   1335 	}
   1336     }
   1337     // Directory is empty now, will remove it below.
   1338     break;
   1339 #ifndef __AVR__
   1340   case ENOENT:
   1341     // Our work here is done.
   1342     return 0;
   1343   case ENOTDIR:
   1344   case ELOOP:  // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink.
   1345 #if defined __FreeBSD__ || defined __DragonFly__
   1346   case EMLINK: // Used instead of ELOOP
   1347 #endif
   1348 #if defined __NetBSD__ && defined EFTYPE
   1349   case EFTYPE: // Used instead of ELOOP
   1350 #endif
   1351     // Not a directory, will remove below.
   1352     break;
   1353 #endif
   1354   default:
   1355     // An error occurred.
   1356     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
   1357   }
   1358 
   1359   // Remove p itself, which is either a non-directory or is now empty.
   1360   return count + fs::remove(p);
   1361 }
   1362 
   1363 std::uintmax_t
   1364 fs::remove_all(const path& p, error_code& ec)
   1365 {
   1366   uintmax_t count = 0;
   1367   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
   1368   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
   1369   {
   1370   case 0:
   1371     // Iterate over the directory removing everything.
   1372     {
   1373       const recursive_directory_iterator end;
   1374       while (dir != end)
   1375 	{
   1376 	  dir.__erase(&ec);
   1377 	  if (ec)
   1378 	    return -1;
   1379 	  ++count;
   1380 	}
   1381     }
   1382     // Directory is empty now, will remove it below.
   1383     break;
   1384 #ifndef __AVR__
   1385   case ENOENT:
   1386     // Our work here is done.
   1387     ec.clear();
   1388     return 0;
   1389   case ENOTDIR:
   1390   case ELOOP:  // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink.
   1391 #if defined __FreeBSD__ || defined __DragonFly__
   1392   case EMLINK: // Used instead of ELOOP
   1393 #endif
   1394 #if defined __NetBSD__ && defined EFTYPE
   1395   case EFTYPE: // Used instead of ELOOP
   1396 #endif
   1397     // Not a directory, will remove below.
   1398     break;
   1399 #endif
   1400   default:
   1401     // An error occurred.
   1402     return -1;
   1403   }
   1404 
   1405   // Remove p itself, which is either a non-directory or is now empty.
   1406   if (int last = fs::remove(p, ec); !ec)
   1407     return count + last;
   1408   return -1;
   1409 }
   1410 
   1411 void
   1412 fs::rename(const path& from, const path& to)
   1413 {
   1414   error_code ec;
   1415   rename(from, to, ec);
   1416   if (ec)
   1417     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
   1418 }
   1419 
   1420 void
   1421 fs::rename(const path& from, const path& to, error_code& ec) noexcept
   1422 {
   1423 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1424   const auto to_status = fs::status(to, ec);
   1425   if (to_status.type() == file_type::not_found)
   1426     ec.clear();
   1427   else if (ec)
   1428     return;
   1429 
   1430   if (fs::exists(to_status))
   1431   {
   1432     const auto from_status = fs::status(from, ec);
   1433     if (ec)
   1434       return;
   1435 
   1436     if (fs::is_directory(to_status))
   1437     {
   1438       if (!fs::is_directory(from_status))
   1439       {
   1440 	// Cannot rename a non-directory over an existing directory.
   1441 	ec = std::make_error_code(std::errc::is_a_directory);
   1442 	return;
   1443       }
   1444     }
   1445     else if (fs::is_directory(from_status))
   1446     {
   1447       // Cannot rename a directory over an existing non-directory.
   1448       ec = std::make_error_code(std::errc::not_a_directory);
   1449       return;
   1450     }
   1451   }
   1452 #endif
   1453   if (posix::rename(from.c_str(), to.c_str()))
   1454     ec.assign(errno, std::generic_category());
   1455   else
   1456     ec.clear();
   1457 }
   1458 
   1459 void
   1460 fs::resize_file(const path& p, uintmax_t size)
   1461 {
   1462   error_code ec;
   1463   resize_file(p, size, ec);
   1464   if (ec)
   1465     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
   1466 }
   1467 
   1468 void
   1469 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
   1470 {
   1471   if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max()))
   1472     ec.assign(EINVAL, std::generic_category());
   1473   else if (posix::truncate(p.c_str(), size))
   1474     ec.assign(errno, std::generic_category());
   1475   else
   1476     ec.clear();
   1477 }
   1478 
   1479 
   1480 fs::space_info
   1481 fs::space(const path& p)
   1482 {
   1483   error_code ec;
   1484   space_info s = space(p, ec);
   1485   if (ec)
   1486     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
   1487   return s;
   1488 }
   1489 
   1490 fs::space_info
   1491 fs::space(const path& p, error_code& ec) noexcept
   1492 {
   1493   space_info info = {
   1494     static_cast<uintmax_t>(-1),
   1495     static_cast<uintmax_t>(-1),
   1496     static_cast<uintmax_t>(-1)
   1497   };
   1498 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   1499 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1500   path dir = absolute(p);
   1501   dir.remove_filename();
   1502   auto str = dir.c_str();
   1503 #else
   1504   auto str = p.c_str();
   1505 #endif
   1506 
   1507   do_space(str, info.capacity, info.free, info.available, ec);
   1508 #endif // _GLIBCXX_HAVE_SYS_STAT_H
   1509 
   1510   return info;
   1511 }
   1512 
   1513 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   1514 fs::file_status
   1515 fs::status(const fs::path& p, error_code& ec) noexcept
   1516 {
   1517   file_status status;
   1518   auto str = p.c_str();
   1519 
   1520 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1521   // stat() fails if there's a trailing slash (PR 88881)
   1522   path p2;
   1523   if (p.has_relative_path() && !p.has_filename())
   1524     {
   1525       __try
   1526 	{
   1527 	  p2 = p.parent_path();
   1528 	  str = p2.c_str();
   1529 	}
   1530       __catch(const bad_alloc&)
   1531 	{
   1532 	  ec = std::make_error_code(std::errc::not_enough_memory);
   1533 	  return status;
   1534 	}
   1535       str = p2.c_str();
   1536     }
   1537 #endif
   1538 
   1539   stat_type st;
   1540   if (posix::stat(str, &st))
   1541     {
   1542       int err = errno;
   1543       ec.assign(err, std::generic_category());
   1544       if (is_not_found_errno(err))
   1545 	status.type(file_type::not_found);
   1546 #ifdef EOVERFLOW
   1547       else if (err == EOVERFLOW)
   1548 	status.type(file_type::unknown);
   1549 #endif
   1550     }
   1551   else
   1552     {
   1553       status = make_file_status(st);
   1554       ec.clear();
   1555     }
   1556   return status;
   1557 }
   1558 
   1559 fs::file_status
   1560 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
   1561 {
   1562   file_status status;
   1563   auto str = p.c_str();
   1564 
   1565 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
   1566   // stat() fails if there's a trailing slash (PR 88881)
   1567   path p2;
   1568   if (p.has_relative_path() && !p.has_filename())
   1569     {
   1570       __try
   1571 	{
   1572 	  p2 = p.parent_path();
   1573 	  str = p2.c_str();
   1574 	}
   1575       __catch(const bad_alloc&)
   1576 	{
   1577 	  ec = std::make_error_code(std::errc::not_enough_memory);
   1578 	  return status;
   1579 	}
   1580       str = p2.c_str();
   1581     }
   1582 #endif
   1583 
   1584   stat_type st;
   1585   if (posix::lstat(str, &st))
   1586     {
   1587       int err = errno;
   1588       ec.assign(err, std::generic_category());
   1589       if (is_not_found_errno(err))
   1590 	status.type(file_type::not_found);
   1591     }
   1592   else
   1593     {
   1594       status = make_file_status(st);
   1595       ec.clear();
   1596     }
   1597   return status;
   1598 }
   1599 #endif
   1600 
   1601 fs::file_status
   1602 fs::status(const fs::path& p)
   1603 {
   1604   std::error_code ec;
   1605   auto result = status(p, ec);
   1606   if (result.type() == file_type::none)
   1607     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
   1608   return result;
   1609 }
   1610 
   1611 fs::file_status
   1612 fs::symlink_status(const fs::path& p)
   1613 {
   1614   std::error_code ec;
   1615   auto result = symlink_status(p, ec);
   1616   if (result.type() == file_type::none)
   1617     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
   1618   return result;
   1619 }
   1620 
   1621 fs::path
   1622 fs::temp_directory_path()
   1623 {
   1624   error_code ec;
   1625   path p = fs::get_temp_directory_from_env(ec);
   1626   if (!ec)
   1627     {
   1628       auto st = status(p, ec);
   1629       if (!ec && !is_directory(st))
   1630 	ec = std::make_error_code(std::errc::not_a_directory);
   1631     }
   1632   if (ec)
   1633     {
   1634       if (p.empty())
   1635 	_GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
   1636       else
   1637 	_GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec));
   1638     }
   1639   return p;
   1640 }
   1641 
   1642 fs::path
   1643 fs::temp_directory_path(error_code& ec)
   1644 {
   1645   path p = fs::get_temp_directory_from_env(ec);
   1646   if (!ec)
   1647     {
   1648       auto st = status(p, ec);
   1649       if (ec)
   1650 	p.clear();
   1651       else if (!is_directory(st))
   1652 	{
   1653 	  p.clear();
   1654 	  ec = std::make_error_code(std::errc::not_a_directory);
   1655 	}
   1656     }
   1657   return p;
   1658 }
   1659 
   1660 fs::path
   1661 fs::weakly_canonical(const path& p)
   1662 {
   1663   path result;
   1664   if (exists(status(p)))
   1665     return canonical(p);
   1666 
   1667   path tmp;
   1668   auto iter = p.begin(), end = p.end();
   1669   // find leading elements of p that exist:
   1670   while (iter != end)
   1671     {
   1672       tmp = result / *iter;
   1673       if (exists(status(tmp)))
   1674 	swap(result, tmp);
   1675       else
   1676 	break;
   1677       ++iter;
   1678     }
   1679   // canonicalize:
   1680   if (!result.empty())
   1681     result = canonical(result);
   1682   // append the non-existing elements:
   1683   while (iter != end)
   1684     result /= *iter++;
   1685   // normalize:
   1686   return result.lexically_normal();
   1687 }
   1688 
   1689 fs::path
   1690 fs::weakly_canonical(const path& p, error_code& ec)
   1691 {
   1692   path result;
   1693   file_status st = status(p, ec);
   1694   if (exists(st))
   1695     return canonical(p, ec);
   1696   else if (status_known(st))
   1697     ec.clear();
   1698   else
   1699     return result;
   1700 
   1701   path tmp;
   1702   auto iter = p.begin(), end = p.end();
   1703   // find leading elements of p that exist:
   1704   while (iter != end)
   1705     {
   1706       tmp = result / *iter;
   1707       st = status(tmp, ec);
   1708       if (exists(st))
   1709 	swap(result, tmp);
   1710       else
   1711 	{
   1712 	  if (status_known(st))
   1713 	    ec.clear();
   1714 	  break;
   1715 	}
   1716       ++iter;
   1717     }
   1718   // canonicalize:
   1719   if (!ec && !result.empty())
   1720     result = canonical(result, ec);
   1721   if (ec)
   1722     result.clear();
   1723   else
   1724     {
   1725       // append the non-existing elements:
   1726       while (iter != end)
   1727 	result /= *iter++;
   1728       // normalize:
   1729       result = result.lexically_normal();
   1730     }
   1731   return result;
   1732 }
   1733