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