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 ()__anonb5e7b4590111::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   else if (!fs::is_symlink(make_file_status(st)))
1252     {
1253       ec.assign(EINVAL, std::generic_category());
1254       return result;
1255     }
1256 
1257   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1258   do
1259     {
1260       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1261       if (len == -1)
1262 	{
1263 	  ec.assign(errno, std::generic_category());
1264 	  return result;
1265 	}
1266       else if (len == (ssize_t)buf.size())
1267 	{
1268 	  if (buf.size() > 4096)
1269 	    {
1270 	      ec.assign(ENAMETOOLONG, std::generic_category());
1271 	      return result;
1272 	    }
1273 	  buf.resize(buf.size() * 2);
1274 	}
1275       else
1276 	{
1277 	  buf.resize(len);
1278 	  result.assign(buf);
1279 	  ec.clear();
1280 	  break;
1281 	}
1282     }
1283   while (true);
1284 #else
1285   ec = std::make_error_code(std::errc::not_supported);
1286 #endif
1287   return result;
1288 }
1289 
1290 fs::path
relative(const path & p,const path & base)1291 fs::relative(const path& p, const path& base)
1292 {
1293   return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1294 }
1295 
1296 fs::path
relative(const path & p,const path & base,error_code & ec)1297 fs::relative(const path& p, const path& base, error_code& ec)
1298 {
1299   auto result = weakly_canonical(p, ec);
1300   fs::path cbase;
1301   if (!ec)
1302     cbase = weakly_canonical(base, ec);
1303   if (!ec)
1304     result = result.lexically_relative(cbase);
1305   if (ec)
1306     result.clear();
1307   return result;
1308 }
1309 
1310 bool
remove(const path & p)1311 fs::remove(const path& p)
1312 {
1313   error_code ec;
1314   const bool result = fs::remove(p, ec);
1315   if (ec)
1316     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1317   return result;
1318 }
1319 
1320 bool
remove(const path & p,error_code & ec)1321 fs::remove(const path& p, error_code& ec) noexcept
1322 {
1323   if (::remove(p.c_str()) == 0)
1324     {
1325       ec.clear();
1326       return true;
1327     }
1328   else if (errno == ENOENT)
1329     ec.clear();
1330   else
1331     ec.assign(errno, std::generic_category());
1332   return false;
1333 }
1334 
1335 
1336 std::uintmax_t
remove_all(const path & p)1337 fs::remove_all(const path& p)
1338 {
1339   error_code ec;
1340   const auto result = remove_all(p, ec);
1341   if (ec)
1342     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1343   return result;
1344 }
1345 
1346 std::uintmax_t
remove_all(const path & p,error_code & ec)1347 fs::remove_all(const path& p, error_code& ec)
1348 {
1349   const auto s = symlink_status(p, ec);
1350   if (!status_known(s))
1351     return -1;
1352 
1353   ec.clear();
1354   if (s.type() == file_type::not_found)
1355     return 0;
1356 
1357   uintmax_t count = 0;
1358   if (s.type() == file_type::directory)
1359     {
1360       directory_iterator d(p, ec), end;
1361       while (!ec && d != end)
1362 	{
1363 	  const auto removed = fs::remove_all(d->path(), ec);
1364 	  if (removed == numeric_limits<uintmax_t>::max())
1365 	    return -1;
1366 	  count += removed;
1367 	  d.increment(ec);
1368 	  if (ec)
1369 	    return -1;
1370 	}
1371     }
1372 
1373   if (fs::remove(p, ec))
1374     ++count;
1375   return ec ? -1 : count;
1376 }
1377 
1378 void
rename(const path & from,const path & to)1379 fs::rename(const path& from, const path& to)
1380 {
1381   error_code ec;
1382   rename(from, to, ec);
1383   if (ec)
1384     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1385 }
1386 
1387 void
rename(const path & from,const path & to,error_code & ec)1388 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1389 {
1390   if (::rename(from.c_str(), to.c_str()))
1391     ec.assign(errno, std::generic_category());
1392   else
1393     ec.clear();
1394 }
1395 
1396 void
resize_file(const path & p,uintmax_t size)1397 fs::resize_file(const path& p, uintmax_t size)
1398 {
1399   error_code ec;
1400   resize_file(p, size, ec);
1401   if (ec)
1402     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1403 }
1404 
1405 void
resize_file(const path & p,uintmax_t size,error_code & ec)1406 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1407 {
1408 #ifdef _GLIBCXX_HAVE_UNISTD_H
1409   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1410     ec.assign(EINVAL, std::generic_category());
1411   else if (::truncate(p.c_str(), size))
1412     ec.assign(errno, std::generic_category());
1413   else
1414     ec.clear();
1415 #else
1416   ec = std::make_error_code(std::errc::not_supported);
1417 #endif
1418 }
1419 
1420 
1421 fs::space_info
space(const path & p)1422 fs::space(const path& p)
1423 {
1424   error_code ec;
1425   space_info s = space(p, ec);
1426   if (ec)
1427     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1428   return s;
1429 }
1430 
1431 fs::space_info
space(const path & p,error_code & ec)1432 fs::space(const path& p, error_code& ec) noexcept
1433 {
1434   space_info info = {
1435     static_cast<uintmax_t>(-1),
1436     static_cast<uintmax_t>(-1),
1437     static_cast<uintmax_t>(-1)
1438   };
1439 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1440   struct ::statvfs f;
1441   if (::statvfs(p.c_str(), &f))
1442       ec.assign(errno, std::generic_category());
1443   else
1444     {
1445       uintmax_t fragment_size = f.f_frsize;
1446       info = space_info{
1447 	f.f_blocks * fragment_size,
1448 	f.f_bfree * fragment_size,
1449 	f.f_bavail * fragment_size
1450       };
1451       ec.clear();
1452     }
1453 #else
1454   ec = std::make_error_code(std::errc::not_supported);
1455 #endif
1456   return info;
1457 }
1458 
1459 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1460 fs::file_status
status(const fs::path & p,error_code & ec)1461 fs::status(const fs::path& p, error_code& ec) noexcept
1462 {
1463   file_status status;
1464   stat_type st;
1465   if (::stat(p.c_str(), &st))
1466     {
1467       int err = errno;
1468       ec.assign(err, std::generic_category());
1469       if (is_not_found_errno(err))
1470 	status.type(file_type::not_found);
1471 #ifdef EOVERFLOW
1472       else if (err == EOVERFLOW)
1473 	status.type(file_type::unknown);
1474 #endif
1475     }
1476   else
1477     {
1478       status = make_file_status(st);
1479       ec.clear();
1480     }
1481   return status;
1482 }
1483 
1484 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1485 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1486 {
1487   file_status status;
1488   stat_type st;
1489   if (::lstat(p.c_str(), &st))
1490     {
1491       int err = errno;
1492       ec.assign(err, std::generic_category());
1493       if (is_not_found_errno(err))
1494 	status.type(file_type::not_found);
1495     }
1496   else
1497     {
1498       status = make_file_status(st);
1499       ec.clear();
1500     }
1501   return status;
1502 }
1503 #endif
1504 
1505 fs::file_status
status(const fs::path & p)1506 fs::status(const fs::path& p)
1507 {
1508   std::error_code ec;
1509   auto result = status(p, ec);
1510   if (result.type() == file_type::none)
1511     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1512   return result;
1513 }
1514 
1515 fs::file_status
symlink_status(const fs::path & p)1516 fs::symlink_status(const fs::path& p)
1517 {
1518   std::error_code ec;
1519   auto result = symlink_status(p, ec);
1520   if (result.type() == file_type::none)
1521     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1522   return result;
1523 }
1524 
temp_directory_path()1525 fs::path fs::temp_directory_path()
1526 {
1527   error_code ec;
1528   path tmp = temp_directory_path(ec);
1529   if (ec)
1530     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1531   return tmp;
1532 }
1533 
temp_directory_path(error_code & ec)1534 fs::path fs::temp_directory_path(error_code& ec)
1535 {
1536 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1537   ec = std::make_error_code(std::errc::not_supported);
1538   return {}; // TODO
1539 #else
1540   const char* tmpdir = nullptr;
1541   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1542   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1543     tmpdir = ::getenv(*e);
1544   path p = tmpdir ? tmpdir : "/tmp";
1545   auto st = status(p, ec);
1546   if (!ec)
1547     {
1548       if (is_directory(st))
1549 	{
1550 	  ec.clear();
1551 	  return p;
1552 	}
1553       else
1554 	ec = std::make_error_code(std::errc::not_a_directory);
1555     }
1556   return {};
1557 #endif
1558 }
1559 
1560 fs::path
weakly_canonical(const path & p)1561 fs::weakly_canonical(const path& p)
1562 {
1563   path result;
1564   if (exists(status(p)))
1565     return canonical(p);
1566 
1567   path tmp;
1568   auto iter = p.begin(), end = p.end();
1569   // find leading elements of p that exist:
1570   while (iter != end)
1571     {
1572       tmp = result / *iter;
1573       if (exists(status(tmp)))
1574 	swap(result, tmp);
1575       else
1576 	break;
1577       ++iter;
1578     }
1579   // canonicalize:
1580   if (!result.empty())
1581     result = canonical(result);
1582   // append the non-existing elements:
1583   while (iter != end)
1584     result /= *iter++;
1585   // normalize:
1586   return result.lexically_normal();
1587 }
1588 
1589 fs::path
weakly_canonical(const path & p,error_code & ec)1590 fs::weakly_canonical(const path& p, error_code& ec)
1591 {
1592   path result;
1593   file_status st = status(p, ec);
1594   if (exists(st))
1595     return canonical(p, ec);
1596   else if (status_known(st))
1597     ec.clear();
1598   else
1599     return result;
1600 
1601   path tmp;
1602   auto iter = p.begin(), end = p.end();
1603   // find leading elements of p that exist:
1604   while (iter != end)
1605     {
1606       tmp = result / *iter;
1607       st = status(tmp, ec);
1608       if (exists(st))
1609 	swap(result, tmp);
1610       else
1611 	{
1612 	  if (status_known(st))
1613 	    ec.clear();
1614 	  break;
1615 	}
1616       ++iter;
1617     }
1618   // canonicalize:
1619   if (!ec && !result.empty())
1620     result = canonical(result, ec);
1621   if (ec)
1622     result.clear();
1623   else
1624     {
1625       // append the non-existing elements:
1626       while (iter != end)
1627 	result /= *iter++;
1628       // normalize:
1629       result = result.lexically_normal();
1630     }
1631   return result;
1632 }
1633