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