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