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