1 // Filesystem TS 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 #endif
28 
29 #include <bits/largefile-config.h>
30 #include <experimental/filesystem>
31 #include <functional>
32 #include <ostream>
33 #include <stack>
34 #include <ext/stdio_filebuf.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <limits.h>  // PATH_MAX
39 #ifdef _GLIBCXX_HAVE_FCNTL_H
40 # include <fcntl.h>  // AT_FDCWD, AT_SYMLINK_NOFOLLOW
41 #endif
42 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
43 #  include <sys/stat.h>   // stat, utimensat, fchmodat
44 #endif
45 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
46 # include <sys/statvfs.h> // statvfs
47 #endif
48 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
49 # include <utime.h> // utime
50 #endif
51 
52 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
53   namespace experimental { namespace filesystem {
54 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
55 #include "ops-common.h"
56 
57 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
58 # undef utime
59 # define utime _wutime
60 # undef chmod
61 # define chmod _wchmod
62 #endif
63 
64 namespace fs = std::experimental::filesystem;
65 
66 fs::path
absolute(const path & p,const path & base)67 fs::absolute(const path& p, const path& base)
68 {
69   const bool has_root_dir = p.has_root_directory();
70   const bool has_root_name = p.has_root_name();
71   path abs;
72   if (has_root_dir && has_root_name)
73     abs = p;
74   else
75     {
76       abs = base.is_absolute() ? base : absolute(base);
77       if (has_root_dir)
78 	abs = abs.root_name() / p;
79       else if (has_root_name)
80 	abs = p.root_name() / abs.root_directory() / abs.relative_path()
81 	  / p.relative_path();
82       else
83 	abs = abs / p;
84     }
85   return abs;
86 }
87 
88 namespace
89 {
90 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_dot(wchar_t c)91   inline bool is_dot(wchar_t c) { return c == L'.'; }
92 #else
93   inline bool is_dot(char c) { return c == '.'; }
94 #endif
95 
is_dot(const fs::path & path)96   inline bool is_dot(const fs::path& path)
97   {
98     const auto& filename = path.native();
99     return filename.size() == 1 && is_dot(filename[0]);
100   }
101 
is_dotdot(const fs::path & path)102   inline bool is_dotdot(const fs::path& path)
103   {
104     const auto& filename = path.native();
105     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
106   }
107 
108   struct free_as_in_malloc
109   {
operator ()__anon8b32e2c10111::free_as_in_malloc110     void operator()(void* p) const { ::free(p); }
111   };
112 
113   using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
114 }
115 
116 fs::path
canonical(const path & p,const path & base,error_code & ec)117 fs::canonical(const path& p, const path& base, error_code& ec)
118 {
119   const path pa = absolute(p, base);
120   path result;
121 
122 #ifdef _GLIBCXX_USE_REALPATH
123   char_ptr buf{ nullptr };
124 # if _XOPEN_VERSION < 700
125   // Not safe to call realpath(path, NULL)
126   buf.reset( (char*)::malloc(PATH_MAX) );
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   typedef struct ::stat stat_type;
246 
247   using std::filesystem::is_not_found_errno;
248   using std::filesystem::file_time;
249   using std::filesystem::do_copy_file;
250 #endif // _GLIBCXX_HAVE_SYS_STAT_H
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       ? ::lstat(from.c_str(), &from_st)
268       : ::stat(from.c_str(), &from_st))
269     {
270       ec.assign(errno, std::generic_category());
271       return;
272     }
273   if (use_lstat
274       ? ::lstat(to.c_str(), &to_st)
275       : ::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 = symlink_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     ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
482     if (::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 (::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_UNISTD_H
585   if (::link(to.c_str(), new_hard_link.c_str()))
586     ec.assign(errno, std::generic_category());
587   else
588     ec.clear();
589 #else
590   ec = std::make_error_code(std::errc::not_supported);
591 #endif
592 }
593 
594 void
create_symlink(const path & to,const path & new_symlink)595 fs::create_symlink(const path& to, const path& new_symlink)
596 {
597   error_code ec;
598   create_symlink(to, new_symlink, ec);
599   if (ec.value())
600     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
601 	  to, new_symlink, ec));
602 }
603 
604 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)605 fs::create_symlink(const path& to, const path& new_symlink,
606 		   error_code& ec) noexcept
607 {
608 #ifdef _GLIBCXX_HAVE_UNISTD_H
609   if (::symlink(to.c_str(), new_symlink.c_str()))
610     ec.assign(errno, std::generic_category());
611   else
612     ec.clear();
613 #else
614   ec = std::make_error_code(std::errc::not_supported);
615 #endif
616 }
617 
618 
619 fs::path
current_path()620 fs::current_path()
621 {
622   error_code ec;
623   path p = current_path(ec);
624   if (ec.value())
625     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
626   return p;
627 }
628 
629 fs::path
current_path(error_code & ec)630 fs::current_path(error_code& ec)
631 {
632   path p;
633 #ifdef _GLIBCXX_HAVE_UNISTD_H
634 #ifdef __GLIBC__
635   if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
636     {
637       p.assign(cwd.get());
638       ec.clear();
639     }
640   else
641     ec.assign(errno, std::generic_category());
642 #else
643   long path_max = pathconf(".", _PC_PATH_MAX);
644   size_t size;
645   if (path_max == -1)
646       size = 1024;
647   else if (path_max > 10240)
648       size = 10240;
649   else
650       size = path_max;
651   for (char_ptr buf; p.empty(); size *= 2)
652     {
653       buf.reset((char*)malloc(size));
654       if (buf)
655 	{
656 	  if (getcwd(buf.get(), size))
657 	    {
658 	      p.assign(buf.get());
659 	      ec.clear();
660 	    }
661 	  else if (errno != ERANGE)
662 	    {
663 	      ec.assign(errno, std::generic_category());
664 	      return {};
665 	    }
666 	}
667       else
668 	{
669 	  ec = std::make_error_code(std::errc::not_enough_memory);
670 	  return {};
671 	}
672     }
673 #endif  // __GLIBC__
674 #else   // _GLIBCXX_HAVE_UNISTD_H
675   ec = std::make_error_code(std::errc::not_supported);
676 #endif
677   return p;
678 }
679 
680 void
current_path(const path & p)681 fs::current_path(const path& p)
682 {
683   error_code ec;
684   current_path(p, ec);
685   if (ec.value())
686     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
687 }
688 
689 void
current_path(const path & p,error_code & ec)690 fs::current_path(const path& p, error_code& ec) noexcept
691 {
692 #ifdef _GLIBCXX_HAVE_UNISTD_H
693   if (::chdir(p.c_str()))
694     ec.assign(errno, std::generic_category());
695   else
696     ec.clear();
697 #else
698   ec = std::make_error_code(std::errc::not_supported);
699 #endif
700 }
701 
702 bool
equivalent(const path & p1,const path & p2)703 fs::equivalent(const path& p1, const path& p2)
704 {
705   error_code ec;
706   auto result = equivalent(p1, p2, ec);
707   if (ec)
708     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
709 	  p1, p2, ec));
710   return result;
711 }
712 
713 bool
equivalent(const path & p1,const path & p2,error_code & ec)714 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
715 {
716 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
717   int err = 0;
718   file_status s1, s2;
719   stat_type st1, st2;
720   if (::stat(p1.c_str(), &st1) == 0)
721     s1 = make_file_status(st1);
722   else if (is_not_found_errno(errno))
723     s1.type(file_type::not_found);
724   else
725     err = errno;
726 
727   if (::stat(p2.c_str(), &st2) == 0)
728     s2 = make_file_status(st2);
729   else if (is_not_found_errno(errno))
730     s2.type(file_type::not_found);
731   else
732     err = errno;
733 
734   if (exists(s1) && exists(s2))
735     {
736       if (is_other(s1) && is_other(s2))
737 	{
738 	  ec = std::make_error_code(std::errc::not_supported);
739 	  return false;
740 	}
741       ec.clear();
742       if (is_other(s1) || is_other(s2))
743 	return false;
744       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
745     }
746   else if (!exists(s1) && !exists(s2))
747     ec = std::make_error_code(std::errc::no_such_file_or_directory);
748   else if (err)
749     ec.assign(err, std::generic_category());
750   else
751     ec.clear();
752   return false;
753 #else
754   ec = std::make_error_code(std::errc::not_supported);
755 #endif
756   return false;
757 }
758 
759 std::uintmax_t
file_size(const path & p)760 fs::file_size(const path& p)
761 {
762   error_code ec;
763   auto sz = file_size(p, ec);
764   if (ec.value())
765     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
766   return sz;
767 }
768 
769 namespace
770 {
771   template<typename Accessor, typename T>
772     inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)773     do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
774     {
775 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
776       stat_type st;
777       if (::stat(p.c_str(), &st))
778 	{
779 	  ec.assign(errno, std::generic_category());
780 	  return deflt;
781 	}
782       ec.clear();
783       return f(st);
784 #else
785       ec = std::make_error_code(std::errc::not_supported);
786       return deflt;
787 #endif
788     }
789 }
790 
791 std::uintmax_t
file_size(const path & p,error_code & ec)792 fs::file_size(const path& p, error_code& ec) noexcept
793 {
794   struct S
795   {
796     S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
797     S() : type(file_type::not_found) { }
798     file_type type;
799     uintmax_t size;
800   };
801   auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
802   if (s.type == file_type::regular)
803     return s.size;
804   if (!ec)
805     {
806       if (s.type == file_type::directory)
807 	ec = std::make_error_code(std::errc::is_a_directory);
808       else
809 	ec = std::make_error_code(std::errc::not_supported);
810     }
811   return -1;
812 }
813 
814 std::uintmax_t
hard_link_count(const path & p)815 fs::hard_link_count(const path& p)
816 {
817   error_code ec;
818   auto count = hard_link_count(p, ec);
819   if (ec.value())
820     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
821   return count;
822 }
823 
824 std::uintmax_t
hard_link_count(const path & p,error_code & ec)825 fs::hard_link_count(const path& p, error_code& ec) noexcept
826 {
827   return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
828 		 static_cast<uintmax_t>(-1));
829 }
830 
831 bool
is_empty(const path & p)832 fs::is_empty(const path& p)
833 {
834   error_code ec;
835   bool e = is_empty(p, ec);
836   if (ec)
837     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
838 					     p, ec));
839   return e;
840 }
841 
842 bool
is_empty(const path & p,error_code & ec)843 fs::is_empty(const path& p, error_code& ec) noexcept
844 {
845   auto s = status(p, ec);
846   if (ec)
847     return false;
848   bool empty = fs::is_directory(s)
849     ? fs::directory_iterator(p, ec) == fs::directory_iterator()
850     : fs::file_size(p, ec) == 0;
851   return ec ? false : empty;
852 }
853 
854 fs::file_time_type
last_write_time(const path & p)855 fs::last_write_time(const path& p)
856 {
857   error_code ec;
858   auto t = last_write_time(p, ec);
859   if (ec.value())
860     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
861   return t;
862 }
863 
864 fs::file_time_type
last_write_time(const path & p,error_code & ec)865 fs::last_write_time(const path& p, error_code& ec) noexcept
866 {
867   return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
868 		 file_time_type::min());
869 }
870 
871 void
last_write_time(const path & p,file_time_type new_time)872 fs::last_write_time(const path& p, file_time_type new_time)
873 {
874   error_code ec;
875   last_write_time(p, new_time, ec);
876   if (ec.value())
877     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
878 }
879 
880 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)881 fs::last_write_time(const path& p __attribute__((__unused__)),
882 		    file_time_type new_time, error_code& ec) noexcept
883 {
884   auto d = new_time.time_since_epoch();
885   auto s = chrono::duration_cast<chrono::seconds>(d);
886 #if _GLIBCXX_USE_UTIMENSAT
887   auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
888   if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
889     {
890       --s;
891       ns += chrono::seconds(1);
892     }
893   struct ::timespec ts[2];
894   ts[0].tv_sec = 0;
895   ts[0].tv_nsec = UTIME_OMIT;
896   ts[1].tv_sec = static_cast<std::time_t>(s.count());
897   ts[1].tv_nsec = static_cast<long>(ns.count());
898   if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
899     ec.assign(errno, std::generic_category());
900   else
901     ec.clear();
902 #elif _GLIBCXX_HAVE_UTIME_H
903   ::utimbuf times;
904   times.modtime = s.count();
905   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
906 			 times.modtime);
907   if (::utime(p.c_str(), &times))
908     ec.assign(errno, std::generic_category());
909   else
910     ec.clear();
911 #else
912   ec = std::make_error_code(std::errc::not_supported);
913 #endif
914 }
915 
916 void
permissions(const path & p,perms prms)917 fs::permissions(const path& p, perms prms)
918 {
919   error_code ec;
920   permissions(p, prms, ec);
921   if (ec.value())
922     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
923 }
924 
925 void
permissions(const path & p,perms prms,error_code & ec)926 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
927 {
928   const bool add = is_set(prms, perms::add_perms);
929   const bool remove = is_set(prms, perms::remove_perms);
930   const bool nofollow = is_set(prms, perms::symlink_nofollow);
931   if (add && remove)
932     {
933       ec = std::make_error_code(std::errc::invalid_argument);
934       return;
935     }
936 
937   prms &= perms::mask;
938 
939   file_status st;
940   if (add || remove || nofollow)
941     {
942       st = nofollow ? symlink_status(p, ec) : status(p, ec);
943       if (ec)
944 	return;
945       auto curr = st.permissions();
946       if (add)
947 	prms |= curr;
948       else if (remove)
949 	prms = curr & ~prms;
950     }
951 
952   int err = 0;
953 #if _GLIBCXX_USE_FCHMODAT
954   const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
955   if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
956     err = errno;
957 #else
958   if (nofollow && is_symlink(st))
959     ec = std::make_error_code(std::errc::operation_not_supported);
960   else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
961     err = errno;
962 #endif
963 
964   if (err)
965     ec.assign(err, std::generic_category());
966   else
967     ec.clear();
968 }
969 
970 fs::path
read_symlink(const path & p)971 fs::read_symlink(const path& p)
972 {
973   error_code ec;
974   path tgt = read_symlink(p, ec);
975   if (ec.value())
976     _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
977   return tgt;
978 }
979 
read_symlink(const path & p,error_code & ec)980 fs::path fs::read_symlink(const path& p, error_code& ec)
981 {
982   path result;
983 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
984   stat_type st;
985   if (::lstat(p.c_str(), &st))
986     {
987       ec.assign(errno, std::generic_category());
988       return result;
989     }
990   else if (!fs::is_symlink(make_file_status(st)))
991     {
992       ec.assign(EINVAL, std::generic_category());
993       return result;
994     }
995 
996   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
997   do
998     {
999       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1000       if (len == -1)
1001 	{
1002 	  ec.assign(errno, std::generic_category());
1003 	  return result;
1004 	}
1005       else if (len == (ssize_t)buf.size())
1006 	{
1007 	  if (buf.size() > 4096)
1008 	    {
1009 	      ec.assign(ENAMETOOLONG, std::generic_category());
1010 	      return result;
1011 	    }
1012 	  buf.resize(buf.size() * 2);
1013 	}
1014       else
1015 	{
1016 	  buf.resize(len);
1017 	  result.assign(buf);
1018 	  ec.clear();
1019 	  break;
1020 	}
1021     }
1022   while (true);
1023 #else
1024   ec = std::make_error_code(std::errc::not_supported);
1025 #endif
1026   return result;
1027 }
1028 
1029 
1030 bool
remove(const path & p)1031 fs::remove(const path& p)
1032 {
1033   error_code ec;
1034   bool result = fs::remove(p, ec);
1035   if (ec)
1036     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1037   return result;
1038 }
1039 
1040 bool
remove(const path & p,error_code & ec)1041 fs::remove(const path& p, error_code& ec) noexcept
1042 {
1043   if (::remove(p.c_str()) == 0)
1044     {
1045       ec.clear();
1046       return true;
1047     }
1048   else if (errno == ENOENT)
1049     ec.clear();
1050   else
1051     ec.assign(errno, std::generic_category());
1052   return false;
1053 }
1054 
1055 
1056 std::uintmax_t
remove_all(const path & p)1057 fs::remove_all(const path& p)
1058 {
1059   error_code ec;
1060   const auto result = remove_all(p, ec);
1061   if (ec)
1062     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1063   return result;
1064 }
1065 
1066 std::uintmax_t
remove_all(const path & p,error_code & ec)1067 fs::remove_all(const path& p, error_code& ec) noexcept
1068 {
1069   const auto s = symlink_status(p, ec);
1070   if (!status_known(s))
1071     return -1;
1072 
1073   ec.clear();
1074   if (s.type() == file_type::not_found)
1075     return 0;
1076 
1077   uintmax_t count = 0;
1078   if (s.type() == file_type::directory)
1079     {
1080       directory_iterator d(p, ec), end;
1081       while (!ec && d != end)
1082 	{
1083 	  const auto removed = fs::remove_all(d->path(), ec);
1084 	  if (removed == numeric_limits<uintmax_t>::max())
1085 	    return -1;
1086 	  count += removed;
1087 	  d.increment(ec);
1088 	  if (ec)
1089 	    return -1;
1090 	}
1091     }
1092 
1093   if (fs::remove(p, ec))
1094     ++count;
1095   return ec ? -1 : count;
1096 }
1097 
1098 void
rename(const path & from,const path & to)1099 fs::rename(const path& from, const path& to)
1100 {
1101   error_code ec;
1102   rename(from, to, ec);
1103   if (ec.value())
1104     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1105 }
1106 
1107 void
rename(const path & from,const path & to,error_code & ec)1108 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1109 {
1110   if (::rename(from.c_str(), to.c_str()))
1111     ec.assign(errno, std::generic_category());
1112   else
1113     ec.clear();
1114 }
1115 
1116 void
resize_file(const path & p,uintmax_t size)1117 fs::resize_file(const path& p, uintmax_t size)
1118 {
1119   error_code ec;
1120   resize_file(p, size, ec);
1121   if (ec.value())
1122     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1123 }
1124 
1125 void
resize_file(const path & p,uintmax_t size,error_code & ec)1126 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1127 {
1128 #ifdef _GLIBCXX_HAVE_UNISTD_H
1129   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1130     ec.assign(EINVAL, std::generic_category());
1131   else if (::truncate(p.c_str(), size))
1132     ec.assign(errno, std::generic_category());
1133   else
1134     ec.clear();
1135 #else
1136   ec = std::make_error_code(std::errc::not_supported);
1137 #endif
1138 }
1139 
1140 
1141 fs::space_info
space(const path & p)1142 fs::space(const path& p)
1143 {
1144   error_code ec;
1145   space_info s = space(p, ec);
1146   if (ec.value())
1147     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1148   return s;
1149 }
1150 
1151 fs::space_info
space(const path & p,error_code & ec)1152 fs::space(const path& p, error_code& ec) noexcept
1153 {
1154   space_info info = {
1155     static_cast<uintmax_t>(-1),
1156     static_cast<uintmax_t>(-1),
1157     static_cast<uintmax_t>(-1)
1158   };
1159 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1160   struct ::statvfs f;
1161   if (::statvfs(p.c_str(), &f))
1162       ec.assign(errno, std::generic_category());
1163   else
1164     {
1165       uintmax_t fragment_size = f.f_frsize;
1166       info = space_info{
1167 	f.f_blocks * fragment_size,
1168 	f.f_bfree * fragment_size,
1169 	f.f_bavail * fragment_size
1170       };
1171       ec.clear();
1172     }
1173 #else
1174   ec = std::make_error_code(std::errc::not_supported);
1175 #endif
1176   return info;
1177 }
1178 
1179 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1180 fs::file_status
status(const fs::path & p,error_code & ec)1181 fs::status(const fs::path& p, error_code& ec) noexcept
1182 {
1183   file_status status;
1184   stat_type st;
1185   if (::stat(p.c_str(), &st))
1186     {
1187       int err = errno;
1188       ec.assign(err, std::generic_category());
1189       if (is_not_found_errno(err))
1190 	status.type(file_type::not_found);
1191 #ifdef EOVERFLOW
1192       else if (err == EOVERFLOW)
1193 	status.type(file_type::unknown);
1194 #endif
1195     }
1196   else
1197     {
1198       status = make_file_status(st);
1199       ec.clear();
1200     }
1201   return status;
1202 }
1203 
1204 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1205 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1206 {
1207   file_status status;
1208   stat_type st;
1209   if (::lstat(p.c_str(), &st))
1210     {
1211       int err = errno;
1212       ec.assign(err, std::generic_category());
1213       if (is_not_found_errno(err))
1214 	status.type(file_type::not_found);
1215     }
1216   else
1217     {
1218       status = make_file_status(st);
1219       ec.clear();
1220     }
1221   return status;
1222 }
1223 #endif
1224 
1225 fs::file_status
status(const fs::path & p)1226 fs::status(const fs::path& p)
1227 {
1228   std::error_code ec;
1229   auto result = status(p, ec);
1230   if (result.type() == file_type::none)
1231     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1232   return result;
1233 }
1234 
1235 fs::file_status
symlink_status(const fs::path & p)1236 fs::symlink_status(const fs::path& p)
1237 {
1238   std::error_code ec;
1239   auto result = symlink_status(p, ec);
1240   if (result.type() == file_type::none)
1241     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1242   return result;
1243 }
1244 
1245 fs::path
system_complete(const path & p)1246 fs::system_complete(const path& p)
1247 {
1248   error_code ec;
1249   path comp = system_complete(p, ec);
1250   if (ec.value())
1251     _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1252   return comp;
1253 }
1254 
1255 fs::path
system_complete(const path & p,error_code & ec)1256 fs::system_complete(const path& p, error_code& ec)
1257 {
1258   path base = current_path(ec);
1259 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1260   if (p.is_absolute() || !p.has_root_name()
1261       || p.root_name() == base.root_name())
1262     return absolute(p, base);
1263   // else TODO
1264   ec = std::make_error_code(std::errc::not_supported);
1265   return {};
1266 #else
1267   if (ec.value())
1268     return {};
1269   return absolute(p, base);
1270 #endif
1271 }
1272 
temp_directory_path()1273 fs::path fs::temp_directory_path()
1274 {
1275   error_code ec;
1276   path tmp = temp_directory_path(ec);
1277   if (ec.value())
1278     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1279   return tmp;
1280 }
1281 
temp_directory_path(error_code & ec)1282 fs::path fs::temp_directory_path(error_code& ec)
1283 {
1284 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1285   ec = std::make_error_code(std::errc::not_supported);
1286   return {}; // TODO
1287 #else
1288   const char* tmpdir = nullptr;
1289   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1290   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1291     tmpdir = ::getenv(*e);
1292   path p = tmpdir ? tmpdir : "/tmp";
1293   auto st = status(p, ec);
1294   if (!ec)
1295     {
1296       if (is_directory(st))
1297 	{
1298 	  ec.clear();
1299 	  return p;
1300 	}
1301       else
1302 	ec = std::make_error_code(std::errc::not_a_directory);
1303     }
1304   return {};
1305 #endif
1306 }
1307 
1308