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