Fix typo.
[official-gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
blob8ed0a105dfa3a78525a620f612dbb398c4797e71
1 // Filesystem operations -*- C++ -*-
3 // Copyright (C) 2014-2016 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_UNISTD_H
39 # include <unistd.h>
40 # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H)
41 # include <sys/types.h>
42 # include <sys/stat.h>
43 # endif
44 #endif
45 #ifdef _GLIBCXX_HAVE_FCNTL_H
46 # include <fcntl.h>
47 #endif
48 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
49 # include <sys/statvfs.h>
50 #endif
51 #ifdef _GLIBCXX_USE_SENDFILE
52 # include <sys/sendfile.h>
53 #endif
54 #if _GLIBCXX_HAVE_UTIME_H
55 # include <utime.h>
56 #endif
58 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
59 # undef utime
60 # define utime _wutime
61 # undef chmod
62 # define chmod _wchmod
63 #endif
65 namespace fs = std::experimental::filesystem;
67 fs::path
68 fs::absolute(const path& p, const path& base)
70 const bool has_root_dir = p.has_root_directory();
71 const bool has_root_name = p.has_root_name();
72 path abs;
73 if (has_root_dir && has_root_name)
74 abs = p;
75 else
77 abs = base.is_absolute() ? base : absolute(base);
78 if (has_root_dir)
79 abs = abs.root_name() / p;
80 else if (has_root_name)
81 abs = p.root_name() / abs.root_directory() / abs.relative_path()
82 / p.relative_path();
83 else
84 abs = abs / p;
86 return abs;
89 namespace
91 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
92 inline bool is_dot(wchar_t c) { return c == L'.'; }
93 #else
94 inline bool is_dot(char c) { return c == '.'; }
95 #endif
97 inline bool is_dot(const fs::path& path)
99 const auto& filename = path.native();
100 return filename.size() == 1 && is_dot(filename[0]);
103 inline bool is_dotdot(const fs::path& path)
105 const auto& filename = path.native();
106 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
109 struct free_as_in_malloc
111 void operator()(void* p) const { ::free(p); }
114 using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
117 fs::path
118 fs::canonical(const path& p, const path& base, error_code& ec)
120 const path pa = absolute(p, base);
121 path result;
123 #ifdef _GLIBCXX_USE_REALPATH
124 char_ptr buf{ nullptr };
125 # if _XOPEN_VERSION < 700
126 // Not safe to call realpath(path, NULL)
127 buf.reset( (char*)::malloc(PATH_MAX) );
128 # endif
129 if (char* rp = ::realpath(pa.c_str(), buf.get()))
131 if (buf == nullptr)
132 buf.reset(rp);
133 result.assign(rp);
134 ec.clear();
135 return result;
137 if (errno != ENAMETOOLONG)
139 ec.assign(errno, std::generic_category());
140 return result;
142 #endif
144 if (!exists(pa, ec))
146 if (!ec)
147 ec = make_error_code(std::errc::no_such_file_or_directory);
148 return result;
150 // else: we know there are (currently) no unresolvable symlink loops
152 result = pa.root_path();
154 deque<path> cmpts;
155 for (auto& f : pa.relative_path())
156 cmpts.push_back(f);
158 int max_allowed_symlinks = 40;
160 while (!cmpts.empty() && !ec)
162 path f = std::move(cmpts.front());
163 cmpts.pop_front();
165 if (is_dot(f))
167 if (!is_directory(result, ec) && !ec)
168 ec.assign(ENOTDIR, std::generic_category());
170 else if (is_dotdot(f))
172 auto parent = result.parent_path();
173 if (parent.empty())
174 result = pa.root_path();
175 else
176 result.swap(parent);
178 else
180 result /= f;
182 if (is_symlink(result, ec))
184 path link = read_symlink(result, ec);
185 if (!ec)
187 if (--max_allowed_symlinks == 0)
188 ec.assign(ELOOP, std::generic_category());
189 else
191 if (link.is_absolute())
193 result = link.root_path();
194 link = link.relative_path();
196 else
197 result.remove_filename();
199 cmpts.insert(cmpts.begin(), link.begin(), link.end());
206 if (ec || !exists(result, ec))
207 result.clear();
209 return result;
212 fs::path
213 fs::canonical(const path& p, error_code& ec)
215 path cur = current_path(ec);
216 if (ec.value())
217 return {};
218 return canonical(p, cur, ec);
221 fs::path
222 fs::canonical(const path& p, const path& base)
224 error_code ec;
225 path can = canonical(p, base, ec);
226 if (ec)
227 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
228 ec));
229 return can;
232 void
233 fs::copy(const path& from, const path& to, copy_options options)
235 error_code ec;
236 copy(from, to, options, ec);
237 if (ec.value())
238 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
241 namespace
243 template<typename Bitmask>
244 inline bool is_set(Bitmask obj, Bitmask bits)
246 return (obj & bits) != Bitmask::none;
250 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
251 namespace
253 typedef struct ::stat stat_type;
255 inline fs::file_type
256 make_file_type(const stat_type& st) noexcept
258 using fs::file_type;
259 #ifdef _GLIBCXX_HAVE_S_ISREG
260 if (S_ISREG(st.st_mode))
261 return file_type::regular;
262 else if (S_ISDIR(st.st_mode))
263 return file_type::directory;
264 else if (S_ISCHR(st.st_mode))
265 return file_type::character;
266 else if (S_ISBLK(st.st_mode))
267 return file_type::block;
268 else if (S_ISFIFO(st.st_mode))
269 return file_type::fifo;
270 else if (S_ISLNK(st.st_mode))
271 return file_type::symlink;
272 else if (S_ISSOCK(st.st_mode))
273 return file_type::socket;
274 #endif
275 return file_type::unknown;
279 inline fs::file_status
280 make_file_status(const stat_type& st) noexcept
282 return fs::file_status{
283 make_file_type(st),
284 static_cast<fs::perms>(st.st_mode) & fs::perms::mask
288 inline bool
289 is_not_found_errno(int err) noexcept
291 return err == ENOENT || err == ENOTDIR;
294 inline fs::file_time_type
295 file_time(const stat_type& st, std::error_code& ec) noexcept
297 using namespace std::chrono;
298 #ifdef _GLIBCXX_USE_ST_MTIM
299 time_t s = st.st_mtim.tv_sec;
300 nanoseconds ns{st.st_mtim.tv_nsec};
301 #else
302 time_t s = st.st_mtime;
303 nanoseconds ns{};
304 #endif
306 if (s >= (nanoseconds::max().count() / 1e9))
308 ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
309 return fs::file_time_type::min();
311 ec.clear();
312 return fs::file_time_type{seconds{s} + ns};
315 bool
316 do_copy_file(const fs::path& from, const fs::path& to,
317 fs::copy_options option,
318 stat_type* from_st, stat_type* to_st,
319 std::error_code& ec) noexcept
321 stat_type st1, st2;
322 fs::file_status t, f;
324 if (to_st == nullptr)
326 if (::stat(to.c_str(), &st1))
328 int err = errno;
329 if (!is_not_found_errno(err))
331 ec.assign(err, std::generic_category());
332 return false;
335 else
336 to_st = &st1;
338 else if (to_st == from_st)
339 to_st = nullptr;
341 if (to_st == nullptr)
342 t = fs::file_status{fs::file_type::not_found};
343 else
344 t = make_file_status(*to_st);
346 if (from_st == nullptr)
348 if (::stat(from.c_str(), &st2))
350 ec.assign(errno, std::generic_category());
351 return false;
353 else
354 from_st = &st2;
356 f = make_file_status(*from_st);
357 // _GLIBCXX_RESOLVE_LIB_DEFECTS
358 // 2712. copy_file() has a number of unspecified error conditions
359 if (!is_regular_file(f))
361 ec = std::make_error_code(std::errc::not_supported);
362 return false;
365 using opts = fs::copy_options;
367 if (exists(t))
369 if (!is_regular_file(t))
371 ec = std::make_error_code(std::errc::not_supported);
372 return false;
375 if (to_st->st_dev == from_st->st_dev
376 && to_st->st_ino == from_st->st_ino)
378 ec = std::make_error_code(std::errc::file_exists);
379 return false;
382 if (is_set(option, opts::skip_existing))
384 ec.clear();
385 return false;
387 else if (is_set(option, opts::update_existing))
389 const auto from_mtime = file_time(*from_st, ec);
390 if (ec)
391 return false;
392 if ((from_mtime <= file_time(*to_st, ec)) || ec)
393 return false;
395 else if (!is_set(option, opts::overwrite_existing))
397 ec = std::make_error_code(std::errc::file_exists);
398 return false;
400 else if (!is_regular_file(t))
402 ec = std::make_error_code(std::errc::not_supported);
403 return false;
407 struct CloseFD {
408 ~CloseFD() { if (fd != -1) ::close(fd); }
409 bool close() { return ::close(std::exchange(fd, -1)) == 0; }
410 int fd;
413 CloseFD in = { ::open(from.c_str(), O_RDONLY) };
414 if (in.fd == -1)
416 ec.assign(errno, std::generic_category());
417 return false;
419 int oflag = O_WRONLY|O_CREAT;
420 if (is_set(option, opts::overwrite_existing|opts::update_existing))
421 oflag |= O_TRUNC;
422 else
423 oflag |= O_EXCL;
424 CloseFD out = { ::open(to.c_str(), oflag, S_IWUSR) };
425 if (out.fd == -1)
427 if (errno == EEXIST && is_set(option, opts::skip_existing))
428 ec.clear();
429 else
430 ec.assign(errno, std::generic_category());
431 return false;
434 #ifdef _GLIBCXX_USE_FCHMOD
435 if (::fchmod(out.fd, from_st->st_mode))
436 #elif defined _GLIBCXX_USE_FCHMODAT
437 if (::fchmodat(AT_FDCWD, to.c_str(), from_st->st_mode, 0))
438 #else
439 if (::chmod(to.c_str(), from_st->st_mode))
440 #endif
442 ec.assign(errno, std::generic_category());
443 return false;
446 #ifdef _GLIBCXX_USE_SENDFILE
447 off_t offset = 0;
448 const auto n = ::sendfile(out.fd, in.fd, &offset, from_st->st_size);
449 if (n < 0 && (errno == ENOSYS || errno == EINVAL))
451 #endif
452 __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in);
453 __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out);
454 if (sbin.is_open())
455 in.fd = -1;
456 if (sbout.is_open())
457 out.fd = -1;
458 if (from_st->st_size && !(std::ostream(&sbout) << &sbin))
460 ec = std::make_error_code(std::errc::io_error);
461 return false;
463 if (!sbout.close() || !sbin.close())
465 ec.assign(errno, std::generic_category());
466 return false;
469 ec.clear();
470 return true;
472 #ifdef _GLIBCXX_USE_SENDFILE
474 if (n != from_st->st_size)
476 ec.assign(errno, std::generic_category());
477 return false;
479 if (!out.close() || !in.close())
481 ec.assign(errno, std::generic_category());
482 return false;
485 ec.clear();
486 return true;
487 #endif
490 #endif
492 void
493 fs::copy(const path& from, const path& to, copy_options options,
494 error_code& ec) noexcept
496 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
497 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
498 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
499 const bool use_lstat = create_symlinks || skip_symlinks;
501 file_status f, t;
502 stat_type from_st, to_st;
503 // _GLIBCXX_RESOLVE_LIB_DEFECTS
504 // 2681. filesystem::copy() cannot copy symlinks
505 if (use_lstat || copy_symlinks
506 ? ::lstat(from.c_str(), &from_st)
507 : ::stat(from.c_str(), &from_st))
509 ec.assign(errno, std::generic_category());
510 return;
512 if (use_lstat
513 ? ::lstat(to.c_str(), &to_st)
514 : ::stat(to.c_str(), &to_st))
516 if (!is_not_found_errno(errno))
518 ec.assign(errno, std::generic_category());
519 return;
521 t = file_status{file_type::not_found};
523 else
524 t = make_file_status(to_st);
525 f = make_file_status(from_st);
527 if (exists(t) && !is_other(t) && !is_other(f)
528 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
530 ec = std::make_error_code(std::errc::file_exists);
531 return;
533 if (is_other(f) || is_other(t))
535 ec = std::make_error_code(std::errc::not_supported);
536 return;
538 if (is_directory(f) && is_regular_file(t))
540 ec = std::make_error_code(std::errc::is_a_directory);
541 return;
544 if (is_symlink(f))
546 if (skip_symlinks)
547 ec.clear();
548 else if (!exists(t) && copy_symlinks)
549 copy_symlink(from, to, ec);
550 else
551 // Not clear what should be done here.
552 // "Otherwise report an error as specified in Error reporting (7)."
553 ec = std::make_error_code(std::errc::invalid_argument);
555 else if (is_regular_file(f))
557 if (is_set(options, copy_options::directories_only))
558 ec.clear();
559 else if (create_symlinks)
560 create_symlink(from, to, ec);
561 else if (is_set(options, copy_options::create_hard_links))
562 create_hard_link(from, to, ec);
563 else if (is_directory(t))
564 do_copy_file(from, to / from.filename(), options, &from_st, 0, ec);
565 else
567 auto ptr = exists(t) ? &to_st : &from_st;
568 do_copy_file(from, to, options, &from_st, ptr, ec);
571 // _GLIBCXX_RESOLVE_LIB_DEFECTS
572 // 2682. filesystem::copy() won't create a symlink to a directory
573 else if (is_directory(f) && create_symlinks)
574 ec = std::make_error_code(errc::is_a_directory);
575 else if (is_directory(f) && (is_set(options, copy_options::recursive)
576 || options == copy_options::none))
578 if (!exists(t))
579 if (!create_directory(to, from, ec))
580 return;
581 // set an unused bit in options to disable further recursion
582 if (!is_set(options, copy_options::recursive))
583 options |= static_cast<copy_options>(4096);
584 for (const directory_entry& x : directory_iterator(from))
585 copy(x.path(), to/x.path().filename(), options, ec);
587 // _GLIBCXX_RESOLVE_LIB_DEFECTS
588 // 2683. filesystem::copy() says "no effects"
589 else
590 ec.clear();
593 bool
594 fs::copy_file(const path& from, const path& to, copy_options option)
596 error_code ec;
597 bool result = copy_file(from, to, option, ec);
598 if (ec.value())
599 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
600 ec));
601 return result;
604 bool
605 fs::copy_file(const path& from, const path& to, copy_options option,
606 error_code& ec) noexcept
608 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
609 return do_copy_file(from, to, option, nullptr, nullptr, ec);
610 #else
611 ec = std::make_error_code(std::errc::not_supported);
612 return false;
613 #endif
617 void
618 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
620 error_code ec;
621 copy_symlink(existing_symlink, new_symlink, ec);
622 if (ec.value())
623 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
624 existing_symlink, new_symlink, ec));
627 void
628 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
629 error_code& ec) noexcept
631 auto p = read_symlink(existing_symlink, ec);
632 if (ec.value())
633 return;
634 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
635 if (is_directory(p))
637 create_directory_symlink(p, new_symlink, ec);
638 return;
640 #endif
641 create_symlink(p, new_symlink, ec);
645 bool
646 fs::create_directories(const path& p)
648 error_code ec;
649 bool result = create_directories(p, ec);
650 if (ec.value())
651 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
652 ec));
653 return result;
656 bool
657 fs::create_directories(const path& p, error_code& ec) noexcept
659 if (p.empty())
661 ec = std::make_error_code(errc::invalid_argument);
662 return false;
664 std::stack<path> missing;
665 path pp = p;
667 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
669 ec.clear();
670 const auto& filename = pp.filename();
671 if (!is_dot(filename) && !is_dotdot(filename))
672 missing.push(pp);
673 pp.remove_filename();
676 if (ec || missing.empty())
677 return false;
681 const path& top = missing.top();
682 create_directory(top, ec);
683 if (ec && is_directory(top))
684 ec.clear();
685 missing.pop();
687 while (!missing.empty() && !ec);
689 return missing.empty();
692 namespace
694 bool
695 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
697 bool created = false;
698 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
699 ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
700 if (::mkdir(p.c_str(), mode))
702 const int err = errno;
703 if (err != EEXIST || !is_directory(p))
704 ec.assign(err, std::generic_category());
705 else
706 ec.clear();
708 else
710 ec.clear();
711 created = true;
713 #else
714 ec = std::make_error_code(std::errc::not_supported);
715 #endif
716 return created;
718 } // namespace
720 bool
721 fs::create_directory(const path& p)
723 error_code ec;
724 bool result = create_directory(p, ec);
725 if (ec.value())
726 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
727 ec));
728 return result;
731 bool
732 fs::create_directory(const path& p, error_code& ec) noexcept
734 return create_dir(p, perms::all, ec);
738 bool
739 fs::create_directory(const path& p, const path& attributes)
741 error_code ec;
742 bool result = create_directory(p, attributes, ec);
743 if (ec.value())
744 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
745 ec));
746 return result;
749 bool
750 fs::create_directory(const path& p, const path& attributes,
751 error_code& ec) noexcept
753 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
754 stat_type st;
755 if (::stat(attributes.c_str(), &st))
757 ec.assign(errno, std::generic_category());
758 return false;
760 return create_dir(p, static_cast<perms>(st.st_mode), ec);
761 #else
762 ec = std::make_error_code(std::errc::not_supported);
763 return false;
764 #endif
768 void
769 fs::create_directory_symlink(const path& to, const path& new_symlink)
771 error_code ec;
772 create_directory_symlink(to, new_symlink, ec);
773 if (ec.value())
774 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
775 to, new_symlink, ec));
778 void
779 fs::create_directory_symlink(const path& to, const path& new_symlink,
780 error_code& ec) noexcept
782 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
783 ec = std::make_error_code(std::errc::not_supported);
784 #else
785 create_symlink(to, new_symlink, ec);
786 #endif
790 void
791 fs::create_hard_link(const path& to, const path& new_hard_link)
793 error_code ec;
794 create_hard_link(to, new_hard_link, ec);
795 if (ec.value())
796 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
797 to, new_hard_link, ec));
800 void
801 fs::create_hard_link(const path& to, const path& new_hard_link,
802 error_code& ec) noexcept
804 #ifdef _GLIBCXX_HAVE_UNISTD_H
805 if (::link(to.c_str(), new_hard_link.c_str()))
806 ec.assign(errno, std::generic_category());
807 else
808 ec.clear();
809 #else
810 ec = std::make_error_code(std::errc::not_supported);
811 #endif
814 void
815 fs::create_symlink(const path& to, const path& new_symlink)
817 error_code ec;
818 create_symlink(to, new_symlink, ec);
819 if (ec.value())
820 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
821 to, new_symlink, ec));
824 void
825 fs::create_symlink(const path& to, const path& new_symlink,
826 error_code& ec) noexcept
828 #ifdef _GLIBCXX_HAVE_UNISTD_H
829 if (::symlink(to.c_str(), new_symlink.c_str()))
830 ec.assign(errno, std::generic_category());
831 else
832 ec.clear();
833 #else
834 ec = std::make_error_code(std::errc::not_supported);
835 #endif
839 fs::path
840 fs::current_path()
842 error_code ec;
843 path p = current_path(ec);
844 if (ec.value())
845 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
846 return p;
849 fs::path
850 fs::current_path(error_code& ec)
852 path p;
853 #ifdef _GLIBCXX_HAVE_UNISTD_H
854 #ifdef __GLIBC__
855 if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
857 p.assign(cwd.get());
858 ec.clear();
860 else
861 ec.assign(errno, std::generic_category());
862 #else
863 long path_max = pathconf(".", _PC_PATH_MAX);
864 size_t size;
865 if (path_max == -1)
866 size = 1024;
867 else if (path_max > 10240)
868 size = 10240;
869 else
870 size = path_max;
871 for (char_ptr buf; p.empty(); size *= 2)
873 buf.reset((char*)malloc(size));
874 if (buf)
876 if (getcwd(buf.get(), size))
878 p.assign(buf.get());
879 ec.clear();
881 else if (errno != ERANGE)
883 ec.assign(errno, std::generic_category());
884 return {};
887 else
889 ec = std::make_error_code(std::errc::not_enough_memory);
890 return {};
893 #endif // __GLIBC__
894 #else // _GLIBCXX_HAVE_UNISTD_H
895 ec = std::make_error_code(std::errc::not_supported);
896 #endif
897 return p;
900 void
901 fs::current_path(const path& p)
903 error_code ec;
904 current_path(p, ec);
905 if (ec.value())
906 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
909 void
910 fs::current_path(const path& p, error_code& ec) noexcept
912 #ifdef _GLIBCXX_HAVE_UNISTD_H
913 if (::chdir(p.c_str()))
914 ec.assign(errno, std::generic_category());
915 else
916 ec.clear();
917 #else
918 ec = std::make_error_code(std::errc::not_supported);
919 #endif
922 bool
923 fs::equivalent(const path& p1, const path& p2)
925 error_code ec;
926 auto result = equivalent(p1, p2, ec);
927 if (ec)
928 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
929 p1, p2, ec));
930 return result;
933 bool
934 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
936 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
937 int err = 0;
938 file_status s1, s2;
939 stat_type st1, st2;
940 if (::stat(p1.c_str(), &st1) == 0)
941 s1 = make_file_status(st1);
942 else if (is_not_found_errno(errno))
943 s1.type(file_type::not_found);
944 else
945 err = errno;
947 if (::stat(p2.c_str(), &st2) == 0)
948 s2 = make_file_status(st2);
949 else if (is_not_found_errno(errno))
950 s2.type(file_type::not_found);
951 else
952 err = errno;
954 if (exists(s1) && exists(s2))
956 if (is_other(s1) && is_other(s2))
958 ec = std::make_error_code(std::errc::not_supported);
959 return false;
961 ec.clear();
962 if (is_other(s1) || is_other(s2))
963 return false;
964 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
966 else if (!exists(s1) && !exists(s2))
967 ec = std::make_error_code(std::errc::no_such_file_or_directory);
968 else if (err)
969 ec.assign(err, std::generic_category());
970 else
971 ec.clear();
972 return false;
973 #else
974 ec = std::make_error_code(std::errc::not_supported);
975 #endif
976 return false;
979 std::uintmax_t
980 fs::file_size(const path& p)
982 error_code ec;
983 auto sz = file_size(p, ec);
984 if (ec.value())
985 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
986 return sz;
989 namespace
991 template<typename Accessor, typename T>
992 inline T
993 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
995 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
996 stat_type st;
997 if (::stat(p.c_str(), &st))
999 ec.assign(errno, std::generic_category());
1000 return deflt;
1002 ec.clear();
1003 return f(st);
1004 #else
1005 ec = std::make_error_code(std::errc::not_supported);
1006 return deflt;
1007 #endif
1011 std::uintmax_t
1012 fs::file_size(const path& p, error_code& ec) noexcept
1014 struct S
1016 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
1017 S() : type(file_type::not_found) { }
1018 file_type type;
1019 size_t size;
1021 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
1022 if (s.type == file_type::regular)
1023 return s.size;
1024 if (!ec)
1026 if (s.type == file_type::directory)
1027 ec = std::make_error_code(std::errc::is_a_directory);
1028 else
1029 ec = std::make_error_code(std::errc::not_supported);
1031 return -1;
1034 std::uintmax_t
1035 fs::hard_link_count(const path& p)
1037 error_code ec;
1038 auto count = hard_link_count(p, ec);
1039 if (ec.value())
1040 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
1041 return count;
1044 std::uintmax_t
1045 fs::hard_link_count(const path& p, error_code& ec) noexcept
1047 return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
1048 static_cast<uintmax_t>(-1));
1051 bool
1052 fs::is_empty(const path& p)
1054 error_code ec;
1055 bool e = is_empty(p, ec);
1056 if (ec)
1057 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check is file is empty",
1058 p, ec));
1059 return e;
1062 bool
1063 fs::is_empty(const path& p, error_code& ec) noexcept
1065 auto s = status(p, ec);
1066 if (ec)
1067 return false;
1068 bool empty = fs::is_directory(s)
1069 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1070 : fs::file_size(p, ec) == 0;
1071 return ec ? false : empty;
1074 fs::file_time_type
1075 fs::last_write_time(const path& p)
1077 error_code ec;
1078 auto t = last_write_time(p, ec);
1079 if (ec.value())
1080 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1081 return t;
1084 fs::file_time_type
1085 fs::last_write_time(const path& p, error_code& ec) noexcept
1087 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
1088 file_time_type::min());
1091 void
1092 fs::last_write_time(const path& p, file_time_type new_time)
1094 error_code ec;
1095 last_write_time(p, new_time, ec);
1096 if (ec.value())
1097 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1100 void
1101 fs::last_write_time(const path& p __attribute__((__unused__)),
1102 file_time_type new_time, error_code& ec) noexcept
1104 auto d = new_time.time_since_epoch();
1105 auto s = chrono::duration_cast<chrono::seconds>(d);
1106 #if _GLIBCXX_USE_UTIMENSAT
1107 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1108 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1110 --s;
1111 ns += chrono::seconds(1);
1113 struct ::timespec ts[2];
1114 ts[0].tv_sec = 0;
1115 ts[0].tv_nsec = UTIME_OMIT;
1116 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1117 ts[1].tv_nsec = static_cast<long>(ns.count());
1118 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1119 ec.assign(errno, std::generic_category());
1120 else
1121 ec.clear();
1122 #elif _GLIBCXX_HAVE_UTIME_H
1123 ::utimbuf times;
1124 times.modtime = s.count();
1125 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1126 times.modtime);
1127 if (::utime(p.c_str(), &times))
1128 ec.assign(errno, std::generic_category());
1129 else
1130 ec.clear();
1131 #else
1132 ec = std::make_error_code(std::errc::not_supported);
1133 #endif
1136 void
1137 fs::permissions(const path& p, perms prms)
1139 error_code ec;
1140 permissions(p, prms, ec);
1141 if (ec.value())
1142 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1145 void
1146 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
1148 const bool add = is_set(prms, perms::add_perms);
1149 const bool remove = is_set(prms, perms::remove_perms);
1150 const bool nofollow = is_set(prms, perms::symlink_nofollow);
1151 if (add && remove)
1153 ec = std::make_error_code(std::errc::invalid_argument);
1154 return;
1157 prms &= perms::mask;
1159 file_status st;
1160 if (add || remove || nofollow)
1162 st = nofollow ? symlink_status(p, ec) : status(p, ec);
1163 if (ec)
1164 return;
1165 auto curr = st.permissions();
1166 if (add)
1167 prms |= curr;
1168 else if (remove)
1169 prms = curr & ~prms;
1172 int err = 0;
1173 #if _GLIBCXX_USE_FCHMODAT
1174 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1175 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1176 err = errno;
1177 #else
1178 if (nofollow && is_symlink(st))
1179 ec = std::make_error_code(std::errc::operation_not_supported);
1180 else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
1181 err = errno;
1182 #endif
1184 if (err)
1185 ec.assign(err, std::generic_category());
1186 else
1187 ec.clear();
1190 fs::path
1191 fs::read_symlink(const path& p)
1193 error_code ec;
1194 path tgt = read_symlink(p, ec);
1195 if (ec.value())
1196 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1197 return tgt;
1200 fs::path fs::read_symlink(const path& p, error_code& ec)
1202 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1203 stat_type st;
1204 if (::lstat(p.c_str(), &st))
1206 ec.assign(errno, std::generic_category());
1207 return {};
1209 std::string buf(st.st_size, '\0');
1210 ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size());
1211 if (len == -1)
1213 ec.assign(errno, std::generic_category());
1214 return {};
1216 ec.clear();
1217 return path{buf.data(), buf.data()+len};
1218 #else
1219 ec = std::make_error_code(std::errc::not_supported);
1220 return {};
1221 #endif
1225 bool
1226 fs::remove(const path& p)
1228 error_code ec;
1229 bool result = fs::remove(p, ec);
1230 if (ec.value())
1231 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1232 return result;
1235 bool
1236 fs::remove(const path& p, error_code& ec) noexcept
1238 if (exists(symlink_status(p, ec)))
1240 if (::remove(p.c_str()) == 0)
1242 ec.clear();
1243 return true;
1245 else
1246 ec.assign(errno, std::generic_category());
1248 return false;
1252 std::uintmax_t
1253 fs::remove_all(const path& p)
1255 error_code ec;
1256 bool result = remove_all(p, ec);
1257 if (ec.value())
1258 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1259 return result;
1262 std::uintmax_t
1263 fs::remove_all(const path& p, error_code& ec) noexcept
1265 auto fs = symlink_status(p, ec);
1266 uintmax_t count = 0;
1267 if (ec.value() == 0 && fs.type() == file_type::directory)
1268 for (directory_iterator d(p, ec), end; ec.value() == 0 && d != end; ++d)
1269 count += fs::remove_all(d->path(), ec);
1270 if (ec.value())
1271 return -1;
1272 return fs::remove(p, ec) ? ++count : -1; // fs:remove() calls ec.clear()
1275 void
1276 fs::rename(const path& from, const path& to)
1278 error_code ec;
1279 rename(from, to, ec);
1280 if (ec.value())
1281 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1284 void
1285 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1287 if (::rename(from.c_str(), to.c_str()))
1288 ec.assign(errno, std::generic_category());
1289 else
1290 ec.clear();
1293 void
1294 fs::resize_file(const path& p, uintmax_t size)
1296 error_code ec;
1297 resize_file(p, size, ec);
1298 if (ec.value())
1299 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1302 void
1303 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1305 #ifdef _GLIBCXX_HAVE_UNISTD_H
1306 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1307 ec.assign(EINVAL, std::generic_category());
1308 else if (::truncate(p.c_str(), size))
1309 ec.assign(errno, std::generic_category());
1310 else
1311 ec.clear();
1312 #else
1313 ec = std::make_error_code(std::errc::not_supported);
1314 #endif
1318 fs::space_info
1319 fs::space(const path& p)
1321 error_code ec;
1322 space_info s = space(p, ec);
1323 if (ec.value())
1324 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1325 return s;
1328 fs::space_info
1329 fs::space(const path& p, error_code& ec) noexcept
1331 space_info info = {
1332 static_cast<uintmax_t>(-1),
1333 static_cast<uintmax_t>(-1),
1334 static_cast<uintmax_t>(-1)
1336 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1337 struct ::statvfs f;
1338 if (::statvfs(p.c_str(), &f))
1339 ec.assign(errno, std::generic_category());
1340 else
1342 info = space_info{
1343 f.f_blocks * f.f_frsize,
1344 f.f_bfree * f.f_frsize,
1345 f.f_bavail * f.f_frsize
1347 ec.clear();
1349 #else
1350 ec = std::make_error_code(std::errc::not_supported);
1351 #endif
1352 return info;
1355 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1356 fs::file_status
1357 fs::status(const fs::path& p, error_code& ec) noexcept
1359 file_status status;
1360 stat_type st;
1361 if (::stat(p.c_str(), &st))
1363 int err = errno;
1364 ec.assign(err, std::generic_category());
1365 if (is_not_found_errno(err))
1366 status.type(file_type::not_found);
1367 #ifdef EOVERFLOW
1368 else if (err == EOVERFLOW)
1369 status.type(file_type::unknown);
1370 #endif
1372 else
1374 status = make_file_status(st);
1375 ec.clear();
1377 return status;
1380 fs::file_status
1381 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1383 file_status status;
1384 stat_type st;
1385 if (::lstat(p.c_str(), &st))
1387 int err = errno;
1388 ec.assign(err, std::generic_category());
1389 if (is_not_found_errno(err))
1390 status.type(file_type::not_found);
1392 else
1394 status = make_file_status(st);
1395 ec.clear();
1397 return status;
1399 #endif
1401 fs::file_status
1402 fs::status(const fs::path& p)
1404 std::error_code ec;
1405 auto result = status(p, ec);
1406 if (result.type() == file_type::none)
1407 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1408 return result;
1411 fs::file_status
1412 fs::symlink_status(const fs::path& p)
1414 std::error_code ec;
1415 auto result = symlink_status(p, ec);
1416 if (result.type() == file_type::none)
1417 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1418 return result;
1421 fs::path
1422 fs::system_complete(const path& p)
1424 error_code ec;
1425 path comp = system_complete(p, ec);
1426 if (ec.value())
1427 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1428 return comp;
1431 fs::path
1432 fs::system_complete(const path& p, error_code& ec)
1434 path base = current_path(ec);
1435 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1436 if (p.is_absolute() || !p.has_root_name()
1437 || p.root_name() == base.root_name())
1438 return absolute(p, base);
1439 // else TODO
1440 ec = std::make_error_code(std::errc::not_supported);
1441 return {};
1442 #else
1443 if (ec.value())
1444 return {};
1445 return absolute(p, base);
1446 #endif
1449 fs::path fs::temp_directory_path()
1451 error_code ec;
1452 path tmp = temp_directory_path(ec);
1453 if (ec.value())
1454 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1455 return tmp;
1458 fs::path fs::temp_directory_path(error_code& ec)
1460 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1461 ec = std::make_error_code(std::errc::not_supported);
1462 return {}; // TODO
1463 #else
1464 const char* tmpdir = nullptr;
1465 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1466 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1467 tmpdir = ::getenv(*e);
1468 path p = tmpdir ? tmpdir : "/tmp";
1469 auto st = status(p, ec);
1470 if (!ec)
1472 if (is_directory(st))
1474 ec.clear();
1475 return p;
1477 else
1478 ec = std::make_error_code(std::errc::not_a_directory);
1480 return {};
1481 #endif