xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/src/filesystem/ops.cc (revision e7ac2a8b5bd66fa2e050809de09a075c36a7014d)
1 // Filesystem TS operations -*- C++ -*-
2 
3 // Copyright (C) 2014-2018 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 #endif
28 
29 #include <bits/largefile-config.h>
30 #include <experimental/filesystem>
31 #include <functional>
32 #include <ostream>
33 #include <stack>
34 #include <ext/stdio_filebuf.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <limits.h>  // PATH_MAX
39 #ifdef _GLIBCXX_HAVE_FCNTL_H
40 # include <fcntl.h>  // AT_FDCWD, AT_SYMLINK_NOFOLLOW
41 #endif
42 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
43 #  include <sys/stat.h>   // stat, utimensat, fchmodat
44 #endif
45 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
46 # include <sys/statvfs.h> // statvfs
47 #endif
48 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
49 # include <utime.h> // utime
50 #endif
51 
52 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
53   namespace experimental { namespace filesystem {
54 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
55 #include "ops-common.h"
56 
57 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
58 # undef utime
59 # define utime _wutime
60 # undef chmod
61 # define chmod _wchmod
62 #endif
63 
64 namespace fs = std::experimental::filesystem;
65 
66 fs::path
67 fs::absolute(const path& p, const path& base)
68 {
69   const bool has_root_dir = p.has_root_directory();
70   const bool has_root_name = p.has_root_name();
71   path abs;
72   if (has_root_dir && has_root_name)
73     abs = p;
74   else
75     {
76       abs = base.is_absolute() ? base : absolute(base);
77       if (has_root_dir)
78 	abs = abs.root_name() / p;
79       else if (has_root_name)
80 	abs = p.root_name() / abs.root_directory() / abs.relative_path()
81 	  / p.relative_path();
82       else
83 	abs = abs / p;
84     }
85   return abs;
86 }
87 
88 namespace
89 {
90 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
91   inline bool is_dot(wchar_t c) { return c == L'.'; }
92 #else
93   inline bool is_dot(char c) { return c == '.'; }
94 #endif
95 
96   inline bool is_dot(const fs::path& path)
97   {
98     const auto& filename = path.native();
99     return filename.size() == 1 && is_dot(filename[0]);
100   }
101 
102   inline bool is_dotdot(const fs::path& path)
103   {
104     const auto& filename = path.native();
105     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
106   }
107 
108   struct free_as_in_malloc
109   {
110     void operator()(void* p) const { ::free(p); }
111   };
112 
113   using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
114 }
115 
116 fs::path
117 fs::canonical(const path& p, const path& base, error_code& ec)
118 {
119   const path pa = absolute(p, base);
120   path result;
121 
122 #ifdef _GLIBCXX_USE_REALPATH
123   char_ptr buf{ nullptr };
124 # if _XOPEN_VERSION < 700
125   // Not safe to call realpath(path, NULL)
126   buf.reset( (char*)::malloc(PATH_MAX) );
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   typedef struct ::stat stat_type;
246 
247   using std::filesystem::is_not_found_errno;
248   using std::filesystem::file_time;
249   using std::filesystem::do_copy_file;
250 #endif // _GLIBCXX_HAVE_SYS_STAT_H
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       ? ::lstat(from.c_str(), &from_st)
268       : ::stat(from.c_str(), &from_st))
269     {
270       ec.assign(errno, std::generic_category());
271       return;
272     }
273   if (use_lstat
274       ? ::lstat(to.c_str(), &to_st)
275       : ::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     ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
482     if (::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 (::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_UNISTD_H
585   if (::link(to.c_str(), new_hard_link.c_str()))
586     ec.assign(errno, std::generic_category());
587   else
588     ec.clear();
589 #else
590   ec = std::make_error_code(std::errc::not_supported);
591 #endif
592 }
593 
594 void
595 fs::create_symlink(const path& to, const path& new_symlink)
596 {
597   error_code ec;
598   create_symlink(to, new_symlink, ec);
599   if (ec.value())
600     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
601 	  to, new_symlink, ec));
602 }
603 
604 void
605 fs::create_symlink(const path& to, const path& new_symlink,
606 		   error_code& ec) noexcept
607 {
608 #ifdef _GLIBCXX_HAVE_UNISTD_H
609   if (::symlink(to.c_str(), new_symlink.c_str()))
610     ec.assign(errno, std::generic_category());
611   else
612     ec.clear();
613 #else
614   ec = std::make_error_code(std::errc::not_supported);
615 #endif
616 }
617 
618 
619 fs::path
620 fs::current_path()
621 {
622   error_code ec;
623   path p = current_path(ec);
624   if (ec.value())
625     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
626   return p;
627 }
628 
629 fs::path
630 fs::current_path(error_code& ec)
631 {
632   path p;
633 #ifdef _GLIBCXX_HAVE_UNISTD_H
634 #ifdef __GLIBC__
635   if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
636     {
637       p.assign(cwd.get());
638       ec.clear();
639     }
640   else
641     ec.assign(errno, std::generic_category());
642 #else
643   long path_max = pathconf(".", _PC_PATH_MAX);
644   size_t size;
645   if (path_max == -1)
646       size = 1024;
647   else if (path_max > 10240)
648       size = 10240;
649   else
650       size = path_max;
651   for (char_ptr buf; p.empty(); size *= 2)
652     {
653       buf.reset((char*)malloc(size));
654       if (buf)
655 	{
656 	  if (getcwd(buf.get(), size))
657 	    {
658 	      p.assign(buf.get());
659 	      ec.clear();
660 	    }
661 	  else if (errno != ERANGE)
662 	    {
663 	      ec.assign(errno, std::generic_category());
664 	      return {};
665 	    }
666 	}
667       else
668 	{
669 	  ec = std::make_error_code(std::errc::not_enough_memory);
670 	  return {};
671 	}
672     }
673 #endif  // __GLIBC__
674 #else   // _GLIBCXX_HAVE_UNISTD_H
675   ec = std::make_error_code(std::errc::not_supported);
676 #endif
677   return p;
678 }
679 
680 void
681 fs::current_path(const path& p)
682 {
683   error_code ec;
684   current_path(p, ec);
685   if (ec.value())
686     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
687 }
688 
689 void
690 fs::current_path(const path& p, error_code& ec) noexcept
691 {
692 #ifdef _GLIBCXX_HAVE_UNISTD_H
693   if (::chdir(p.c_str()))
694     ec.assign(errno, std::generic_category());
695   else
696     ec.clear();
697 #else
698   ec = std::make_error_code(std::errc::not_supported);
699 #endif
700 }
701 
702 bool
703 fs::equivalent(const path& p1, const path& p2)
704 {
705   error_code ec;
706   auto result = equivalent(p1, p2, ec);
707   if (ec)
708     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
709 	  p1, p2, ec));
710   return result;
711 }
712 
713 bool
714 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
715 {
716 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
717   int err = 0;
718   file_status s1, s2;
719   stat_type st1, st2;
720   if (::stat(p1.c_str(), &st1) == 0)
721     s1 = make_file_status(st1);
722   else if (is_not_found_errno(errno))
723     s1.type(file_type::not_found);
724   else
725     err = errno;
726 
727   if (::stat(p2.c_str(), &st2) == 0)
728     s2 = make_file_status(st2);
729   else if (is_not_found_errno(errno))
730     s2.type(file_type::not_found);
731   else
732     err = errno;
733 
734   if (exists(s1) && exists(s2))
735     {
736       if (is_other(s1) && is_other(s2))
737 	{
738 	  ec = std::make_error_code(std::errc::not_supported);
739 	  return false;
740 	}
741       ec.clear();
742       if (is_other(s1) || is_other(s2))
743 	return false;
744       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
745     }
746   else if (!exists(s1) && !exists(s2))
747     ec = std::make_error_code(std::errc::no_such_file_or_directory);
748   else if (err)
749     ec.assign(err, std::generic_category());
750   else
751     ec.clear();
752   return false;
753 #else
754   ec = std::make_error_code(std::errc::not_supported);
755 #endif
756   return false;
757 }
758 
759 std::uintmax_t
760 fs::file_size(const path& p)
761 {
762   error_code ec;
763   auto sz = file_size(p, ec);
764   if (ec.value())
765     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
766   return sz;
767 }
768 
769 namespace
770 {
771   template<typename Accessor, typename T>
772     inline T
773     do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
774     {
775 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
776       stat_type st;
777       if (::stat(p.c_str(), &st))
778 	{
779 	  ec.assign(errno, std::generic_category());
780 	  return deflt;
781 	}
782       ec.clear();
783       return f(st);
784 #else
785       ec = std::make_error_code(std::errc::not_supported);
786       return deflt;
787 #endif
788     }
789 }
790 
791 std::uintmax_t
792 fs::file_size(const path& p, error_code& ec) noexcept
793 {
794   struct S
795   {
796     S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
797     S() : type(file_type::not_found) { }
798     file_type type;
799     uintmax_t size;
800   };
801   auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
802   if (s.type == file_type::regular)
803     return s.size;
804   if (!ec)
805     {
806       if (s.type == file_type::directory)
807 	ec = std::make_error_code(std::errc::is_a_directory);
808       else
809 	ec = std::make_error_code(std::errc::not_supported);
810     }
811   return -1;
812 }
813 
814 std::uintmax_t
815 fs::hard_link_count(const path& p)
816 {
817   error_code ec;
818   auto count = hard_link_count(p, ec);
819   if (ec.value())
820     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
821   return count;
822 }
823 
824 std::uintmax_t
825 fs::hard_link_count(const path& p, error_code& ec) noexcept
826 {
827   return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
828 		 static_cast<uintmax_t>(-1));
829 }
830 
831 bool
832 fs::is_empty(const path& p)
833 {
834   error_code ec;
835   bool e = is_empty(p, ec);
836   if (ec)
837     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
838 					     p, ec));
839   return e;
840 }
841 
842 bool
843 fs::is_empty(const path& p, error_code& ec) noexcept
844 {
845   auto s = status(p, ec);
846   if (ec)
847     return false;
848   bool empty = fs::is_directory(s)
849     ? fs::directory_iterator(p, ec) == fs::directory_iterator()
850     : fs::file_size(p, ec) == 0;
851   return ec ? false : empty;
852 }
853 
854 fs::file_time_type
855 fs::last_write_time(const path& p)
856 {
857   error_code ec;
858   auto t = last_write_time(p, ec);
859   if (ec.value())
860     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
861   return t;
862 }
863 
864 fs::file_time_type
865 fs::last_write_time(const path& p, error_code& ec) noexcept
866 {
867   return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
868 		 file_time_type::min());
869 }
870 
871 void
872 fs::last_write_time(const path& p, file_time_type new_time)
873 {
874   error_code ec;
875   last_write_time(p, new_time, ec);
876   if (ec.value())
877     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
878 }
879 
880 void
881 fs::last_write_time(const path& p __attribute__((__unused__)),
882 		    file_time_type new_time, error_code& ec) noexcept
883 {
884   auto d = new_time.time_since_epoch();
885   auto s = chrono::duration_cast<chrono::seconds>(d);
886 #if _GLIBCXX_USE_UTIMENSAT
887   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
888   if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
889     {
890       --s;
891       ns += chrono::seconds(1);
892     }
893   struct ::timespec ts[2];
894   ts[0].tv_sec = 0;
895   ts[0].tv_nsec = UTIME_OMIT;
896   ts[1].tv_sec = static_cast<std::time_t>(s.count());
897   ts[1].tv_nsec = static_cast<long>(ns.count());
898   if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
899     ec.assign(errno, std::generic_category());
900   else
901     ec.clear();
902 #elif _GLIBCXX_HAVE_UTIME_H
903   ::utimbuf times;
904   times.modtime = s.count();
905   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
906 			 times.modtime);
907   if (::utime(p.c_str(), &times))
908     ec.assign(errno, std::generic_category());
909   else
910     ec.clear();
911 #else
912   ec = std::make_error_code(std::errc::not_supported);
913 #endif
914 }
915 
916 void
917 fs::permissions(const path& p, perms prms)
918 {
919   error_code ec;
920   permissions(p, prms, ec);
921   if (ec.value())
922     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
923 }
924 
925 void
926 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
927 {
928   const bool add = is_set(prms, perms::add_perms);
929   const bool remove = is_set(prms, perms::remove_perms);
930   const bool nofollow = is_set(prms, perms::symlink_nofollow);
931   if (add && remove)
932     {
933       ec = std::make_error_code(std::errc::invalid_argument);
934       return;
935     }
936 
937   prms &= perms::mask;
938 
939   file_status st;
940   if (add || remove || nofollow)
941     {
942       st = nofollow ? symlink_status(p, ec) : status(p, ec);
943       if (ec)
944 	return;
945       auto curr = st.permissions();
946       if (add)
947 	prms |= curr;
948       else if (remove)
949 	prms = curr & ~prms;
950     }
951 
952   int err = 0;
953 #if _GLIBCXX_USE_FCHMODAT
954   const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
955   if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
956     err = errno;
957 #else
958   if (nofollow && is_symlink(st))
959     ec = std::make_error_code(std::errc::operation_not_supported);
960   else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
961     err = errno;
962 #endif
963 
964   if (err)
965     ec.assign(err, std::generic_category());
966   else
967     ec.clear();
968 }
969 
970 fs::path
971 fs::read_symlink(const path& p)
972 {
973   error_code ec;
974   path tgt = read_symlink(p, ec);
975   if (ec.value())
976     _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
977   return tgt;
978 }
979 
980 fs::path fs::read_symlink(const path& p, error_code& ec)
981 {
982   path result;
983 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
984   stat_type st;
985   if (::lstat(p.c_str(), &st))
986     {
987       ec.assign(errno, std::generic_category());
988       return result;
989     }
990   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
991   do
992     {
993       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
994       if (len == -1)
995 	{
996 	  ec.assign(errno, std::generic_category());
997 	  return result;
998 	}
999       else if (len == (ssize_t)buf.size())
1000 	{
1001 	  if (buf.size() > 4096)
1002 	    {
1003 	      ec.assign(ENAMETOOLONG, std::generic_category());
1004 	      return result;
1005 	    }
1006 	  buf.resize(buf.size() * 2);
1007 	}
1008       else
1009 	{
1010 	  buf.resize(len);
1011 	  result.assign(buf);
1012 	  ec.clear();
1013 	  break;
1014 	}
1015     }
1016   while (true);
1017 #else
1018   ec = std::make_error_code(std::errc::not_supported);
1019 #endif
1020   return result;
1021 }
1022 
1023 
1024 bool
1025 fs::remove(const path& p)
1026 {
1027   error_code ec;
1028   bool result = fs::remove(p, ec);
1029   if (ec)
1030     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1031   return result;
1032 }
1033 
1034 bool
1035 fs::remove(const path& p, error_code& ec) noexcept
1036 {
1037   if (::remove(p.c_str()) == 0)
1038     {
1039       ec.clear();
1040       return true;
1041     }
1042   else if (errno == ENOENT)
1043     ec.clear();
1044   else
1045     ec.assign(errno, std::generic_category());
1046   return false;
1047 }
1048 
1049 
1050 std::uintmax_t
1051 fs::remove_all(const path& p)
1052 {
1053   error_code ec;
1054   const auto result = remove_all(p, ec);
1055   if (ec)
1056     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1057   return result;
1058 }
1059 
1060 std::uintmax_t
1061 fs::remove_all(const path& p, error_code& ec) noexcept
1062 {
1063   const auto s = symlink_status(p, ec);
1064   if (!status_known(s))
1065     return -1;
1066 
1067   ec.clear();
1068   if (s.type() == file_type::not_found)
1069     return 0;
1070 
1071   uintmax_t count = 0;
1072   if (s.type() == file_type::directory)
1073     {
1074       directory_iterator d(p, ec), end;
1075       while (!ec && d != end)
1076 	{
1077 	  const auto removed = fs::remove_all(d->path(), ec);
1078 	  if (removed == numeric_limits<uintmax_t>::max())
1079 	    return -1;
1080 	  count += removed;
1081 	  d.increment(ec);
1082 	  if (ec)
1083 	    return -1;
1084 	}
1085     }
1086 
1087   if (fs::remove(p, ec))
1088     ++count;
1089   return ec ? -1 : count;
1090 }
1091 
1092 void
1093 fs::rename(const path& from, const path& to)
1094 {
1095   error_code ec;
1096   rename(from, to, ec);
1097   if (ec.value())
1098     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1099 }
1100 
1101 void
1102 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1103 {
1104   if (::rename(from.c_str(), to.c_str()))
1105     ec.assign(errno, std::generic_category());
1106   else
1107     ec.clear();
1108 }
1109 
1110 void
1111 fs::resize_file(const path& p, uintmax_t size)
1112 {
1113   error_code ec;
1114   resize_file(p, size, ec);
1115   if (ec.value())
1116     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1117 }
1118 
1119 void
1120 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1121 {
1122 #ifdef _GLIBCXX_HAVE_UNISTD_H
1123   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1124     ec.assign(EINVAL, std::generic_category());
1125   else if (::truncate(p.c_str(), size))
1126     ec.assign(errno, std::generic_category());
1127   else
1128     ec.clear();
1129 #else
1130   ec = std::make_error_code(std::errc::not_supported);
1131 #endif
1132 }
1133 
1134 
1135 fs::space_info
1136 fs::space(const path& p)
1137 {
1138   error_code ec;
1139   space_info s = space(p, ec);
1140   if (ec.value())
1141     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1142   return s;
1143 }
1144 
1145 fs::space_info
1146 fs::space(const path& p, error_code& ec) noexcept
1147 {
1148   space_info info = {
1149     static_cast<uintmax_t>(-1),
1150     static_cast<uintmax_t>(-1),
1151     static_cast<uintmax_t>(-1)
1152   };
1153 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1154   struct ::statvfs f;
1155   if (::statvfs(p.c_str(), &f))
1156       ec.assign(errno, std::generic_category());
1157   else
1158     {
1159       uintmax_t fragment_size = f.f_frsize;
1160       info = space_info{
1161 	f.f_blocks * fragment_size,
1162 	f.f_bfree * fragment_size,
1163 	f.f_bavail * fragment_size
1164       };
1165       ec.clear();
1166     }
1167 #else
1168   ec = std::make_error_code(std::errc::not_supported);
1169 #endif
1170   return info;
1171 }
1172 
1173 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1174 fs::file_status
1175 fs::status(const fs::path& p, error_code& ec) noexcept
1176 {
1177   file_status status;
1178   stat_type st;
1179   if (::stat(p.c_str(), &st))
1180     {
1181       int err = errno;
1182       ec.assign(err, std::generic_category());
1183       if (is_not_found_errno(err))
1184 	status.type(file_type::not_found);
1185 #ifdef EOVERFLOW
1186       else if (err == EOVERFLOW)
1187 	status.type(file_type::unknown);
1188 #endif
1189     }
1190   else
1191     {
1192       status = make_file_status(st);
1193       ec.clear();
1194     }
1195   return status;
1196 }
1197 
1198 fs::file_status
1199 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1200 {
1201   file_status status;
1202   stat_type st;
1203   if (::lstat(p.c_str(), &st))
1204     {
1205       int err = errno;
1206       ec.assign(err, std::generic_category());
1207       if (is_not_found_errno(err))
1208 	status.type(file_type::not_found);
1209     }
1210   else
1211     {
1212       status = make_file_status(st);
1213       ec.clear();
1214     }
1215   return status;
1216 }
1217 #endif
1218 
1219 fs::file_status
1220 fs::status(const fs::path& p)
1221 {
1222   std::error_code ec;
1223   auto result = status(p, ec);
1224   if (result.type() == file_type::none)
1225     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1226   return result;
1227 }
1228 
1229 fs::file_status
1230 fs::symlink_status(const fs::path& p)
1231 {
1232   std::error_code ec;
1233   auto result = symlink_status(p, ec);
1234   if (result.type() == file_type::none)
1235     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1236   return result;
1237 }
1238 
1239 fs::path
1240 fs::system_complete(const path& p)
1241 {
1242   error_code ec;
1243   path comp = system_complete(p, ec);
1244   if (ec.value())
1245     _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1246   return comp;
1247 }
1248 
1249 fs::path
1250 fs::system_complete(const path& p, error_code& ec)
1251 {
1252   path base = current_path(ec);
1253 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1254   if (p.is_absolute() || !p.has_root_name()
1255       || p.root_name() == base.root_name())
1256     return absolute(p, base);
1257   // else TODO
1258   ec = std::make_error_code(std::errc::not_supported);
1259   return {};
1260 #else
1261   if (ec.value())
1262     return {};
1263   return absolute(p, base);
1264 #endif
1265 }
1266 
1267 fs::path fs::temp_directory_path()
1268 {
1269   error_code ec;
1270   path tmp = temp_directory_path(ec);
1271   if (ec.value())
1272     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1273   return tmp;
1274 }
1275 
1276 fs::path fs::temp_directory_path(error_code& ec)
1277 {
1278 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1279   ec = std::make_error_code(std::errc::not_supported);
1280   return {}; // TODO
1281 #else
1282   const char* tmpdir = nullptr;
1283   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1284   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1285     tmpdir = ::getenv(*e);
1286   path p = tmpdir ? tmpdir : "/tmp";
1287   auto st = status(p, ec);
1288   if (!ec)
1289     {
1290       if (is_directory(st))
1291 	{
1292 	  ec.clear();
1293 	  return p;
1294 	}
1295       else
1296 	ec = std::make_error_code(std::errc::not_a_directory);
1297     }
1298   return {};
1299 #endif
1300 }
1301 
1302