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