xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/src/filesystem/ops.cc (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1 // Filesystem TS operations -*- C++ -*-
2 
3 // Copyright (C) 2014-2020 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 ()__anonf636ab8c0111::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, ec))
348 	{
349 	  copy(x.path(), to/x.path().filename(), options, ec);
350 	  if (ec)
351 	    return;
352 	}
353     }
354   // _GLIBCXX_RESOLVE_LIB_DEFECTS
355   // 2683. filesystem::copy() says "no effects"
356   else
357     ec.clear();
358 }
359 
360 bool
copy_file(const path & from,const path & to,copy_options option)361 fs::copy_file(const path& from, const path& to, copy_options option)
362 {
363   error_code ec;
364   bool result = copy_file(from, to, option, ec);
365   if (ec.value())
366     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
367 	  ec));
368   return result;
369 }
370 
371 bool
copy_file(const path & from,const path & to,copy_options options,error_code & ec)372 fs::copy_file(const path& from, const path& to, copy_options options,
373 	      error_code& ec)
374 {
375 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
376   return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
377 		      nullptr, nullptr, ec);
378 #else
379   ec = std::make_error_code(std::errc::not_supported);
380   return false;
381 #endif
382 }
383 
384 
385 void
copy_symlink(const path & existing_symlink,const path & new_symlink)386 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
387 {
388   error_code ec;
389   copy_symlink(existing_symlink, new_symlink, ec);
390   if (ec.value())
391     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
392 	  existing_symlink, new_symlink, ec));
393 }
394 
395 void
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)396 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
397 		 error_code& ec) noexcept
398 {
399   auto p = read_symlink(existing_symlink, ec);
400   if (ec.value())
401     return;
402 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
403   if (is_directory(p))
404     {
405       create_directory_symlink(p, new_symlink, ec);
406       return;
407     }
408 #endif
409   create_symlink(p, new_symlink, ec);
410 }
411 
412 
413 bool
create_directories(const path & p)414 fs::create_directories(const path& p)
415 {
416   error_code ec;
417   bool result = create_directories(p, ec);
418   if (ec.value())
419     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
420 	  ec));
421   return result;
422 }
423 
424 bool
create_directories(const path & p,error_code & ec)425 fs::create_directories(const path& p, error_code& ec)
426 {
427   if (p.empty())
428     {
429       ec = std::make_error_code(errc::invalid_argument);
430       return false;
431     }
432 
433   file_status st = status(p, ec);
434   if (is_directory(st))
435     return false;
436   else if (ec && !status_known(st))
437     return false;
438   else if (exists(st))
439     {
440       if (!ec)
441 	ec = std::make_error_code(std::errc::not_a_directory);
442       return false;
443     }
444 
445   std::stack<path> missing;
446   path pp = p;
447 
448   while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
449     {
450       ec.clear();
451       const auto& filename = pp.filename();
452       if (!is_dot(filename) && !is_dotdot(filename))
453 	{
454 	  missing.push(std::move(pp));
455 	  pp = missing.top().parent_path();
456 	}
457       else
458 	pp = pp.parent_path();
459     }
460 
461   if (ec || missing.empty())
462     return false;
463 
464   bool created;
465   do
466     {
467       const path& top = missing.top();
468       created = create_directory(top, ec);
469       if (ec)
470 	return false;
471       missing.pop();
472     }
473   while (!missing.empty());
474 
475   return created;
476 }
477 
478 namespace
479 {
480   bool
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)481   create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
482   {
483     bool created = false;
484 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
485     posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
486     if (posix::mkdir(p.c_str(), mode))
487       {
488 	const int err = errno;
489 	if (err != EEXIST || !is_directory(p, ec))
490 	  ec.assign(err, std::generic_category());
491       }
492     else
493       {
494 	ec.clear();
495 	created = true;
496       }
497 #else
498     ec = std::make_error_code(std::errc::not_supported);
499 #endif
500     return created;
501   }
502 } // namespace
503 
504 bool
create_directory(const path & p)505 fs::create_directory(const path& p)
506 {
507   error_code ec;
508   bool result = create_directory(p, ec);
509   if (ec.value())
510     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
511 	  ec));
512   return result;
513 }
514 
515 bool
create_directory(const path & p,error_code & ec)516 fs::create_directory(const path& p, error_code& ec) noexcept
517 {
518   return create_dir(p, perms::all, ec);
519 }
520 
521 
522 bool
create_directory(const path & p,const path & attributes)523 fs::create_directory(const path& p, const path& attributes)
524 {
525   error_code ec;
526   bool result = create_directory(p, attributes, ec);
527   if (ec.value())
528     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
529 	  ec));
530   return result;
531 }
532 
533 bool
create_directory(const path & p,const path & attributes,error_code & ec)534 fs::create_directory(const path& p, const path& attributes,
535 		     error_code& ec) noexcept
536 {
537 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
538   stat_type st;
539   if (posix::stat(attributes.c_str(), &st))
540     {
541       ec.assign(errno, std::generic_category());
542       return false;
543     }
544   return create_dir(p, static_cast<perms>(st.st_mode), ec);
545 #else
546   ec = std::make_error_code(std::errc::not_supported);
547   return false;
548 #endif
549 }
550 
551 
552 void
create_directory_symlink(const path & to,const path & new_symlink)553 fs::create_directory_symlink(const path& to, const path& new_symlink)
554 {
555   error_code ec;
556   create_directory_symlink(to, new_symlink, ec);
557   if (ec.value())
558     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
559 	  to, new_symlink, ec));
560 }
561 
562 void
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)563 fs::create_directory_symlink(const path& to, const path& new_symlink,
564 			     error_code& ec) noexcept
565 {
566 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
567   ec = std::make_error_code(std::errc::not_supported);
568 #else
569   create_symlink(to, new_symlink, ec);
570 #endif
571 }
572 
573 
574 void
create_hard_link(const path & to,const path & new_hard_link)575 fs::create_hard_link(const path& to, const path& new_hard_link)
576 {
577   error_code ec;
578   create_hard_link(to, new_hard_link, ec);
579   if (ec.value())
580     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
581 	  to, new_hard_link, ec));
582 }
583 
584 void
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)585 fs::create_hard_link(const path& to, const path& new_hard_link,
586 		     error_code& ec) noexcept
587 {
588 #ifdef _GLIBCXX_HAVE_LINK
589   if (::link(to.c_str(), new_hard_link.c_str()))
590     ec.assign(errno, std::generic_category());
591   else
592     ec.clear();
593 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
594   if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
595     ec.clear();
596   else
597     ec.assign((int)GetLastError(), system_category());
598 #else
599   ec = std::make_error_code(std::errc::not_supported);
600 #endif
601 }
602 
603 void
create_symlink(const path & to,const path & new_symlink)604 fs::create_symlink(const path& to, const path& new_symlink)
605 {
606   error_code ec;
607   create_symlink(to, new_symlink, ec);
608   if (ec.value())
609     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
610 	  to, new_symlink, ec));
611 }
612 
613 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)614 fs::create_symlink(const path& to, const path& new_symlink,
615 		   error_code& ec) noexcept
616 {
617 #ifdef _GLIBCXX_HAVE_SYMLINK
618   if (::symlink(to.c_str(), new_symlink.c_str()))
619     ec.assign(errno, std::generic_category());
620   else
621     ec.clear();
622 #else
623   ec = std::make_error_code(std::errc::not_supported);
624 #endif
625 }
626 
627 fs::path
current_path()628 fs::current_path()
629 {
630   error_code ec;
631   path p = current_path(ec);
632   if (ec.value())
633     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
634   return p;
635 }
636 
637 fs::path
current_path(error_code & ec)638 fs::current_path(error_code& ec)
639 {
640   path p;
641 #ifdef _GLIBCXX_HAVE_UNISTD_H
642 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
643   if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
644     {
645       p.assign(cwd.get());
646       ec.clear();
647     }
648   else
649     ec.assign(errno, std::generic_category());
650 #else
651 #ifdef _PC_PATH_MAX
652   long path_max = pathconf(".", _PC_PATH_MAX);
653   size_t size;
654   if (path_max == -1)
655       size = 1024;
656   else if (path_max > 10240)
657       size = 10240;
658   else
659       size = path_max;
660 #elif defined(PATH_MAX)
661   size_t size = PATH_MAX;
662 #else
663   size_t size = 1024;
664 #endif
665   for (char_ptr buf; p.empty(); size *= 2)
666     {
667       using char_type = fs::path::value_type;
668       buf.reset((char_type*)malloc(size * sizeof(char_type)));
669       if (buf)
670 	{
671 	  if (getcwd(buf.get(), size))
672 	    {
673 	      p.assign(buf.get());
674 	      ec.clear();
675 	    }
676 	  else if (errno != ERANGE)
677 	    {
678 	      ec.assign(errno, std::generic_category());
679 	      return {};
680 	    }
681 	}
682       else
683 	{
684 	  ec = std::make_error_code(std::errc::not_enough_memory);
685 	  return {};
686 	}
687     }
688 #endif  // __GLIBC__
689 #else   // _GLIBCXX_HAVE_UNISTD_H
690   ec = std::make_error_code(std::errc::not_supported);
691 #endif
692   return p;
693 }
694 
695 void
current_path(const path & p)696 fs::current_path(const path& p)
697 {
698   error_code ec;
699   current_path(p, ec);
700   if (ec.value())
701     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
702 }
703 
704 void
current_path(const path & p,error_code & ec)705 fs::current_path(const path& p, error_code& ec) noexcept
706 {
707 #ifdef _GLIBCXX_HAVE_UNISTD_H
708   if (posix::chdir(p.c_str()))
709     ec.assign(errno, std::generic_category());
710   else
711     ec.clear();
712 #else
713   ec = std::make_error_code(std::errc::not_supported);
714 #endif
715 }
716 
717 bool
equivalent(const path & p1,const path & p2)718 fs::equivalent(const path& p1, const path& p2)
719 {
720   error_code ec;
721   auto result = equivalent(p1, p2, ec);
722   if (ec)
723     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
724 	  p1, p2, ec));
725   return result;
726 }
727 
728 bool
equivalent(const path & p1,const path & p2,error_code & ec)729 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
730 {
731 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
732   int err = 0;
733   file_status s1, s2;
734   stat_type st1, st2;
735   if (posix::stat(p1.c_str(), &st1) == 0)
736     s1 = make_file_status(st1);
737   else if (is_not_found_errno(errno))
738     s1.type(file_type::not_found);
739   else
740     err = errno;
741 
742   if (posix::stat(p2.c_str(), &st2) == 0)
743     s2 = make_file_status(st2);
744   else if (is_not_found_errno(errno))
745     s2.type(file_type::not_found);
746   else
747     err = errno;
748 
749   if (exists(s1) && exists(s2))
750     {
751       if (is_other(s1) && is_other(s2))
752 	{
753 	  ec = std::make_error_code(std::errc::not_supported);
754 	  return false;
755 	}
756       ec.clear();
757       if (is_other(s1) || is_other(s2))
758 	return false;
759       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
760     }
761   else if (!exists(s1) && !exists(s2))
762     ec = std::make_error_code(std::errc::no_such_file_or_directory);
763   else if (err)
764     ec.assign(err, std::generic_category());
765   else
766     ec.clear();
767   return false;
768 #else
769   ec = std::make_error_code(std::errc::not_supported);
770 #endif
771   return false;
772 }
773 
774 std::uintmax_t
file_size(const path & p)775 fs::file_size(const path& p)
776 {
777   error_code ec;
778   auto sz = file_size(p, ec);
779   if (ec.value())
780     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
781   return sz;
782 }
783 
784 namespace
785 {
786   template<typename Accessor, typename T>
787     inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)788     do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
789     {
790 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
791       stat_type st;
792       if (posix::stat(p.c_str(), &st))
793 	{
794 	  ec.assign(errno, std::generic_category());
795 	  return deflt;
796 	}
797       ec.clear();
798       return f(st);
799 #else
800       ec = std::make_error_code(std::errc::not_supported);
801       return deflt;
802 #endif
803     }
804 }
805 
806 std::uintmax_t
file_size(const path & p,error_code & ec)807 fs::file_size(const path& p, error_code& ec) noexcept
808 {
809   struct S
810   {
811     S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
812     S() : type(file_type::not_found) { }
813     file_type type;
814     uintmax_t size;
815   };
816   auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
817   if (s.type == file_type::regular)
818     return s.size;
819   if (!ec)
820     {
821       if (s.type == file_type::directory)
822 	ec = std::make_error_code(std::errc::is_a_directory);
823       else
824 	ec = std::make_error_code(std::errc::not_supported);
825     }
826   return -1;
827 }
828 
829 std::uintmax_t
hard_link_count(const path & p)830 fs::hard_link_count(const path& p)
831 {
832   error_code ec;
833   auto count = hard_link_count(p, ec);
834   if (ec.value())
835     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
836   return count;
837 }
838 
839 std::uintmax_t
hard_link_count(const path & p,error_code & ec)840 fs::hard_link_count(const path& p, error_code& ec) noexcept
841 {
842   return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
843 		 static_cast<uintmax_t>(-1));
844 }
845 
846 bool
is_empty(const path & p)847 fs::is_empty(const path& p)
848 {
849   error_code ec;
850   bool e = is_empty(p, ec);
851   if (ec)
852     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
853 					     p, ec));
854   return e;
855 }
856 
857 bool
is_empty(const path & p,error_code & ec)858 fs::is_empty(const path& p, error_code& ec) noexcept
859 {
860   auto s = status(p, ec);
861   if (ec)
862     return false;
863   bool empty = fs::is_directory(s)
864     ? fs::directory_iterator(p, ec) == fs::directory_iterator()
865     : fs::file_size(p, ec) == 0;
866   return ec ? false : empty;
867 }
868 
869 fs::file_time_type
last_write_time(const path & p)870 fs::last_write_time(const path& p)
871 {
872   error_code ec;
873   auto t = last_write_time(p, ec);
874   if (ec.value())
875     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
876   return t;
877 }
878 
879 fs::file_time_type
last_write_time(const path & p,error_code & ec)880 fs::last_write_time(const path& p, error_code& ec) noexcept
881 {
882   return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
883 		 file_time_type::min());
884 }
885 
886 void
last_write_time(const path & p,file_time_type new_time)887 fs::last_write_time(const path& p, file_time_type new_time)
888 {
889   error_code ec;
890   last_write_time(p, new_time, ec);
891   if (ec.value())
892     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
893 }
894 
895 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)896 fs::last_write_time(const path& p __attribute__((__unused__)),
897 		    file_time_type new_time, error_code& ec) noexcept
898 {
899   auto d = new_time.time_since_epoch();
900   auto s = chrono::duration_cast<chrono::seconds>(d);
901 #if _GLIBCXX_USE_UTIMENSAT
902   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
903   if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
904     {
905       --s;
906       ns += chrono::seconds(1);
907     }
908   struct ::timespec ts[2];
909   ts[0].tv_sec = 0;
910   ts[0].tv_nsec = UTIME_OMIT;
911   ts[1].tv_sec = static_cast<std::time_t>(s.count());
912   ts[1].tv_nsec = static_cast<long>(ns.count());
913   if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
914     ec.assign(errno, std::generic_category());
915   else
916     ec.clear();
917 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
918   posix::utimbuf times;
919   times.modtime = s.count();
920   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
921 			 times.modtime);
922   if (posix::utime(p.c_str(), &times))
923     ec.assign(errno, std::generic_category());
924   else
925     ec.clear();
926 #else
927   ec = std::make_error_code(std::errc::not_supported);
928 #endif
929 }
930 
931 void
permissions(const path & p,perms prms)932 fs::permissions(const path& p, perms prms)
933 {
934   error_code ec;
935   permissions(p, prms, ec);
936   if (ec.value())
937     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
938 }
939 
940 void
permissions(const path & p,perms prms,error_code & ec)941 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
942 {
943   const bool add = is_set(prms, perms::add_perms);
944   const bool remove = is_set(prms, perms::remove_perms);
945   const bool nofollow = is_set(prms, perms::symlink_nofollow);
946   if (add && remove)
947     {
948       ec = std::make_error_code(std::errc::invalid_argument);
949       return;
950     }
951 
952   prms &= perms::mask;
953 
954   file_status st;
955   if (add || remove || nofollow)
956     {
957       st = nofollow ? symlink_status(p, ec) : status(p, ec);
958       if (ec)
959 	return;
960       auto curr = st.permissions();
961       if (add)
962 	prms |= curr;
963       else if (remove)
964 	prms = curr & ~prms;
965     }
966 
967   int err = 0;
968 #if _GLIBCXX_USE_FCHMODAT
969   const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
970   if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
971     err = errno;
972 #else
973   if (nofollow && is_symlink(st))
974     ec = std::make_error_code(std::errc::operation_not_supported);
975   else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
976     err = errno;
977 #endif
978 
979   if (err)
980     ec.assign(err, std::generic_category());
981   else
982     ec.clear();
983 }
984 
985 fs::path
read_symlink(const path & p)986 fs::read_symlink(const path& p)
987 {
988   error_code ec;
989   path tgt = read_symlink(p, ec);
990   if (ec.value())
991     _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
992   return tgt;
993 }
994 
read_symlink(const path & p,error_code & ec)995 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
996 {
997   path result;
998 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
999   stat_type st;
1000   if (posix::lstat(p.c_str(), &st))
1001     {
1002       ec.assign(errno, std::generic_category());
1003       return result;
1004     }
1005   else if (!fs::is_symlink(make_file_status(st)))
1006     {
1007       ec.assign(EINVAL, std::generic_category());
1008       return result;
1009     }
1010 
1011   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1012   do
1013     {
1014       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1015       if (len == -1)
1016 	{
1017 	  ec.assign(errno, std::generic_category());
1018 	  return result;
1019 	}
1020       else if (len == (ssize_t)buf.size())
1021 	{
1022 	  if (buf.size() > 4096)
1023 	    {
1024 	      ec.assign(ENAMETOOLONG, std::generic_category());
1025 	      return result;
1026 	    }
1027 	  buf.resize(buf.size() * 2);
1028 	}
1029       else
1030 	{
1031 	  buf.resize(len);
1032 	  result.assign(buf);
1033 	  ec.clear();
1034 	  break;
1035 	}
1036     }
1037   while (true);
1038 #else
1039   ec = std::make_error_code(std::errc::not_supported);
1040 #endif
1041   return result;
1042 }
1043 
1044 
1045 bool
remove(const path & p)1046 fs::remove(const path& p)
1047 {
1048   error_code ec;
1049   bool result = fs::remove(p, ec);
1050   if (ec)
1051     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1052   return result;
1053 }
1054 
1055 bool
remove(const path & p,error_code & ec)1056 fs::remove(const path& p, error_code& ec) noexcept
1057 {
1058 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1059   auto st = symlink_status(p, ec);
1060   if (exists(st))
1061     {
1062       if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1063 	  || DeleteFileW(p.c_str()))
1064 	{
1065 	  ec.clear();
1066 	  return true;
1067 	}
1068       else if (!ec)
1069 	ec.assign((int)GetLastError(), system_category());
1070     }
1071   else if (status_known(st))
1072     ec.clear();
1073 #else
1074   if (::remove(p.c_str()) == 0)
1075     {
1076       ec.clear();
1077       return true;
1078     }
1079   else if (errno == ENOENT)
1080     ec.clear();
1081   else
1082     ec.assign(errno, std::generic_category());
1083 #endif
1084   return false;
1085 }
1086 
1087 
1088 std::uintmax_t
remove_all(const path & p)1089 fs::remove_all(const path& p)
1090 {
1091   error_code ec;
1092   const auto result = remove_all(p, ec);
1093   if (ec)
1094     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1095   return result;
1096 }
1097 
1098 std::uintmax_t
remove_all(const path & p,error_code & ec)1099 fs::remove_all(const path& p, error_code& ec)
1100 {
1101   const auto s = symlink_status(p, ec);
1102   if (!status_known(s))
1103     return -1;
1104 
1105   ec.clear();
1106   if (s.type() == file_type::not_found)
1107     return 0;
1108 
1109   uintmax_t count = 0;
1110   if (s.type() == file_type::directory)
1111     {
1112       directory_iterator d(p, ec), end;
1113       while (!ec && d != end)
1114 	{
1115 	  const auto removed = fs::remove_all(d->path(), ec);
1116 	  if (removed == numeric_limits<uintmax_t>::max())
1117 	    return -1;
1118 	  count += removed;
1119 	  d.increment(ec);
1120 	  if (ec)
1121 	    return -1;
1122 	}
1123     }
1124 
1125   if (fs::remove(p, ec))
1126     ++count;
1127   return ec ? -1 : count;
1128 }
1129 
1130 void
rename(const path & from,const path & to)1131 fs::rename(const path& from, const path& to)
1132 {
1133   error_code ec;
1134   rename(from, to, ec);
1135   if (ec.value())
1136     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1137 }
1138 
1139 void
rename(const path & from,const path & to,error_code & ec)1140 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1141 {
1142   if (posix::rename(from.c_str(), to.c_str()))
1143     ec.assign(errno, std::generic_category());
1144   else
1145     ec.clear();
1146 }
1147 
1148 void
resize_file(const path & p,uintmax_t size)1149 fs::resize_file(const path& p, uintmax_t size)
1150 {
1151   error_code ec;
1152   resize_file(p, size, ec);
1153   if (ec.value())
1154     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1155 }
1156 
1157 void
resize_file(const path & p,uintmax_t size,error_code & ec)1158 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1159 {
1160 #ifdef _GLIBCXX_HAVE_UNISTD_H
1161   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1162     ec.assign(EINVAL, std::generic_category());
1163   else if (posix::truncate(p.c_str(), size))
1164     ec.assign(errno, std::generic_category());
1165   else
1166     ec.clear();
1167 #else
1168   ec = std::make_error_code(std::errc::not_supported);
1169 #endif
1170 }
1171 
1172 
1173 fs::space_info
space(const path & p)1174 fs::space(const path& p)
1175 {
1176   error_code ec;
1177   space_info s = space(p, ec);
1178   if (ec.value())
1179     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1180   return s;
1181 }
1182 
1183 fs::space_info
space(const path & p,error_code & ec)1184 fs::space(const path& p, error_code& ec) noexcept
1185 {
1186   space_info info = {
1187     static_cast<uintmax_t>(-1),
1188     static_cast<uintmax_t>(-1),
1189     static_cast<uintmax_t>(-1)
1190   };
1191 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1192   path dir = absolute(p);
1193   dir.remove_filename();
1194   auto str = dir.c_str();
1195 #else
1196   auto str = p.c_str();
1197 #endif
1198   fs::do_space(str, info.capacity, info.free, info.available, ec);
1199   return info;
1200 }
1201 
1202 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1203 fs::file_status
status(const fs::path & p,error_code & ec)1204 fs::status(const fs::path& p, error_code& ec) noexcept
1205 {
1206   file_status status;
1207   stat_type st;
1208   if (posix::stat(p.c_str(), &st))
1209     {
1210       int err = errno;
1211       ec.assign(err, std::generic_category());
1212       if (is_not_found_errno(err))
1213 	status.type(file_type::not_found);
1214 #ifdef EOVERFLOW
1215       else if (err == EOVERFLOW)
1216 	status.type(file_type::unknown);
1217 #endif
1218     }
1219   else
1220     {
1221       status = make_file_status(st);
1222       ec.clear();
1223     }
1224   return status;
1225 }
1226 
1227 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1228 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1229 {
1230   file_status status;
1231   stat_type st;
1232   if (posix::lstat(p.c_str(), &st))
1233     {
1234       int err = errno;
1235       ec.assign(err, std::generic_category());
1236       if (is_not_found_errno(err))
1237 	status.type(file_type::not_found);
1238     }
1239   else
1240     {
1241       status = make_file_status(st);
1242       ec.clear();
1243     }
1244   return status;
1245 }
1246 #endif
1247 
1248 fs::file_status
status(const fs::path & p)1249 fs::status(const fs::path& p)
1250 {
1251   std::error_code ec;
1252   auto result = status(p, ec);
1253   if (result.type() == file_type::none)
1254     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1255   return result;
1256 }
1257 
1258 fs::file_status
symlink_status(const fs::path & p)1259 fs::symlink_status(const fs::path& p)
1260 {
1261   std::error_code ec;
1262   auto result = symlink_status(p, ec);
1263   if (result.type() == file_type::none)
1264     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1265   return result;
1266 }
1267 
1268 fs::path
system_complete(const path & p)1269 fs::system_complete(const path& p)
1270 {
1271   error_code ec;
1272   path comp = system_complete(p, ec);
1273   if (ec.value())
1274     _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1275   return comp;
1276 }
1277 
1278 fs::path
system_complete(const path & p,error_code & ec)1279 fs::system_complete(const path& p, error_code& ec)
1280 {
1281   path base = current_path(ec);
1282 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1283   if (p.is_absolute() || !p.has_root_name()
1284       || p.root_name() == base.root_name())
1285     return absolute(p, base);
1286   // else TODO
1287   ec = std::make_error_code(std::errc::not_supported);
1288   return {};
1289 #else
1290   if (ec.value())
1291     return {};
1292   return absolute(p, base);
1293 #endif
1294 }
1295 
temp_directory_path()1296 fs::path fs::temp_directory_path()
1297 {
1298   error_code ec;
1299   path tmp = temp_directory_path(ec);
1300   if (ec.value())
1301     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1302   return tmp;
1303 }
1304 
temp_directory_path(error_code & ec)1305 fs::path fs::temp_directory_path(error_code& ec)
1306 {
1307   path p;
1308 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1309   unsigned len = 1024;
1310   std::wstring buf;
1311   do
1312     {
1313       buf.resize(len);
1314       len = GetTempPathW(buf.size(), buf.data());
1315     } while (len > buf.size());
1316 
1317   if (len == 0)
1318     {
1319       ec.assign((int)GetLastError(), std::system_category());
1320       return p;
1321     }
1322   buf.resize(len);
1323   p = std::move(buf);
1324 #else
1325   const char* tmpdir = nullptr;
1326   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1327   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1328     tmpdir = ::getenv(*e);
1329   p = tmpdir ? tmpdir : "/tmp";
1330   auto st = status(p, ec);
1331   if (ec)
1332     p.clear();
1333   else if (!is_directory(st))
1334     {
1335       p.clear();
1336       ec = std::make_error_code(std::errc::not_a_directory);
1337     }
1338 #endif
1339   return p;
1340 }
1341 
1342