Use non-throwing is_directory in filesystem::create_directory
[official-gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
blob328332a8a8295415694a679a507ac5840e83ce93
1 // Filesystem TS operations -*- C++ -*-
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.
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.
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.
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/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 #endif
29 #include <experimental/filesystem>
30 #include <functional>
31 #include <ostream>
32 #include <stack>
33 #include <ext/stdio_filebuf.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <limits.h> // PATH_MAX
38 #ifdef _GLIBCXX_HAVE_FCNTL_H
39 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW
40 #endif
41 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
42 # include <sys/stat.h> // stat, utimensat, fchmodat
43 #endif
44 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
45 # include <sys/statvfs.h> // statvfs
46 #endif
47 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
48 # include <utime.h> // utime
49 #endif
51 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
52 namespace experimental { namespace filesystem {
53 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
54 #include "ops-common.h"
56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
57 # undef utime
58 # define utime _wutime
59 # undef chmod
60 # define chmod _wchmod
61 #endif
63 namespace fs = std::experimental::filesystem;
65 fs::path
66 fs::absolute(const path& p, const path& base)
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
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;
84 return abs;
87 namespace
89 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
90 inline bool is_dot(wchar_t c) { return c == L'.'; }
91 #else
92 inline bool is_dot(char c) { return c == '.'; }
93 #endif
95 inline bool is_dot(const fs::path& path)
97 const auto& filename = path.native();
98 return filename.size() == 1 && is_dot(filename[0]);
101 inline bool is_dotdot(const fs::path& path)
103 const auto& filename = path.native();
104 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
107 struct free_as_in_malloc
109 void operator()(void* p) const { ::free(p); }
112 using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
115 fs::path
116 fs::canonical(const path& p, const path& base, error_code& ec)
118 const path pa = absolute(p, base);
119 path result;
121 #ifdef _GLIBCXX_USE_REALPATH
122 char_ptr buf{ nullptr };
123 # if _XOPEN_VERSION < 700
124 // Not safe to call realpath(path, NULL)
125 buf.reset( (char*)::malloc(PATH_MAX) );
126 # endif
127 if (char* rp = ::realpath(pa.c_str(), buf.get()))
129 if (buf == nullptr)
130 buf.reset(rp);
131 result.assign(rp);
132 ec.clear();
133 return result;
135 if (errno != ENAMETOOLONG)
137 ec.assign(errno, std::generic_category());
138 return result;
140 #endif
142 if (!exists(pa, ec))
144 if (!ec)
145 ec = make_error_code(std::errc::no_such_file_or_directory);
146 return result;
148 // else: we know there are (currently) no unresolvable symlink loops
150 result = pa.root_path();
152 deque<path> cmpts;
153 for (auto& f : pa.relative_path())
154 cmpts.push_back(f);
156 int max_allowed_symlinks = 40;
158 while (!cmpts.empty() && !ec)
160 path f = std::move(cmpts.front());
161 cmpts.pop_front();
163 if (is_dot(f))
165 if (!is_directory(result, ec) && !ec)
166 ec.assign(ENOTDIR, std::generic_category());
168 else if (is_dotdot(f))
170 auto parent = result.parent_path();
171 if (parent.empty())
172 result = pa.root_path();
173 else
174 result.swap(parent);
176 else
178 result /= f;
180 if (is_symlink(result, ec))
182 path link = read_symlink(result, ec);
183 if (!ec)
185 if (--max_allowed_symlinks == 0)
186 ec.assign(ELOOP, std::generic_category());
187 else
189 if (link.is_absolute())
191 result = link.root_path();
192 link = link.relative_path();
194 else
195 result.remove_filename();
197 cmpts.insert(cmpts.begin(), link.begin(), link.end());
204 if (ec || !exists(result, ec))
205 result.clear();
207 return result;
210 fs::path
211 fs::canonical(const path& p, error_code& ec)
213 path cur = current_path(ec);
214 if (ec.value())
215 return {};
216 return canonical(p, cur, ec);
219 fs::path
220 fs::canonical(const path& p, const path& base)
222 error_code ec;
223 path can = canonical(p, base, ec);
224 if (ec)
225 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
226 ec));
227 return can;
230 void
231 fs::copy(const path& from, const path& to, copy_options options)
233 error_code ec;
234 copy(from, to, options, ec);
235 if (ec.value())
236 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
239 namespace
241 using std::filesystem::is_set;
243 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
244 typedef struct ::stat stat_type;
246 using std::filesystem::is_not_found_errno;
247 using std::filesystem::file_time;
248 using std::filesystem::do_copy_file;
249 #endif // _GLIBCXX_HAVE_SYS_STAT_H
250 } // namespace
252 void
253 fs::copy(const path& from, const path& to, copy_options options,
254 error_code& ec) noexcept
256 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
257 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
258 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
259 const bool use_lstat = create_symlinks || skip_symlinks;
261 file_status f, t;
262 stat_type from_st, to_st;
263 // _GLIBCXX_RESOLVE_LIB_DEFECTS
264 // 2681. filesystem::copy() cannot copy symlinks
265 if (use_lstat || copy_symlinks
266 ? ::lstat(from.c_str(), &from_st)
267 : ::stat(from.c_str(), &from_st))
269 ec.assign(errno, std::generic_category());
270 return;
272 if (use_lstat
273 ? ::lstat(to.c_str(), &to_st)
274 : ::stat(to.c_str(), &to_st))
276 if (!is_not_found_errno(errno))
278 ec.assign(errno, std::generic_category());
279 return;
281 t = file_status{file_type::not_found};
283 else
284 t = make_file_status(to_st);
285 f = make_file_status(from_st);
287 if (exists(t) && !is_other(t) && !is_other(f)
288 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
290 ec = std::make_error_code(std::errc::file_exists);
291 return;
293 if (is_other(f) || is_other(t))
295 ec = std::make_error_code(std::errc::not_supported);
296 return;
298 if (is_directory(f) && is_regular_file(t))
300 ec = std::make_error_code(std::errc::is_a_directory);
301 return;
304 if (is_symlink(f))
306 if (skip_symlinks)
307 ec.clear();
308 else if (!exists(t) && copy_symlinks)
309 copy_symlink(from, to, ec);
310 else
311 // Not clear what should be done here.
312 // "Otherwise report an error as specified in Error reporting (7)."
313 ec = std::make_error_code(std::errc::invalid_argument);
315 else if (is_regular_file(f))
317 if (is_set(options, copy_options::directories_only))
318 ec.clear();
319 else if (create_symlinks)
320 create_symlink(from, to, ec);
321 else if (is_set(options, copy_options::create_hard_links))
322 create_hard_link(from, to, ec);
323 else if (is_directory(t))
324 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
325 copy_file_options(options), &from_st, nullptr, ec);
326 else
328 auto ptr = exists(t) ? &to_st : &from_st;
329 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
330 &from_st, ptr, ec);
333 // _GLIBCXX_RESOLVE_LIB_DEFECTS
334 // 2682. filesystem::copy() won't create a symlink to a directory
335 else if (is_directory(f) && create_symlinks)
336 ec = std::make_error_code(errc::is_a_directory);
337 else if (is_directory(f) && (is_set(options, copy_options::recursive)
338 || options == copy_options::none))
340 if (!exists(t))
341 if (!create_directory(to, from, ec))
342 return;
343 // set an unused bit in options to disable further recursion
344 if (!is_set(options, copy_options::recursive))
345 options |= static_cast<copy_options>(4096);
346 for (const directory_entry& x : directory_iterator(from))
347 copy(x.path(), to/x.path().filename(), options, ec);
349 // _GLIBCXX_RESOLVE_LIB_DEFECTS
350 // 2683. filesystem::copy() says "no effects"
351 else
352 ec.clear();
355 bool
356 fs::copy_file(const path& from, const path& to, copy_options option)
358 error_code ec;
359 bool result = copy_file(from, to, option, ec);
360 if (ec.value())
361 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
362 ec));
363 return result;
366 bool
367 fs::copy_file(const path& from, const path& to, copy_options options,
368 error_code& ec) noexcept
370 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
371 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
372 nullptr, nullptr, ec);
373 #else
374 ec = std::make_error_code(std::errc::not_supported);
375 return false;
376 #endif
380 void
381 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
383 error_code ec;
384 copy_symlink(existing_symlink, new_symlink, ec);
385 if (ec.value())
386 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
387 existing_symlink, new_symlink, ec));
390 void
391 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
392 error_code& ec) noexcept
394 auto p = read_symlink(existing_symlink, ec);
395 if (ec.value())
396 return;
397 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
398 if (is_directory(p))
400 create_directory_symlink(p, new_symlink, ec);
401 return;
403 #endif
404 create_symlink(p, new_symlink, ec);
408 bool
409 fs::create_directories(const path& p)
411 error_code ec;
412 bool result = create_directories(p, ec);
413 if (ec.value())
414 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
415 ec));
416 return result;
419 bool
420 fs::create_directories(const path& p, error_code& ec) noexcept
422 if (p.empty())
424 ec = std::make_error_code(errc::invalid_argument);
425 return false;
427 std::stack<path> missing;
428 path pp = p;
430 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
432 ec.clear();
433 const auto& filename = pp.filename();
434 if (!is_dot(filename) && !is_dotdot(filename))
435 missing.push(pp);
436 pp.remove_filename();
439 if (ec || missing.empty())
440 return false;
444 const path& top = missing.top();
445 create_directory(top, ec);
446 if (ec && is_directory(top))
447 ec.clear();
448 missing.pop();
450 while (!missing.empty() && !ec);
452 return missing.empty();
455 namespace
457 bool
458 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
460 bool created = false;
461 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
462 ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
463 if (::mkdir(p.c_str(), mode))
465 const int err = errno;
466 if (err != EEXIST || !is_directory(p, ec))
467 ec.assign(err, std::generic_category());
469 else
471 ec.clear();
472 created = true;
474 #else
475 ec = std::make_error_code(std::errc::not_supported);
476 #endif
477 return created;
479 } // namespace
481 bool
482 fs::create_directory(const path& p)
484 error_code ec;
485 bool result = create_directory(p, ec);
486 if (ec.value())
487 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
488 ec));
489 return result;
492 bool
493 fs::create_directory(const path& p, error_code& ec) noexcept
495 return create_dir(p, perms::all, ec);
499 bool
500 fs::create_directory(const path& p, const path& attributes)
502 error_code ec;
503 bool result = create_directory(p, attributes, ec);
504 if (ec.value())
505 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
506 ec));
507 return result;
510 bool
511 fs::create_directory(const path& p, const path& attributes,
512 error_code& ec) noexcept
514 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
515 stat_type st;
516 if (::stat(attributes.c_str(), &st))
518 ec.assign(errno, std::generic_category());
519 return false;
521 return create_dir(p, static_cast<perms>(st.st_mode), ec);
522 #else
523 ec = std::make_error_code(std::errc::not_supported);
524 return false;
525 #endif
529 void
530 fs::create_directory_symlink(const path& to, const path& new_symlink)
532 error_code ec;
533 create_directory_symlink(to, new_symlink, ec);
534 if (ec.value())
535 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
536 to, new_symlink, ec));
539 void
540 fs::create_directory_symlink(const path& to, const path& new_symlink,
541 error_code& ec) noexcept
543 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
544 ec = std::make_error_code(std::errc::not_supported);
545 #else
546 create_symlink(to, new_symlink, ec);
547 #endif
551 void
552 fs::create_hard_link(const path& to, const path& new_hard_link)
554 error_code ec;
555 create_hard_link(to, new_hard_link, ec);
556 if (ec.value())
557 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
558 to, new_hard_link, ec));
561 void
562 fs::create_hard_link(const path& to, const path& new_hard_link,
563 error_code& ec) noexcept
565 #ifdef _GLIBCXX_HAVE_UNISTD_H
566 if (::link(to.c_str(), new_hard_link.c_str()))
567 ec.assign(errno, std::generic_category());
568 else
569 ec.clear();
570 #else
571 ec = std::make_error_code(std::errc::not_supported);
572 #endif
575 void
576 fs::create_symlink(const path& to, const path& new_symlink)
578 error_code ec;
579 create_symlink(to, new_symlink, ec);
580 if (ec.value())
581 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
582 to, new_symlink, ec));
585 void
586 fs::create_symlink(const path& to, const path& new_symlink,
587 error_code& ec) noexcept
589 #ifdef _GLIBCXX_HAVE_UNISTD_H
590 if (::symlink(to.c_str(), new_symlink.c_str()))
591 ec.assign(errno, std::generic_category());
592 else
593 ec.clear();
594 #else
595 ec = std::make_error_code(std::errc::not_supported);
596 #endif
600 fs::path
601 fs::current_path()
603 error_code ec;
604 path p = current_path(ec);
605 if (ec.value())
606 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
607 return p;
610 fs::path
611 fs::current_path(error_code& ec)
613 path p;
614 #ifdef _GLIBCXX_HAVE_UNISTD_H
615 #ifdef __GLIBC__
616 if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
618 p.assign(cwd.get());
619 ec.clear();
621 else
622 ec.assign(errno, std::generic_category());
623 #else
624 long path_max = pathconf(".", _PC_PATH_MAX);
625 size_t size;
626 if (path_max == -1)
627 size = 1024;
628 else if (path_max > 10240)
629 size = 10240;
630 else
631 size = path_max;
632 for (char_ptr buf; p.empty(); size *= 2)
634 buf.reset((char*)malloc(size));
635 if (buf)
637 if (getcwd(buf.get(), size))
639 p.assign(buf.get());
640 ec.clear();
642 else if (errno != ERANGE)
644 ec.assign(errno, std::generic_category());
645 return {};
648 else
650 ec = std::make_error_code(std::errc::not_enough_memory);
651 return {};
654 #endif // __GLIBC__
655 #else // _GLIBCXX_HAVE_UNISTD_H
656 ec = std::make_error_code(std::errc::not_supported);
657 #endif
658 return p;
661 void
662 fs::current_path(const path& p)
664 error_code ec;
665 current_path(p, ec);
666 if (ec.value())
667 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
670 void
671 fs::current_path(const path& p, error_code& ec) noexcept
673 #ifdef _GLIBCXX_HAVE_UNISTD_H
674 if (::chdir(p.c_str()))
675 ec.assign(errno, std::generic_category());
676 else
677 ec.clear();
678 #else
679 ec = std::make_error_code(std::errc::not_supported);
680 #endif
683 bool
684 fs::equivalent(const path& p1, const path& p2)
686 error_code ec;
687 auto result = equivalent(p1, p2, ec);
688 if (ec)
689 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
690 p1, p2, ec));
691 return result;
694 bool
695 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
697 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
698 int err = 0;
699 file_status s1, s2;
700 stat_type st1, st2;
701 if (::stat(p1.c_str(), &st1) == 0)
702 s1 = make_file_status(st1);
703 else if (is_not_found_errno(errno))
704 s1.type(file_type::not_found);
705 else
706 err = errno;
708 if (::stat(p2.c_str(), &st2) == 0)
709 s2 = make_file_status(st2);
710 else if (is_not_found_errno(errno))
711 s2.type(file_type::not_found);
712 else
713 err = errno;
715 if (exists(s1) && exists(s2))
717 if (is_other(s1) && is_other(s2))
719 ec = std::make_error_code(std::errc::not_supported);
720 return false;
722 ec.clear();
723 if (is_other(s1) || is_other(s2))
724 return false;
725 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
727 else if (!exists(s1) && !exists(s2))
728 ec = std::make_error_code(std::errc::no_such_file_or_directory);
729 else if (err)
730 ec.assign(err, std::generic_category());
731 else
732 ec.clear();
733 return false;
734 #else
735 ec = std::make_error_code(std::errc::not_supported);
736 #endif
737 return false;
740 std::uintmax_t
741 fs::file_size(const path& p)
743 error_code ec;
744 auto sz = file_size(p, ec);
745 if (ec.value())
746 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
747 return sz;
750 namespace
752 template<typename Accessor, typename T>
753 inline T
754 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
756 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
757 stat_type st;
758 if (::stat(p.c_str(), &st))
760 ec.assign(errno, std::generic_category());
761 return deflt;
763 ec.clear();
764 return f(st);
765 #else
766 ec = std::make_error_code(std::errc::not_supported);
767 return deflt;
768 #endif
772 std::uintmax_t
773 fs::file_size(const path& p, error_code& ec) noexcept
775 struct S
777 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
778 S() : type(file_type::not_found) { }
779 file_type type;
780 size_t size;
782 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
783 if (s.type == file_type::regular)
784 return s.size;
785 if (!ec)
787 if (s.type == file_type::directory)
788 ec = std::make_error_code(std::errc::is_a_directory);
789 else
790 ec = std::make_error_code(std::errc::not_supported);
792 return -1;
795 std::uintmax_t
796 fs::hard_link_count(const path& p)
798 error_code ec;
799 auto count = hard_link_count(p, ec);
800 if (ec.value())
801 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
802 return count;
805 std::uintmax_t
806 fs::hard_link_count(const path& p, error_code& ec) noexcept
808 return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
809 static_cast<uintmax_t>(-1));
812 bool
813 fs::is_empty(const path& p)
815 error_code ec;
816 bool e = is_empty(p, ec);
817 if (ec)
818 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
819 p, ec));
820 return e;
823 bool
824 fs::is_empty(const path& p, error_code& ec) noexcept
826 auto s = status(p, ec);
827 if (ec)
828 return false;
829 bool empty = fs::is_directory(s)
830 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
831 : fs::file_size(p, ec) == 0;
832 return ec ? false : empty;
835 fs::file_time_type
836 fs::last_write_time(const path& p)
838 error_code ec;
839 auto t = last_write_time(p, ec);
840 if (ec.value())
841 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
842 return t;
845 fs::file_time_type
846 fs::last_write_time(const path& p, error_code& ec) noexcept
848 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
849 file_time_type::min());
852 void
853 fs::last_write_time(const path& p, file_time_type new_time)
855 error_code ec;
856 last_write_time(p, new_time, ec);
857 if (ec.value())
858 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
861 void
862 fs::last_write_time(const path& p __attribute__((__unused__)),
863 file_time_type new_time, error_code& ec) noexcept
865 auto d = new_time.time_since_epoch();
866 auto s = chrono::duration_cast<chrono::seconds>(d);
867 #if _GLIBCXX_USE_UTIMENSAT
868 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
869 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
871 --s;
872 ns += chrono::seconds(1);
874 struct ::timespec ts[2];
875 ts[0].tv_sec = 0;
876 ts[0].tv_nsec = UTIME_OMIT;
877 ts[1].tv_sec = static_cast<std::time_t>(s.count());
878 ts[1].tv_nsec = static_cast<long>(ns.count());
879 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
880 ec.assign(errno, std::generic_category());
881 else
882 ec.clear();
883 #elif _GLIBCXX_HAVE_UTIME_H
884 ::utimbuf times;
885 times.modtime = s.count();
886 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
887 times.modtime);
888 if (::utime(p.c_str(), &times))
889 ec.assign(errno, std::generic_category());
890 else
891 ec.clear();
892 #else
893 ec = std::make_error_code(std::errc::not_supported);
894 #endif
897 void
898 fs::permissions(const path& p, perms prms)
900 error_code ec;
901 permissions(p, prms, ec);
902 if (ec.value())
903 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
906 void
907 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
909 const bool add = is_set(prms, perms::add_perms);
910 const bool remove = is_set(prms, perms::remove_perms);
911 const bool nofollow = is_set(prms, perms::symlink_nofollow);
912 if (add && remove)
914 ec = std::make_error_code(std::errc::invalid_argument);
915 return;
918 prms &= perms::mask;
920 file_status st;
921 if (add || remove || nofollow)
923 st = nofollow ? symlink_status(p, ec) : status(p, ec);
924 if (ec)
925 return;
926 auto curr = st.permissions();
927 if (add)
928 prms |= curr;
929 else if (remove)
930 prms = curr & ~prms;
933 int err = 0;
934 #if _GLIBCXX_USE_FCHMODAT
935 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
936 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
937 err = errno;
938 #else
939 if (nofollow && is_symlink(st))
940 ec = std::make_error_code(std::errc::operation_not_supported);
941 else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
942 err = errno;
943 #endif
945 if (err)
946 ec.assign(err, std::generic_category());
947 else
948 ec.clear();
951 fs::path
952 fs::read_symlink(const path& p)
954 error_code ec;
955 path tgt = read_symlink(p, ec);
956 if (ec.value())
957 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
958 return tgt;
961 fs::path fs::read_symlink(const path& p, error_code& ec)
963 path result;
964 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
965 stat_type st;
966 if (::lstat(p.c_str(), &st))
968 ec.assign(errno, std::generic_category());
969 return result;
971 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
974 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
975 if (len == -1)
977 ec.assign(errno, std::generic_category());
978 return result;
980 else if (len == (ssize_t)buf.size())
982 if (buf.size() > 4096)
984 ec.assign(ENAMETOOLONG, std::generic_category());
985 return result;
987 buf.resize(buf.size() * 2);
989 else
991 buf.resize(len);
992 result.assign(buf);
993 ec.clear();
994 break;
997 while (true);
998 #else
999 ec = std::make_error_code(std::errc::not_supported);
1000 #endif
1001 return result;
1005 bool
1006 fs::remove(const path& p)
1008 error_code ec;
1009 bool result = fs::remove(p, ec);
1010 if (ec)
1011 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1012 return result;
1015 bool
1016 fs::remove(const path& p, error_code& ec) noexcept
1018 if (::remove(p.c_str()) == 0)
1020 ec.clear();
1021 return true;
1023 else if (errno == ENOENT)
1024 ec.clear();
1025 else
1026 ec.assign(errno, std::generic_category());
1027 return false;
1031 std::uintmax_t
1032 fs::remove_all(const path& p)
1034 error_code ec;
1035 const auto result = remove_all(p, ec);
1036 if (ec)
1037 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1038 return result;
1041 std::uintmax_t
1042 fs::remove_all(const path& p, error_code& ec) noexcept
1044 const auto s = symlink_status(p, ec);
1045 if (!status_known(s))
1046 return -1;
1048 ec.clear();
1049 if (s.type() == file_type::not_found)
1050 return 0;
1052 uintmax_t count = 0;
1053 if (s.type() == file_type::directory)
1055 for (directory_iterator d(p, ec), end; !ec && d != end; d.increment(ec))
1056 count += fs::remove_all(d->path(), ec);
1057 if (ec.value() == ENOENT)
1058 ec.clear();
1059 else if (ec)
1060 return -1;
1063 if (fs::remove(p, ec))
1064 ++count;
1065 return ec ? -1 : count;
1068 void
1069 fs::rename(const path& from, const path& to)
1071 error_code ec;
1072 rename(from, to, ec);
1073 if (ec.value())
1074 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1077 void
1078 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1080 if (::rename(from.c_str(), to.c_str()))
1081 ec.assign(errno, std::generic_category());
1082 else
1083 ec.clear();
1086 void
1087 fs::resize_file(const path& p, uintmax_t size)
1089 error_code ec;
1090 resize_file(p, size, ec);
1091 if (ec.value())
1092 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1095 void
1096 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1098 #ifdef _GLIBCXX_HAVE_UNISTD_H
1099 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1100 ec.assign(EINVAL, std::generic_category());
1101 else if (::truncate(p.c_str(), size))
1102 ec.assign(errno, std::generic_category());
1103 else
1104 ec.clear();
1105 #else
1106 ec = std::make_error_code(std::errc::not_supported);
1107 #endif
1111 fs::space_info
1112 fs::space(const path& p)
1114 error_code ec;
1115 space_info s = space(p, ec);
1116 if (ec.value())
1117 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1118 return s;
1121 fs::space_info
1122 fs::space(const path& p, error_code& ec) noexcept
1124 space_info info = {
1125 static_cast<uintmax_t>(-1),
1126 static_cast<uintmax_t>(-1),
1127 static_cast<uintmax_t>(-1)
1129 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1130 struct ::statvfs f;
1131 if (::statvfs(p.c_str(), &f))
1132 ec.assign(errno, std::generic_category());
1133 else
1135 info = space_info{
1136 f.f_blocks * f.f_frsize,
1137 f.f_bfree * f.f_frsize,
1138 f.f_bavail * f.f_frsize
1140 ec.clear();
1142 #else
1143 ec = std::make_error_code(std::errc::not_supported);
1144 #endif
1145 return info;
1148 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1149 fs::file_status
1150 fs::status(const fs::path& p, error_code& ec) noexcept
1152 file_status status;
1153 stat_type st;
1154 if (::stat(p.c_str(), &st))
1156 int err = errno;
1157 ec.assign(err, std::generic_category());
1158 if (is_not_found_errno(err))
1159 status.type(file_type::not_found);
1160 #ifdef EOVERFLOW
1161 else if (err == EOVERFLOW)
1162 status.type(file_type::unknown);
1163 #endif
1165 else
1167 status = make_file_status(st);
1168 ec.clear();
1170 return status;
1173 fs::file_status
1174 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1176 file_status status;
1177 stat_type st;
1178 if (::lstat(p.c_str(), &st))
1180 int err = errno;
1181 ec.assign(err, std::generic_category());
1182 if (is_not_found_errno(err))
1183 status.type(file_type::not_found);
1185 else
1187 status = make_file_status(st);
1188 ec.clear();
1190 return status;
1192 #endif
1194 fs::file_status
1195 fs::status(const fs::path& p)
1197 std::error_code ec;
1198 auto result = status(p, ec);
1199 if (result.type() == file_type::none)
1200 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1201 return result;
1204 fs::file_status
1205 fs::symlink_status(const fs::path& p)
1207 std::error_code ec;
1208 auto result = symlink_status(p, ec);
1209 if (result.type() == file_type::none)
1210 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1211 return result;
1214 fs::path
1215 fs::system_complete(const path& p)
1217 error_code ec;
1218 path comp = system_complete(p, ec);
1219 if (ec.value())
1220 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1221 return comp;
1224 fs::path
1225 fs::system_complete(const path& p, error_code& ec)
1227 path base = current_path(ec);
1228 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1229 if (p.is_absolute() || !p.has_root_name()
1230 || p.root_name() == base.root_name())
1231 return absolute(p, base);
1232 // else TODO
1233 ec = std::make_error_code(std::errc::not_supported);
1234 return {};
1235 #else
1236 if (ec.value())
1237 return {};
1238 return absolute(p, base);
1239 #endif
1242 fs::path fs::temp_directory_path()
1244 error_code ec;
1245 path tmp = temp_directory_path(ec);
1246 if (ec.value())
1247 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1248 return tmp;
1251 fs::path fs::temp_directory_path(error_code& ec)
1253 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1254 ec = std::make_error_code(std::errc::not_supported);
1255 return {}; // TODO
1256 #else
1257 const char* tmpdir = nullptr;
1258 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1259 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1260 tmpdir = ::getenv(*e);
1261 path p = tmpdir ? tmpdir : "/tmp";
1262 auto st = status(p, ec);
1263 if (!ec)
1265 if (is_directory(st))
1267 ec.clear();
1268 return p;
1270 else
1271 ec = std::make_error_code(std::errc::not_a_directory);
1273 return {};
1274 #endif