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