1 // Filesystem 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 # define NEED_DO_COPY_FILE
28 #endif
29 
30 #include <bits/largefile-config.h>
31 #include <filesystem>
32 #include <experimental/filesystem>
33 #include <functional>
34 #include <ostream>
35 #include <stack>
36 #include <ext/stdio_filebuf.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <limits.h>  // PATH_MAX
41 #ifdef _GLIBCXX_HAVE_FCNTL_H
42 # include <fcntl.h>  // AT_FDCWD, AT_SYMLINK_NOFOLLOW
43 #endif
44 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
45 #  include <sys/stat.h>   // stat, utimensat, fchmodat
46 #endif
47 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
48 # include <sys/statvfs.h> // statvfs
49 #endif
50 #ifdef _GLIBCXX_USE_SENDFILE
51 # include <sys/sendfile.h> // sendfile
52 #endif
53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
54 # include <utime.h> // utime
55 #endif
56 
57 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
58 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
59 #include "ops-common.h"
60 
61 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
62 # undef utime
63 # define utime _wutime
64 # undef chmod
65 # define chmod _wchmod
66 #endif
67 
68 namespace fs = std::filesystem;
69 
70 fs::path
absolute(const path & p)71 fs::absolute(const path& p)
72 {
73 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
74   error_code ec;
75   path ret = absolute(p, ec);
76   if (ec)
77     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
78 	std::make_error_code(errc::not_supported)));
79   return ret;
80 #else
81   if (p.empty())
82     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
83 	  make_error_code(std::errc::invalid_argument)));
84   return current_path() / p;
85 #endif
86 }
87 
88 fs::path
absolute(const path & p,error_code & ec)89 fs::absolute(const path& p, error_code& ec)
90 {
91   path ret;
92   if (p.empty())
93     {
94       ec = make_error_code(std::errc::invalid_argument);
95       return ret;
96     }
97   if (p.is_absolute())
98     {
99       ec.clear();
100       ret = p;
101       return ret;
102     }
103 
104 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
105   ec = std::make_error_code(errc::not_supported);
106 #else
107   ret = current_path(ec);
108   ret /= p;
109 #endif
110   return ret;
111 }
112 
113 namespace
114 {
115 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_dot(wchar_t c)116   inline bool is_dot(wchar_t c) { return c == L'.'; }
117 #else
118   inline bool is_dot(char c) { return c == '.'; }
119 #endif
120 
is_dot(const fs::path & path)121   inline bool is_dot(const fs::path& path)
122   {
123     const auto& filename = path.native();
124     return filename.size() == 1 && is_dot(filename[0]);
125   }
126 
is_dotdot(const fs::path & path)127   inline bool is_dotdot(const fs::path& path)
128   {
129     const auto& filename = path.native();
130     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
131   }
132 
133   struct free_as_in_malloc
134   {
operator ()__anon26cbbbb70111::free_as_in_malloc135     void operator()(void* p) const { ::free(p); }
136   };
137 
138   using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
139 }
140 
141 fs::path
canonical(const path & p,error_code & ec)142 fs::canonical(const path& p, error_code& ec)
143 {
144   path result;
145   const path pa = absolute(p, ec);
146   if (ec)
147     return result;
148 
149 #ifdef _GLIBCXX_USE_REALPATH
150   char_ptr buf{ nullptr };
151 # if _XOPEN_VERSION < 700
152   // Not safe to call realpath(path, NULL)
153   buf.reset( (char*)::malloc(PATH_MAX) );
154 # endif
155   if (char* rp = ::realpath(pa.c_str(), buf.get()))
156     {
157       if (buf == nullptr)
158 	buf.reset(rp);
159       result.assign(rp);
160       ec.clear();
161       return result;
162     }
163   if (errno != ENAMETOOLONG)
164     {
165       ec.assign(errno, std::generic_category());
166       return result;
167     }
168 #endif
169 
170   if (!exists(pa, ec))
171     {
172       if (!ec)
173 	ec = make_error_code(std::errc::no_such_file_or_directory);
174       return result;
175     }
176   // else: we know there are (currently) no unresolvable symlink loops
177 
178   result = pa.root_path();
179 
180   deque<path> cmpts;
181   for (auto& f : pa.relative_path())
182     cmpts.push_back(f);
183 
184   int max_allowed_symlinks = 40;
185 
186   while (!cmpts.empty() && !ec)
187     {
188       path f = std::move(cmpts.front());
189       cmpts.pop_front();
190 
191       if (f.empty())
192 	{
193 	  // ignore empty element
194 	}
195       else if (is_dot(f))
196 	{
197 	  if (!is_directory(result, ec) && !ec)
198 	    ec.assign(ENOTDIR, std::generic_category());
199 	}
200       else if (is_dotdot(f))
201 	{
202 	  auto parent = result.parent_path();
203 	  if (parent.empty())
204 	    result = pa.root_path();
205 	  else
206 	    result.swap(parent);
207 	}
208       else
209 	{
210 	  result /= f;
211 
212 	  if (is_symlink(result, ec))
213 	    {
214 	      path link = read_symlink(result, ec);
215 	      if (!ec)
216 		{
217 		  if (--max_allowed_symlinks == 0)
218 		    ec.assign(ELOOP, std::generic_category());
219 		  else
220 		    {
221 		      if (link.is_absolute())
222 			{
223 			  result = link.root_path();
224 			  link = link.relative_path();
225 			}
226 		      else
227 			result = result.parent_path();
228 
229 		      cmpts.insert(cmpts.begin(), link.begin(), link.end());
230 		    }
231 		}
232 	    }
233 	}
234     }
235 
236   if (ec || !exists(result, ec))
237     result.clear();
238 
239   return result;
240 }
241 
242 fs::path
canonical(const path & p)243 fs::canonical(const path& p)
244 {
245   error_code ec;
246   path res = canonical(p, ec);
247   if (ec)
248     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
249 					     p, ec));
250   return res;
251 }
252 
253 void
copy(const path & from,const path & to,copy_options options)254 fs::copy(const path& from, const path& to, copy_options options)
255 {
256   error_code ec;
257   copy(from, to, options, ec);
258   if (ec)
259     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
260 }
261 
262 namespace std::filesystem
263 {
264   // Need this as there's no 'perm_options::none' enumerator.
is_set(fs::perm_options obj,fs::perm_options bits)265   inline bool is_set(fs::perm_options obj, fs::perm_options bits)
266   {
267     return (obj & bits) != fs::perm_options{};
268   }
269 }
270 
271 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
272 #ifdef NEED_DO_COPY_FILE
273 bool
do_copy_file(const char * from,const char * to,copy_options_existing_file options,stat_type * from_st,stat_type * to_st,std::error_code & ec)274 fs::do_copy_file(const char* from, const char* to,
275 		 copy_options_existing_file options,
276 		 stat_type* from_st, stat_type* to_st,
277 		 std::error_code& ec) noexcept
278 {
279   stat_type st1, st2;
280   fs::file_status t, f;
281 
282   if (to_st == nullptr)
283     {
284       if (::stat(to, &st1))
285 	{
286 	  const int err = errno;
287 	  if (!is_not_found_errno(err))
288 	    {
289 	      ec.assign(err, std::generic_category());
290 	      return false;
291 	    }
292 	}
293       else
294 	to_st = &st1;
295     }
296   else if (to_st == from_st)
297     to_st = nullptr;
298 
299   if (to_st == nullptr)
300     t = fs::file_status{fs::file_type::not_found};
301   else
302     t = make_file_status(*to_st);
303 
304   if (from_st == nullptr)
305     {
306       if (::stat(from, &st2))
307 	{
308 	  ec.assign(errno, std::generic_category());
309 	  return false;
310 	}
311       else
312 	from_st = &st2;
313     }
314   f = make_file_status(*from_st);
315   // _GLIBCXX_RESOLVE_LIB_DEFECTS
316   // 2712. copy_file() has a number of unspecified error conditions
317   if (!is_regular_file(f))
318     {
319       ec = std::make_error_code(std::errc::not_supported);
320       return false;
321     }
322 
323   if (exists(t))
324     {
325       if (!is_regular_file(t))
326 	{
327 	  ec = std::make_error_code(std::errc::not_supported);
328 	  return false;
329 	}
330 
331       if (to_st->st_dev == from_st->st_dev
332 	  && to_st->st_ino == from_st->st_ino)
333 	{
334 	  ec = std::make_error_code(std::errc::file_exists);
335 	  return false;
336 	}
337 
338       if (options.skip)
339 	{
340 	  ec.clear();
341 	  return false;
342 	}
343       else if (options.update)
344 	{
345 	  const auto from_mtime = file_time(*from_st, ec);
346 	  if (ec)
347 	    return false;
348 	  if ((from_mtime <= file_time(*to_st, ec)) || ec)
349 	    return false;
350 	}
351       else if (!options.overwrite)
352 	{
353 	  ec = std::make_error_code(std::errc::file_exists);
354 	  return false;
355 	}
356       else if (!is_regular_file(t))
357 	{
358 	  ec = std::make_error_code(std::errc::not_supported);
359 	  return false;
360 	}
361     }
362 
363   struct CloseFD {
364     ~CloseFD() { if (fd != -1) ::close(fd); }
365     bool close() { return ::close(std::exchange(fd, -1)) == 0; }
366     int fd;
367   };
368 
369   CloseFD in = { ::open(from, O_RDONLY) };
370   if (in.fd == -1)
371     {
372       ec.assign(errno, std::generic_category());
373       return false;
374     }
375   int oflag = O_WRONLY|O_CREAT;
376   if (options.overwrite || options.update)
377     oflag |= O_TRUNC;
378   else
379     oflag |= O_EXCL;
380   CloseFD out = { ::open(to, oflag, S_IWUSR) };
381   if (out.fd == -1)
382     {
383       if (errno == EEXIST && options.skip)
384 	ec.clear();
385       else
386 	ec.assign(errno, std::generic_category());
387       return false;
388     }
389 
390 #ifdef _GLIBCXX_USE_FCHMOD
391   if (::fchmod(out.fd, from_st->st_mode))
392 #elif defined _GLIBCXX_USE_FCHMODAT
393   if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
394 #else
395   if (::chmod(to, from_st->st_mode))
396 #endif
397     {
398       ec.assign(errno, std::generic_category());
399       return false;
400     }
401 
402   size_t count = from_st->st_size;
403 #ifdef _GLIBCXX_USE_SENDFILE
404   off_t offset = 0;
405   ssize_t n = ::sendfile(out.fd, in.fd, &offset, count);
406   if (n < 0 && errno != ENOSYS && errno != EINVAL)
407     {
408       ec.assign(errno, std::generic_category());
409       return false;
410     }
411   if ((size_t)n == count)
412     {
413       if (!out.close() || !in.close())
414 	{
415 	  ec.assign(errno, std::generic_category());
416 	  return false;
417 	}
418       ec.clear();
419       return true;
420     }
421   else if (n > 0)
422     count -= n;
423 #endif // _GLIBCXX_USE_SENDFILE
424 
425   using std::ios;
426   __gnu_cxx::stdio_filebuf<char> sbin(in.fd, ios::in|ios::binary);
427   __gnu_cxx::stdio_filebuf<char> sbout(out.fd, ios::out|ios::binary);
428 
429   if (sbin.is_open())
430     in.fd = -1;
431   if (sbout.is_open())
432     out.fd = -1;
433 
434 #ifdef _GLIBCXX_USE_SENDFILE
435   if (n != 0)
436     {
437       if (n < 0)
438 	n = 0;
439 
440       const auto p1 = sbin.pubseekoff(n, ios::beg, ios::in);
441       const auto p2 = sbout.pubseekoff(n, ios::beg, ios::out);
442 
443       const std::streampos errpos(std::streamoff(-1));
444       if (p1 == errpos || p2 == errpos)
445 	{
446 	  ec = std::make_error_code(std::errc::io_error);
447 	  return false;
448 	}
449     }
450 #endif
451 
452   if (count && !(std::ostream(&sbout) << &sbin))
453     {
454       ec = std::make_error_code(std::errc::io_error);
455       return false;
456     }
457   if (!sbout.close() || !sbin.close())
458     {
459       ec.assign(errno, std::generic_category());
460       return false;
461     }
462   ec.clear();
463   return true;
464 }
465 #endif // NEED_DO_COPY_FILE
466 #endif // _GLIBCXX_HAVE_SYS_STAT_H
467 
468 void
copy(const path & from,const path & to,copy_options options,error_code & ec)469 fs::copy(const path& from, const path& to, copy_options options,
470 	 error_code& ec)
471 {
472   const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
473   const bool create_symlinks = is_set(options, copy_options::create_symlinks);
474   const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
475   const bool use_lstat = create_symlinks || skip_symlinks;
476 
477   file_status f, t;
478   stat_type from_st, to_st;
479   // _GLIBCXX_RESOLVE_LIB_DEFECTS
480   // 2681. filesystem::copy() cannot copy symlinks
481   if (use_lstat || copy_symlinks
482       ? ::lstat(from.c_str(), &from_st)
483       : ::stat(from.c_str(), &from_st))
484     {
485       ec.assign(errno, std::generic_category());
486       return;
487     }
488   if (use_lstat
489       ? ::lstat(to.c_str(), &to_st)
490       : ::stat(to.c_str(), &to_st))
491     {
492       if (!is_not_found_errno(errno))
493 	{
494 	  ec.assign(errno, std::generic_category());
495 	  return;
496 	}
497       t = file_status{file_type::not_found};
498     }
499   else
500     t = make_file_status(to_st);
501   f = make_file_status(from_st);
502 
503   if (exists(t) && !is_other(t) && !is_other(f)
504       && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
505     {
506       ec = std::make_error_code(std::errc::file_exists);
507       return;
508     }
509   if (is_other(f) || is_other(t))
510     {
511       ec = std::make_error_code(std::errc::not_supported);
512       return;
513     }
514   if (is_directory(f) && is_regular_file(t))
515     {
516       ec = std::make_error_code(std::errc::is_a_directory);
517       return;
518     }
519 
520   if (is_symlink(f))
521     {
522       if (skip_symlinks)
523 	ec.clear();
524       else if (!exists(t) && copy_symlinks)
525 	copy_symlink(from, to, ec);
526       else
527 	// Not clear what should be done here.
528 	// "Otherwise report an error as specified in Error reporting (7)."
529 	ec = std::make_error_code(std::errc::invalid_argument);
530     }
531   else if (is_regular_file(f))
532     {
533       if (is_set(options, copy_options::directories_only))
534 	ec.clear();
535       else if (create_symlinks)
536 	create_symlink(from, to, ec);
537       else if (is_set(options, copy_options::create_hard_links))
538 	create_hard_link(from, to, ec);
539       else if (is_directory(t))
540 	do_copy_file(from.c_str(), (to / from.filename()).c_str(),
541 		     copy_file_options(options), &from_st, nullptr, ec);
542       else
543 	{
544 	  auto ptr = exists(t) ? &to_st : &from_st;
545 	  do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
546 		       &from_st, ptr, ec);
547 	}
548     }
549   // _GLIBCXX_RESOLVE_LIB_DEFECTS
550   // 2682. filesystem::copy() won't create a symlink to a directory
551   else if (is_directory(f) && create_symlinks)
552     ec = std::make_error_code(errc::is_a_directory);
553   else if (is_directory(f) && (is_set(options, copy_options::recursive)
554 			       || options == copy_options::none))
555     {
556       if (!exists(t))
557 	if (!create_directory(to, from, ec))
558 	  return;
559       // set an unused bit in options to disable further recursion
560       if (!is_set(options, copy_options::recursive))
561 	options |= static_cast<copy_options>(4096);
562       for (const directory_entry& x : directory_iterator(from))
563 	copy(x.path(), to/x.path().filename(), options, ec);
564     }
565   // _GLIBCXX_RESOLVE_LIB_DEFECTS
566   // 2683. filesystem::copy() says "no effects"
567   else
568     ec.clear();
569 }
570 
571 bool
copy_file(const path & from,const path & to,copy_options option)572 fs::copy_file(const path& from, const path& to, copy_options option)
573 {
574   error_code ec;
575   bool result = copy_file(from, to, option, ec);
576   if (ec)
577     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
578 					     ec));
579   return result;
580 }
581 
582 bool
copy_file(const path & from,const path & to,copy_options options,error_code & ec)583 fs::copy_file(const path& from, const path& to, copy_options options,
584 	      error_code& ec)
585 {
586 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
587   return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
588 		      nullptr, nullptr, ec);
589 #else
590   ec = std::make_error_code(std::errc::not_supported);
591   return false;
592 #endif
593 }
594 
595 
596 void
copy_symlink(const path & existing_symlink,const path & new_symlink)597 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
598 {
599   error_code ec;
600   copy_symlink(existing_symlink, new_symlink, ec);
601   if (ec)
602     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
603 	  existing_symlink, new_symlink, ec));
604 }
605 
606 void
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)607 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
608 		 error_code& ec) noexcept
609 {
610   auto p = read_symlink(existing_symlink, ec);
611   if (ec)
612     return;
613 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
614   if (is_directory(p))
615     {
616       create_directory_symlink(p, new_symlink, ec);
617       return;
618     }
619 #endif
620   create_symlink(p, new_symlink, ec);
621 }
622 
623 
624 bool
create_directories(const path & p)625 fs::create_directories(const path& p)
626 {
627   error_code ec;
628   bool result = create_directories(p, ec);
629   if (ec)
630     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
631 					     ec));
632   return result;
633 }
634 
635 bool
create_directories(const path & p,error_code & ec)636 fs::create_directories(const path& p, error_code& ec)
637 {
638   if (p.empty())
639     {
640       ec = std::make_error_code(errc::invalid_argument);
641       return false;
642     }
643 
644   file_status st = symlink_status(p, ec);
645   if (is_directory(st))
646     return false;
647   else if (ec && !status_known(st))
648     return false;
649   else if (exists(st))
650     {
651       if (!ec)
652 	ec = std::make_error_code(std::errc::not_a_directory);
653       return false;
654     }
655 
656   std::stack<path> missing;
657   path pp = p;
658 
659   // Strip any trailing slash
660   if (pp.has_relative_path() && !pp.has_filename())
661     pp = pp.parent_path();
662 
663   do
664     {
665       const auto& filename = pp.filename();
666       if (is_dot(filename) || is_dotdot(filename))
667 	pp = pp.parent_path();
668       else
669 	{
670 	  missing.push(std::move(pp));
671 	  if (missing.size() > 1000) // sanity check
672 	    {
673 	      ec = std::make_error_code(std::errc::filename_too_long);
674 	      return false;
675 	    }
676 	  pp = missing.top().parent_path();
677 	}
678 
679       if (pp.empty())
680 	break;
681 
682       st = status(pp, ec);
683       if (exists(st))
684 	{
685 	  if (ec)
686 	    return false;
687 	  if (!is_directory(st))
688 	    {
689 	      ec = std::make_error_code(std::errc::not_a_directory);
690 	      return false;
691 	    }
692 	}
693 
694       if (ec && exists(st))
695 	return false;
696     }
697   while (st.type() == file_type::not_found);
698 
699   bool created;
700   do
701     {
702       const path& top = missing.top();
703       created = create_directory(top, ec);
704       if (ec)
705 	return false;
706       missing.pop();
707     }
708   while (!missing.empty());
709 
710   return created;
711 }
712 
713 namespace
714 {
715   bool
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)716   create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
717   {
718     bool created = false;
719 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
720     ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
721     if (::mkdir(p.c_str(), mode))
722       {
723 	const int err = errno;
724 	if (err != EEXIST || !is_directory(p, ec))
725 	  ec.assign(err, std::generic_category());
726       }
727     else
728       {
729 	ec.clear();
730 	created = true;
731       }
732 #else
733     ec = std::make_error_code(std::errc::not_supported);
734 #endif
735     return created;
736   }
737 } // namespace
738 
739 bool
create_directory(const path & p)740 fs::create_directory(const path& p)
741 {
742   error_code ec;
743   bool result = create_directory(p, ec);
744   if (ec)
745     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
746 	  ec));
747   return result;
748 }
749 
750 bool
create_directory(const path & p,error_code & ec)751 fs::create_directory(const path& p, error_code& ec) noexcept
752 {
753   return create_dir(p, perms::all, ec);
754 }
755 
756 
757 bool
create_directory(const path & p,const path & attributes)758 fs::create_directory(const path& p, const path& attributes)
759 {
760   error_code ec;
761   bool result = create_directory(p, attributes, ec);
762   if (ec)
763     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
764 	  ec));
765   return result;
766 }
767 
768 bool
create_directory(const path & p,const path & attributes,error_code & ec)769 fs::create_directory(const path& p, const path& attributes,
770 		     error_code& ec) noexcept
771 {
772 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
773   stat_type st;
774   if (::stat(attributes.c_str(), &st))
775     {
776       ec.assign(errno, std::generic_category());
777       return false;
778     }
779   return create_dir(p, static_cast<perms>(st.st_mode), ec);
780 #else
781   ec = std::make_error_code(std::errc::not_supported);
782   return false;
783 #endif
784 }
785 
786 
787 void
create_directory_symlink(const path & to,const path & new_symlink)788 fs::create_directory_symlink(const path& to, const path& new_symlink)
789 {
790   error_code ec;
791   create_directory_symlink(to, new_symlink, ec);
792   if (ec)
793     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
794 	  to, new_symlink, ec));
795 }
796 
797 void
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)798 fs::create_directory_symlink(const path& to, const path& new_symlink,
799 			     error_code& ec) noexcept
800 {
801 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
802   ec = std::make_error_code(std::errc::not_supported);
803 #else
804   create_symlink(to, new_symlink, ec);
805 #endif
806 }
807 
808 
809 void
create_hard_link(const path & to,const path & new_hard_link)810 fs::create_hard_link(const path& to, const path& new_hard_link)
811 {
812   error_code ec;
813   create_hard_link(to, new_hard_link, ec);
814   if (ec)
815     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
816 	  to, new_hard_link, ec));
817 }
818 
819 void
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)820 fs::create_hard_link(const path& to, const path& new_hard_link,
821 		     error_code& ec) noexcept
822 {
823 #ifdef _GLIBCXX_HAVE_UNISTD_H
824   if (::link(to.c_str(), new_hard_link.c_str()))
825     ec.assign(errno, std::generic_category());
826   else
827     ec.clear();
828 #else
829   ec = std::make_error_code(std::errc::not_supported);
830 #endif
831 }
832 
833 void
create_symlink(const path & to,const path & new_symlink)834 fs::create_symlink(const path& to, const path& new_symlink)
835 {
836   error_code ec;
837   create_symlink(to, new_symlink, ec);
838   if (ec)
839     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
840 	  to, new_symlink, ec));
841 }
842 
843 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)844 fs::create_symlink(const path& to, const path& new_symlink,
845 		   error_code& ec) noexcept
846 {
847 #ifdef _GLIBCXX_HAVE_UNISTD_H
848   if (::symlink(to.c_str(), new_symlink.c_str()))
849     ec.assign(errno, std::generic_category());
850   else
851     ec.clear();
852 #else
853   ec = std::make_error_code(std::errc::not_supported);
854 #endif
855 }
856 
857 
858 fs::path
current_path()859 fs::current_path()
860 {
861   error_code ec;
862   path p = current_path(ec);
863   if (ec)
864     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
865   return p;
866 }
867 
868 fs::path
current_path(error_code & ec)869 fs::current_path(error_code& ec)
870 {
871   path p;
872 #ifdef _GLIBCXX_HAVE_UNISTD_H
873 #ifdef __GLIBC__
874   if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
875     {
876       p.assign(cwd.get());
877       ec.clear();
878     }
879   else
880     ec.assign(errno, std::generic_category());
881 #else
882   long path_max = pathconf(".", _PC_PATH_MAX);
883   size_t size;
884   if (path_max == -1)
885       size = 1024;
886   else if (path_max > 10240)
887       size = 10240;
888   else
889       size = path_max;
890   for (char_ptr buf; p.empty(); size *= 2)
891     {
892       buf.reset((char*)malloc(size));
893       if (buf)
894 	{
895 	  if (getcwd(buf.get(), size))
896 	    {
897 	      p.assign(buf.get());
898 	      ec.clear();
899 	    }
900 	  else if (errno != ERANGE)
901 	    {
902 	      ec.assign(errno, std::generic_category());
903 	      return {};
904 	    }
905 	}
906       else
907 	{
908 	  ec = std::make_error_code(std::errc::not_enough_memory);
909 	  return {};
910 	}
911     }
912 #endif  // __GLIBC__
913 #else   // _GLIBCXX_HAVE_UNISTD_H
914   ec = std::make_error_code(std::errc::not_supported);
915 #endif
916   return p;
917 }
918 
919 void
current_path(const path & p)920 fs::current_path(const path& p)
921 {
922   error_code ec;
923   current_path(p, ec);
924   if (ec)
925     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
926 }
927 
928 void
current_path(const path & p,error_code & ec)929 fs::current_path(const path& p, error_code& ec) noexcept
930 {
931 #ifdef _GLIBCXX_HAVE_UNISTD_H
932   if (::chdir(p.c_str()))
933     ec.assign(errno, std::generic_category());
934   else
935     ec.clear();
936 #else
937   ec = std::make_error_code(std::errc::not_supported);
938 #endif
939 }
940 
941 bool
equivalent(const path & p1,const path & p2)942 fs::equivalent(const path& p1, const path& p2)
943 {
944   error_code ec;
945   auto result = equivalent(p1, p2, ec);
946   if (ec)
947     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
948 	  p1, p2, ec));
949   return result;
950 }
951 
952 bool
equivalent(const path & p1,const path & p2,error_code & ec)953 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
954 {
955 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
956   int err = 0;
957   file_status s1, s2;
958   stat_type st1, st2;
959   if (::stat(p1.c_str(), &st1) == 0)
960     s1 = make_file_status(st1);
961   else if (is_not_found_errno(errno))
962     s1.type(file_type::not_found);
963   else
964     err = errno;
965 
966   if (::stat(p2.c_str(), &st2) == 0)
967     s2 = make_file_status(st2);
968   else if (is_not_found_errno(errno))
969     s2.type(file_type::not_found);
970   else
971     err = errno;
972 
973   if (exists(s1) && exists(s2))
974     {
975       if (is_other(s1) && is_other(s2))
976 	{
977 	  ec = std::make_error_code(std::errc::not_supported);
978 	  return false;
979 	}
980       ec.clear();
981       if (is_other(s1) || is_other(s2))
982 	return false;
983       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
984     }
985   else if (!exists(s1) && !exists(s2))
986     ec = std::make_error_code(std::errc::no_such_file_or_directory);
987   else if (err)
988     ec.assign(err, std::generic_category());
989   else
990     ec.clear();
991   return false;
992 #else
993   ec = std::make_error_code(std::errc::not_supported);
994 #endif
995   return false;
996 }
997 
998 std::uintmax_t
file_size(const path & p)999 fs::file_size(const path& p)
1000 {
1001   error_code ec;
1002   auto sz = file_size(p, ec);
1003   if (ec)
1004     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
1005   return sz;
1006 }
1007 
1008 namespace
1009 {
1010   template<typename Accessor, typename T>
1011     inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)1012     do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
1013     {
1014 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1015       fs::stat_type st;
1016       if (::stat(p.c_str(), &st))
1017 	{
1018 	  ec.assign(errno, std::generic_category());
1019 	  return deflt;
1020 	}
1021       ec.clear();
1022       return f(st);
1023 #else
1024       ec = std::make_error_code(std::errc::not_supported);
1025       return deflt;
1026 #endif
1027     }
1028 }
1029 
1030 std::uintmax_t
file_size(const path & p,error_code & ec)1031 fs::file_size(const path& p, error_code& ec) noexcept
1032 {
1033   struct S
1034   {
1035     S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
1036     S() : type(file_type::not_found) { }
1037     file_type type;
1038     uintmax_t size;
1039   };
1040   auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
1041   if (s.type == file_type::regular)
1042     return s.size;
1043   if (!ec)
1044     {
1045       if (s.type == file_type::directory)
1046 	ec = std::make_error_code(std::errc::is_a_directory);
1047       else
1048 	ec = std::make_error_code(std::errc::not_supported);
1049     }
1050   return -1;
1051 }
1052 
1053 std::uintmax_t
hard_link_count(const path & p)1054 fs::hard_link_count(const path& p)
1055 {
1056   error_code ec;
1057   auto count = hard_link_count(p, ec);
1058   if (ec)
1059     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
1060   return count;
1061 }
1062 
1063 std::uintmax_t
hard_link_count(const path & p,error_code & ec)1064 fs::hard_link_count(const path& p, error_code& ec) noexcept
1065 {
1066   return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
1067 		 static_cast<uintmax_t>(-1));
1068 }
1069 
1070 bool
is_empty(const path & p)1071 fs::is_empty(const path& p)
1072 {
1073   error_code ec;
1074   bool e = is_empty(p, ec);
1075   if (ec)
1076     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
1077 					     p, ec));
1078   return e;
1079 }
1080 
1081 bool
is_empty(const path & p,error_code & ec)1082 fs::is_empty(const path& p, error_code& ec)
1083 {
1084   auto s = status(p, ec);
1085   if (ec)
1086     return false;
1087   bool empty = fs::is_directory(s)
1088     ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1089     : fs::file_size(p, ec) == 0;
1090   return ec ? false : empty;
1091 }
1092 
1093 fs::file_time_type
last_write_time(const path & p)1094 fs::last_write_time(const path& p)
1095 {
1096   error_code ec;
1097   auto t = last_write_time(p, ec);
1098   if (ec)
1099     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1100   return t;
1101 }
1102 
1103 fs::file_time_type
last_write_time(const path & p,error_code & ec)1104 fs::last_write_time(const path& p, error_code& ec) noexcept
1105 {
1106   return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
1107 		 file_time_type::min());
1108 }
1109 
1110 void
last_write_time(const path & p,file_time_type new_time)1111 fs::last_write_time(const path& p, file_time_type new_time)
1112 {
1113   error_code ec;
1114   last_write_time(p, new_time, ec);
1115   if (ec)
1116     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1117 }
1118 
1119 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)1120 fs::last_write_time(const path& p __attribute__((__unused__)),
1121 		    file_time_type new_time, error_code& ec) noexcept
1122 {
1123   auto d = new_time.time_since_epoch();
1124   auto s = chrono::duration_cast<chrono::seconds>(d);
1125 #if _GLIBCXX_USE_UTIMENSAT
1126   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1127   if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1128     {
1129       --s;
1130       ns += chrono::seconds(1);
1131     }
1132   struct ::timespec ts[2];
1133   ts[0].tv_sec = 0;
1134   ts[0].tv_nsec = UTIME_OMIT;
1135   ts[1].tv_sec = static_cast<std::time_t>(s.count());
1136   ts[1].tv_nsec = static_cast<long>(ns.count());
1137   if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1138     ec.assign(errno, std::generic_category());
1139   else
1140     ec.clear();
1141 #elif _GLIBCXX_HAVE_UTIME_H
1142   ::utimbuf times;
1143   times.modtime = s.count();
1144   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1145 			 times.modtime);
1146   if (::utime(p.c_str(), &times))
1147     ec.assign(errno, std::generic_category());
1148   else
1149     ec.clear();
1150 #else
1151   ec = std::make_error_code(std::errc::not_supported);
1152 #endif
1153 }
1154 
1155 void
permissions(const path & p,perms prms,perm_options opts)1156 fs::permissions(const path& p, perms prms, perm_options opts)
1157 {
1158   error_code ec;
1159   permissions(p, prms, opts, ec);
1160   if (ec)
1161     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1162 }
1163 
1164 void
permissions(const path & p,perms prms,perm_options opts,error_code & ec)1165 fs::permissions(const path& p, perms prms, perm_options opts,
1166 		error_code& ec) noexcept
1167 {
1168   const bool replace = is_set(opts, perm_options::replace);
1169   const bool add = is_set(opts, perm_options::add);
1170   const bool remove = is_set(opts, perm_options::remove);
1171   const bool nofollow = is_set(opts, perm_options::nofollow);
1172   if (((int)replace + (int)add + (int)remove) != 1)
1173     {
1174       ec = std::make_error_code(std::errc::invalid_argument);
1175       return;
1176     }
1177 
1178   prms &= perms::mask;
1179 
1180   file_status st;
1181   if (add || remove || nofollow)
1182     {
1183       st = nofollow ? symlink_status(p, ec) : status(p, ec);
1184       if (ec)
1185 	return;
1186       auto curr = st.permissions();
1187       if (add)
1188 	prms |= curr;
1189       else if (remove)
1190 	prms = curr & ~prms;
1191     }
1192 
1193   int err = 0;
1194 #if _GLIBCXX_USE_FCHMODAT
1195   const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1196   if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1197     err = errno;
1198 #else
1199   if (nofollow && is_symlink(st))
1200     ec = std::make_error_code(std::errc::operation_not_supported);
1201   else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
1202     err = errno;
1203 #endif
1204 
1205   if (err)
1206     ec.assign(err, std::generic_category());
1207   else
1208     ec.clear();
1209 }
1210 
1211 fs::path
proximate(const path & p,const path & base)1212 fs::proximate(const path& p, const path& base)
1213 {
1214   return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1215 }
1216 
1217 fs::path
proximate(const path & p,const path & base,error_code & ec)1218 fs::proximate(const path& p, const path& base, error_code& ec)
1219 {
1220   path result;
1221   const auto p2 = weakly_canonical(p, ec);
1222   if (!ec)
1223     {
1224       const auto base2 = weakly_canonical(base, ec);
1225       if (!ec)
1226 	result = p2.lexically_proximate(base2);
1227     }
1228   return result;
1229 }
1230 
1231 fs::path
read_symlink(const path & p)1232 fs::read_symlink(const path& p)
1233 {
1234   error_code ec;
1235   path tgt = read_symlink(p, ec);
1236   if (ec)
1237     _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1238   return tgt;
1239 }
1240 
read_symlink(const path & p,error_code & ec)1241 fs::path fs::read_symlink(const path& p, error_code& ec)
1242 {
1243   path result;
1244 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1245   stat_type st;
1246   if (::lstat(p.c_str(), &st))
1247     {
1248       ec.assign(errno, std::generic_category());
1249       return result;
1250     }
1251   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1252   do
1253     {
1254       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1255       if (len == -1)
1256 	{
1257 	  ec.assign(errno, std::generic_category());
1258 	  return result;
1259 	}
1260       else if (len == (ssize_t)buf.size())
1261 	{
1262 	  if (buf.size() > 4096)
1263 	    {
1264 	      ec.assign(ENAMETOOLONG, std::generic_category());
1265 	      return result;
1266 	    }
1267 	  buf.resize(buf.size() * 2);
1268 	}
1269       else
1270 	{
1271 	  buf.resize(len);
1272 	  result.assign(buf);
1273 	  ec.clear();
1274 	  break;
1275 	}
1276     }
1277   while (true);
1278 #else
1279   ec = std::make_error_code(std::errc::not_supported);
1280 #endif
1281   return result;
1282 }
1283 
1284 fs::path
relative(const path & p,const path & base)1285 fs::relative(const path& p, const path& base)
1286 {
1287   return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1288 }
1289 
1290 fs::path
relative(const path & p,const path & base,error_code & ec)1291 fs::relative(const path& p, const path& base, error_code& ec)
1292 {
1293   auto result = weakly_canonical(p, ec);
1294   fs::path cbase;
1295   if (!ec)
1296     cbase = weakly_canonical(base, ec);
1297   if (!ec)
1298     result = result.lexically_relative(cbase);
1299   if (ec)
1300     result.clear();
1301   return result;
1302 }
1303 
1304 bool
remove(const path & p)1305 fs::remove(const path& p)
1306 {
1307   error_code ec;
1308   const bool result = fs::remove(p, ec);
1309   if (ec)
1310     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1311   return result;
1312 }
1313 
1314 bool
remove(const path & p,error_code & ec)1315 fs::remove(const path& p, error_code& ec) noexcept
1316 {
1317   if (::remove(p.c_str()) == 0)
1318     {
1319       ec.clear();
1320       return true;
1321     }
1322   else if (errno == ENOENT)
1323     ec.clear();
1324   else
1325     ec.assign(errno, std::generic_category());
1326   return false;
1327 }
1328 
1329 
1330 std::uintmax_t
remove_all(const path & p)1331 fs::remove_all(const path& p)
1332 {
1333   error_code ec;
1334   const auto result = remove_all(p, ec);
1335   if (ec)
1336     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1337   return result;
1338 }
1339 
1340 std::uintmax_t
remove_all(const path & p,error_code & ec)1341 fs::remove_all(const path& p, error_code& ec)
1342 {
1343   const auto s = symlink_status(p, ec);
1344   if (!status_known(s))
1345     return -1;
1346 
1347   ec.clear();
1348   if (s.type() == file_type::not_found)
1349     return 0;
1350 
1351   uintmax_t count = 0;
1352   if (s.type() == file_type::directory)
1353     {
1354       directory_iterator d(p, ec), end;
1355       while (!ec && d != end)
1356 	{
1357 	  const auto removed = fs::remove_all(d->path(), ec);
1358 	  if (removed == numeric_limits<uintmax_t>::max())
1359 	    return -1;
1360 	  count += removed;
1361 	  d.increment(ec);
1362 	  if (ec)
1363 	    return -1;
1364 	}
1365     }
1366 
1367   if (fs::remove(p, ec))
1368     ++count;
1369   return ec ? -1 : count;
1370 }
1371 
1372 void
rename(const path & from,const path & to)1373 fs::rename(const path& from, const path& to)
1374 {
1375   error_code ec;
1376   rename(from, to, ec);
1377   if (ec)
1378     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1379 }
1380 
1381 void
rename(const path & from,const path & to,error_code & ec)1382 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1383 {
1384   if (::rename(from.c_str(), to.c_str()))
1385     ec.assign(errno, std::generic_category());
1386   else
1387     ec.clear();
1388 }
1389 
1390 void
resize_file(const path & p,uintmax_t size)1391 fs::resize_file(const path& p, uintmax_t size)
1392 {
1393   error_code ec;
1394   resize_file(p, size, ec);
1395   if (ec)
1396     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1397 }
1398 
1399 void
resize_file(const path & p,uintmax_t size,error_code & ec)1400 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1401 {
1402 #ifdef _GLIBCXX_HAVE_UNISTD_H
1403   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1404     ec.assign(EINVAL, std::generic_category());
1405   else if (::truncate(p.c_str(), size))
1406     ec.assign(errno, std::generic_category());
1407   else
1408     ec.clear();
1409 #else
1410   ec = std::make_error_code(std::errc::not_supported);
1411 #endif
1412 }
1413 
1414 
1415 fs::space_info
space(const path & p)1416 fs::space(const path& p)
1417 {
1418   error_code ec;
1419   space_info s = space(p, ec);
1420   if (ec)
1421     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1422   return s;
1423 }
1424 
1425 fs::space_info
space(const path & p,error_code & ec)1426 fs::space(const path& p, error_code& ec) noexcept
1427 {
1428   space_info info = {
1429     static_cast<uintmax_t>(-1),
1430     static_cast<uintmax_t>(-1),
1431     static_cast<uintmax_t>(-1)
1432   };
1433 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1434   struct ::statvfs f;
1435   if (::statvfs(p.c_str(), &f))
1436       ec.assign(errno, std::generic_category());
1437   else
1438     {
1439       uintmax_t fragment_size = f.f_frsize;
1440       info = space_info{
1441 	f.f_blocks * fragment_size,
1442 	f.f_bfree * fragment_size,
1443 	f.f_bavail * fragment_size
1444       };
1445       ec.clear();
1446     }
1447 #else
1448   ec = std::make_error_code(std::errc::not_supported);
1449 #endif
1450   return info;
1451 }
1452 
1453 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1454 fs::file_status
status(const fs::path & p,error_code & ec)1455 fs::status(const fs::path& p, error_code& ec) noexcept
1456 {
1457   file_status status;
1458   stat_type st;
1459   if (::stat(p.c_str(), &st))
1460     {
1461       int err = errno;
1462       ec.assign(err, std::generic_category());
1463       if (is_not_found_errno(err))
1464 	status.type(file_type::not_found);
1465 #ifdef EOVERFLOW
1466       else if (err == EOVERFLOW)
1467 	status.type(file_type::unknown);
1468 #endif
1469     }
1470   else
1471     {
1472       status = make_file_status(st);
1473       ec.clear();
1474     }
1475   return status;
1476 }
1477 
1478 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1479 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1480 {
1481   file_status status;
1482   stat_type st;
1483   if (::lstat(p.c_str(), &st))
1484     {
1485       int err = errno;
1486       ec.assign(err, std::generic_category());
1487       if (is_not_found_errno(err))
1488 	status.type(file_type::not_found);
1489     }
1490   else
1491     {
1492       status = make_file_status(st);
1493       ec.clear();
1494     }
1495   return status;
1496 }
1497 #endif
1498 
1499 fs::file_status
status(const fs::path & p)1500 fs::status(const fs::path& p)
1501 {
1502   std::error_code ec;
1503   auto result = status(p, ec);
1504   if (result.type() == file_type::none)
1505     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1506   return result;
1507 }
1508 
1509 fs::file_status
symlink_status(const fs::path & p)1510 fs::symlink_status(const fs::path& p)
1511 {
1512   std::error_code ec;
1513   auto result = symlink_status(p, ec);
1514   if (result.type() == file_type::none)
1515     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1516   return result;
1517 }
1518 
temp_directory_path()1519 fs::path fs::temp_directory_path()
1520 {
1521   error_code ec;
1522   path tmp = temp_directory_path(ec);
1523   if (ec)
1524     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1525   return tmp;
1526 }
1527 
temp_directory_path(error_code & ec)1528 fs::path fs::temp_directory_path(error_code& ec)
1529 {
1530 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1531   ec = std::make_error_code(std::errc::not_supported);
1532   return {}; // TODO
1533 #else
1534   const char* tmpdir = nullptr;
1535   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1536   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1537     tmpdir = ::getenv(*e);
1538   path p = tmpdir ? tmpdir : "/tmp";
1539   auto st = status(p, ec);
1540   if (!ec)
1541     {
1542       if (is_directory(st))
1543 	{
1544 	  ec.clear();
1545 	  return p;
1546 	}
1547       else
1548 	ec = std::make_error_code(std::errc::not_a_directory);
1549     }
1550   return {};
1551 #endif
1552 }
1553 
1554 fs::path
weakly_canonical(const path & p)1555 fs::weakly_canonical(const path& p)
1556 {
1557   path result;
1558   if (exists(status(p)))
1559     return canonical(p);
1560 
1561   path tmp;
1562   auto iter = p.begin(), end = p.end();
1563   // find leading elements of p that exist:
1564   while (iter != end)
1565     {
1566       tmp = result / *iter;
1567       if (exists(status(tmp)))
1568 	swap(result, tmp);
1569       else
1570 	break;
1571       ++iter;
1572     }
1573   // canonicalize:
1574   if (!result.empty())
1575     result = canonical(result);
1576   // append the non-existing elements:
1577   while (iter != end)
1578     result /= *iter++;
1579   // normalize:
1580   return result.lexically_normal();
1581 }
1582 
1583 fs::path
weakly_canonical(const path & p,error_code & ec)1584 fs::weakly_canonical(const path& p, error_code& ec)
1585 {
1586   path result;
1587   file_status st = status(p, ec);
1588   if (exists(st))
1589     return canonical(p, ec);
1590   else if (status_known(st))
1591     ec.clear();
1592   else
1593     return result;
1594 
1595   path tmp;
1596   auto iter = p.begin(), end = p.end();
1597   // find leading elements of p that exist:
1598   while (iter != end)
1599     {
1600       tmp = result / *iter;
1601       st = status(tmp, ec);
1602       if (exists(st))
1603 	swap(result, tmp);
1604       else
1605 	{
1606 	  if (status_known(st))
1607 	    ec.clear();
1608 	  break;
1609 	}
1610       ++iter;
1611     }
1612   // canonicalize:
1613   if (!ec && !result.empty())
1614     result = canonical(result, ec);
1615   if (ec)
1616     result.clear();
1617   else
1618     {
1619       // append the non-existing elements:
1620       while (iter != end)
1621 	result /= *iter++;
1622       // normalize:
1623       result = result.lexically_normal();
1624     }
1625   return result;
1626 }
1627