libstdc++/71037 Add base path to filesystem::canonical exceptions
[official-gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
blobe18c7510c4170b54c9e1a83c6abee105247f61c7
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 <stack>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <limits.h> // PATH_MAX
36 #ifdef _GLIBCXX_HAVE_UNISTD_H
37 # include <unistd.h>
38 # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H)
39 # include <sys/types.h>
40 # include <sys/stat.h>
41 # endif
42 #endif
43 #ifdef _GLIBCXX_HAVE_FCNTL_H
44 # include <fcntl.h>
45 #endif
46 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h>
48 #endif
49 #ifdef _GLIBCXX_USE_SENDFILE
50 # include <sys/sendfile.h>
51 #else
52 # include <ext/stdio_filebuf.h>
53 # include <ostream>
54 #endif
55 #if _GLIBCXX_HAVE_UTIME_H
56 # include <utime.h>
57 #endif
59 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
60 # undef utime
61 # define utime _wutime
62 # undef chmod
63 # define chmod _wchmod
64 #endif
66 namespace fs = std::experimental::filesystem;
68 fs::path
69 fs::absolute(const path& p, const path& base)
71 const bool has_root_dir = p.has_root_directory();
72 const bool has_root_name = p.has_root_name();
73 path abs;
74 if (has_root_dir && has_root_name)
75 abs = p;
76 else
78 abs = base.is_absolute() ? base : absolute(base);
79 if (has_root_dir)
80 abs = abs.root_name() / p;
81 else if (has_root_name)
82 abs = p.root_name() / abs.root_directory() / abs.relative_path()
83 / p.relative_path();
84 else
85 abs = abs / p;
87 return abs;
90 namespace
92 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
93 inline bool is_dot(wchar_t c) { return c == L'.'; }
94 #else
95 inline bool is_dot(char c) { return c == '.'; }
96 #endif
98 inline bool is_dot(const fs::path& path)
100 const auto& filename = path.native();
101 return filename.size() == 1 && is_dot(filename[0]);
104 inline bool is_dotdot(const fs::path& path)
106 const auto& filename = path.native();
107 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
110 struct free_as_in_malloc
112 void operator()(void* p) const { ::free(p); }
115 using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
118 fs::path
119 fs::canonical(const path& p, const path& base, error_code& ec)
121 const path pa = absolute(p, base);
122 path result;
124 #ifdef _GLIBCXX_USE_REALPATH
125 char_ptr buf{ nullptr };
126 # if _XOPEN_VERSION < 700
127 // Not safe to call realpath(path, NULL)
128 buf.reset( (char*)::malloc(PATH_MAX) );
129 # endif
130 if (char* rp = ::realpath(pa.c_str(), buf.get()))
132 if (buf == nullptr)
133 buf.reset(rp);
134 result.assign(rp);
135 ec.clear();
136 return result;
138 if (errno != ENAMETOOLONG)
140 ec.assign(errno, std::generic_category());
141 return result;
143 #endif
145 if (!exists(pa, ec))
146 return result;
147 // else: we know there are (currently) no unresolvable symlink loops
149 result = pa.root_path();
151 deque<path> cmpts;
152 for (auto& f : pa.relative_path())
153 cmpts.push_back(f);
155 int max_allowed_symlinks = 40;
157 while (!cmpts.empty() && !ec)
159 path f = std::move(cmpts.front());
160 cmpts.pop_front();
162 if (is_dot(f))
164 if (!is_directory(result, ec) && !ec)
165 ec.assign(ENOTDIR, std::generic_category());
167 else if (is_dotdot(f))
169 auto parent = result.parent_path();
170 if (parent.empty())
171 result = pa.root_path();
172 else
173 result.swap(parent);
175 else
177 result /= f;
179 if (is_symlink(result, ec))
181 path link = read_symlink(result, ec);
182 if (!ec)
184 if (--max_allowed_symlinks == 0)
185 ec.assign(ELOOP, std::generic_category());
186 else
188 if (link.is_absolute())
190 result = link.root_path();
191 link = link.relative_path();
193 else
194 result.remove_filename();
196 cmpts.insert(cmpts.begin(), link.begin(), link.end());
203 if (ec || !exists(result, ec))
204 result.clear();
206 return result;
209 fs::path
210 fs::canonical(const path& p, error_code& ec)
212 path cur = current_path(ec);
213 if (ec.value())
214 return {};
215 return canonical(p, cur, ec);
218 fs::path
219 fs::canonical(const path& p, const path& base)
221 error_code ec;
222 path can = canonical(p, base, ec);
223 if (ec)
224 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
225 ec));
226 return can;
229 void
230 fs::copy(const path& from, const path& to, copy_options options)
232 error_code ec;
233 copy(from, to, options, ec);
234 if (ec.value())
235 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
238 namespace
240 template<typename Bitmask>
241 inline bool is_set(Bitmask obj, Bitmask bits)
243 return (obj & bits) != Bitmask::none;
247 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
248 namespace
250 typedef struct ::stat stat_type;
252 inline fs::file_type
253 make_file_type(const stat_type& st) noexcept
255 using fs::file_type;
256 #ifdef _GLIBCXX_HAVE_S_ISREG
257 if (S_ISREG(st.st_mode))
258 return file_type::regular;
259 else if (S_ISDIR(st.st_mode))
260 return file_type::directory;
261 else if (S_ISCHR(st.st_mode))
262 return file_type::character;
263 else if (S_ISBLK(st.st_mode))
264 return file_type::block;
265 else if (S_ISFIFO(st.st_mode))
266 return file_type::fifo;
267 else if (S_ISLNK(st.st_mode))
268 return file_type::symlink;
269 else if (S_ISSOCK(st.st_mode))
270 return file_type::socket;
271 #endif
272 return file_type::unknown;
276 inline fs::file_status
277 make_file_status(const stat_type& st) noexcept
279 return fs::file_status{
280 make_file_type(st),
281 static_cast<fs::perms>(st.st_mode) & fs::perms::mask
285 inline bool
286 is_not_found_errno(int err) noexcept
288 return err == ENOENT || err == ENOTDIR;
291 inline fs::file_time_type
292 file_time(const stat_type& st) noexcept
294 using namespace std::chrono;
295 return fs::file_time_type{
296 #ifdef _GLIBCXX_USE_ST_MTIM
297 seconds{st.st_mtim.tv_sec} + nanoseconds{st.st_mtim.tv_nsec}
298 #else
299 seconds{st.st_mtime}
300 #endif
304 // Returns true if the file descriptor was successfully closed,
305 // otherwise returns false and the reason will be in errno.
306 inline bool
307 close_fd(int fd)
309 while (::close(fd))
310 if (errno != EINTR)
311 return false;
312 return true;
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);
358 using opts = fs::copy_options;
360 if (exists(t))
362 if (!is_other(t) && !is_other(f)
363 && to_st->st_dev == from_st->st_dev
364 && to_st->st_ino == from_st->st_ino)
366 ec = std::make_error_code(std::errc::file_exists);
367 return false;
370 if (is_set(option, opts::skip_existing))
372 ec.clear();
373 return false;
375 else if (is_set(option, opts::update_existing))
377 if (file_time(*from_st) <= file_time(*to_st))
379 ec.clear();
380 return false;
383 else if (!is_set(option, opts::overwrite_existing))
385 ec = std::make_error_code(std::errc::file_exists);
386 return false;
390 struct CloseFD {
391 ~CloseFD() { if (fd != -1) close_fd(fd); }
392 bool close() { return close_fd(std::exchange(fd, -1)); }
393 int fd;
396 CloseFD in = { ::open(from.c_str(), O_RDONLY) };
397 if (in.fd == -1)
399 ec.assign(errno, std::generic_category());
400 return false;
402 int oflag = O_WRONLY|O_CREAT;
403 if (is_set(option, opts::overwrite_existing|opts::update_existing))
404 oflag |= O_TRUNC;
405 else
406 oflag |= O_EXCL;
407 CloseFD out = { ::open(to.c_str(), oflag, S_IWUSR) };
408 if (out.fd == -1)
410 if (errno == EEXIST && is_set(option, opts::skip_existing))
411 ec.clear();
412 else
413 ec.assign(errno, std::generic_category());
414 return false;
417 #ifdef _GLIBCXX_USE_FCHMOD
418 if (::fchmod(out.fd, from_st->st_mode))
419 #elif _GLIBCXX_USE_FCHMODAT
420 if (::fchmodat(AT_FDCWD, to.c_str(), from_st->st_mode, 0))
421 #else
422 if (::chmod(to.c_str(), from_st->st_mode))
423 #endif
425 ec.assign(errno, std::generic_category());
426 return false;
429 #ifdef _GLIBCXX_USE_SENDFILE
430 const auto n = ::sendfile(out.fd, in.fd, nullptr, from_st->st_size);
431 if (n != from_st->st_size)
433 ec.assign(errno, std::generic_category());
434 return false;
436 if (!out.close() || !in.close())
438 ec.assign(errno, std::generic_category());
439 return false;
441 #else
442 __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in);
443 __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out);
444 if (sbin.is_open())
445 in.fd = -1;
446 if (sbout.is_open())
447 out.fd = -1;
448 if (from_st->st_size && !(std::ostream(&sbout) << &sbin))
450 ec = std::make_error_code(std::errc::io_error);
451 return false;
453 if (sbout.close() || sbin.close())
455 ec.assign(errno, std::generic_category());
456 return false;
458 #endif
460 ec.clear();
461 return true;
464 #endif
466 void
467 fs::copy(const path& from, const path& to, copy_options options,
468 error_code& ec) noexcept
470 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
471 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
472 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
473 const bool use_lstat = create_symlinks || skip_symlinks;
475 file_status f, t;
476 stat_type from_st, to_st;
477 // N4099 doesn't check copy_symlinks here, but I think that's a defect.
478 if (use_lstat || copy_symlinks
479 ? ::lstat(from.c_str(), &from_st)
480 : ::stat(from.c_str(), &from_st))
482 ec.assign(errno, std::generic_category());
483 return;
485 if (use_lstat
486 ? ::lstat(to.c_str(), &to_st)
487 : ::stat(to.c_str(), &to_st))
489 if (!is_not_found_errno(errno))
491 ec.assign(errno, std::generic_category());
492 return;
494 t = file_status{file_type::not_found};
496 else
497 t = make_file_status(to_st);
498 f = make_file_status(from_st);
500 if (exists(t) && !is_other(t) && !is_other(f)
501 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
503 ec = std::make_error_code(std::errc::file_exists);
504 return;
506 if (is_other(f) || is_other(t))
508 ec = std::make_error_code(std::errc::not_supported);
509 return;
511 if (is_directory(f) && is_regular_file(t))
513 ec = std::make_error_code(std::errc::is_a_directory);
514 return;
517 if (is_symlink(f))
519 if (skip_symlinks)
520 ec.clear();
521 else if (!exists(t) && copy_symlinks)
522 copy_symlink(from, to, ec);
523 else
524 // Not clear what should be done here.
525 // "Otherwise report an error as specified in Error reporting (7)."
526 ec = std::make_error_code(std::errc::invalid_argument);
528 else if (is_regular_file(f))
530 if (is_set(options, copy_options::directories_only))
531 ec.clear();
532 else if (create_symlinks)
533 create_symlink(from, to, ec);
534 else if (is_set(options, copy_options::create_hard_links))
535 create_hard_link(from, to, ec);
536 else if (is_directory(t))
537 do_copy_file(from, to / from.filename(), options, &from_st, 0, ec);
538 else
540 auto ptr = exists(t) ? &to_st : &from_st;
541 do_copy_file(from, to, options, &from_st, ptr, ec);
544 else if (is_directory(f) && (is_set(options, copy_options::recursive)
545 || options == copy_options::none))
547 if (!exists(t))
548 if (!create_directory(to, from, ec))
549 return;
550 // set an unused bit in options to disable further recursion
551 if (!is_set(options, copy_options::recursive))
552 options |= static_cast<copy_options>(4096);
553 for (const directory_entry& x : directory_iterator(from))
554 copy(x.path(), to/x.path().filename(), options, ec);
556 // "Otherwise no effects." (should ec.clear() be called?)
559 bool
560 fs::copy_file(const path& from, const path& to, copy_options option)
562 error_code ec;
563 bool result = copy_file(from, to, option, ec);
564 if (ec.value())
565 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
566 ec));
567 return result;
570 bool
571 fs::copy_file(const path& from, const path& to, copy_options option,
572 error_code& ec) noexcept
574 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
575 return do_copy_file(from, to, option, nullptr, nullptr, ec);
576 #else
577 ec = std::make_error_code(std::errc::not_supported);
578 return false;
579 #endif
583 void
584 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
586 error_code ec;
587 copy_symlink(existing_symlink, new_symlink, ec);
588 if (ec.value())
589 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
590 existing_symlink, new_symlink, ec));
593 void
594 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
595 error_code& ec) noexcept
597 auto p = read_symlink(existing_symlink, ec);
598 if (ec.value())
599 return;
600 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
601 if (is_directory(p))
603 create_directory_symlink(p, new_symlink, ec);
604 return;
606 #endif
607 create_symlink(p, new_symlink, ec);
611 bool
612 fs::create_directories(const path& p)
614 error_code ec;
615 bool result = create_directories(p, ec);
616 if (ec.value())
617 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
618 ec));
619 return result;
622 bool
623 fs::create_directories(const path& p, error_code& ec) noexcept
625 if (p.empty())
627 ec = std::make_error_code(errc::invalid_argument);
628 return false;
630 std::stack<path> missing;
631 path pp = p;
633 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
635 ec.clear();
636 const auto& filename = pp.filename();
637 if (!is_dot(filename) && !is_dotdot(filename))
638 missing.push(pp);
639 pp.remove_filename();
642 if (ec || missing.empty())
643 return false;
647 const path& top = missing.top();
648 create_directory(top, ec);
649 if (ec && is_directory(top))
650 ec.clear();
651 missing.pop();
653 while (!missing.empty() && !ec);
655 return missing.empty();
658 namespace
660 bool
661 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
663 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
664 ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
665 if (::mkdir(p.c_str(), mode))
667 ec.assign(errno, std::generic_category());
668 return false;
670 else
672 ec.clear();
673 return true;
675 #else
676 ec = std::make_error_code(std::errc::not_supported);
677 return false;
678 #endif
680 } // namespace
682 bool
683 fs::create_directory(const path& p)
685 error_code ec;
686 bool result = create_directory(p, ec);
687 if (ec.value())
688 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
689 ec));
690 return result;
693 bool
694 fs::create_directory(const path& p, error_code& ec) noexcept
696 return create_dir(p, perms::all, ec);
700 bool
701 fs::create_directory(const path& p, const path& attributes)
703 error_code ec;
704 bool result = create_directory(p, attributes, ec);
705 if (ec.value())
706 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
707 ec));
708 return result;
711 bool
712 fs::create_directory(const path& p, const path& attributes,
713 error_code& ec) noexcept
715 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
716 stat_type st;
717 if (::stat(attributes.c_str(), &st))
719 ec.assign(errno, std::generic_category());
720 return false;
722 return create_dir(p, static_cast<perms>(st.st_mode), ec);
723 #else
724 ec = std::make_error_code(std::errc::not_supported);
725 return false;
726 #endif
730 void
731 fs::create_directory_symlink(const path& to, const path& new_symlink)
733 error_code ec;
734 create_directory_symlink(to, new_symlink, ec);
735 if (ec.value())
736 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
737 to, new_symlink, ec));
740 void
741 fs::create_directory_symlink(const path& to, const path& new_symlink,
742 error_code& ec) noexcept
744 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
745 ec = std::make_error_code(std::errc::not_supported);
746 #else
747 create_symlink(to, new_symlink, ec);
748 #endif
752 void
753 fs::create_hard_link(const path& to, const path& new_hard_link)
755 error_code ec;
756 create_hard_link(to, new_hard_link, ec);
757 if (ec.value())
758 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
759 to, new_hard_link, ec));
762 void
763 fs::create_hard_link(const path& to, const path& new_hard_link,
764 error_code& ec) noexcept
766 #ifdef _GLIBCXX_HAVE_UNISTD_H
767 if (::link(to.c_str(), new_hard_link.c_str()))
768 ec.assign(errno, std::generic_category());
769 else
770 ec.clear();
771 #else
772 ec = std::make_error_code(std::errc::not_supported);
773 #endif
776 void
777 fs::create_symlink(const path& to, const path& new_symlink)
779 error_code ec;
780 create_symlink(to, new_symlink, ec);
781 if (ec.value())
782 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
783 to, new_symlink, ec));
786 void
787 fs::create_symlink(const path& to, const path& new_symlink,
788 error_code& ec) noexcept
790 #ifdef _GLIBCXX_HAVE_UNISTD_H
791 if (::symlink(to.c_str(), new_symlink.c_str()))
792 ec.assign(errno, std::generic_category());
793 else
794 ec.clear();
795 #else
796 ec = std::make_error_code(std::errc::not_supported);
797 #endif
801 fs::path
802 fs::current_path()
804 error_code ec;
805 path p = current_path(ec);
806 if (ec.value())
807 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
808 return p;
811 fs::path
812 fs::current_path(error_code& ec)
814 path p;
815 #ifdef _GLIBCXX_HAVE_UNISTD_H
816 #ifdef __GLIBC__
817 if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
819 p.assign(cwd.get());
820 ec.clear();
822 else
823 ec.assign(errno, std::generic_category());
824 #else
825 long path_max = pathconf(".", _PC_PATH_MAX);
826 size_t size;
827 if (path_max == -1)
828 size = 1024;
829 else if (path_max > 10240)
830 size = 10240;
831 else
832 size = path_max;
833 for (char_ptr buf; p.empty(); size *= 2)
835 buf.reset((char*)malloc(size));
836 if (buf)
838 if (getcwd(buf.get(), size))
840 p.assign(buf.get());
841 ec.clear();
843 else if (errno != ERANGE)
845 ec.assign(errno, std::generic_category());
846 return {};
849 else
851 ec = std::make_error_code(std::errc::not_enough_memory);
852 return {};
855 #endif // __GLIBC__
856 #else // _GLIBCXX_HAVE_UNISTD_H
857 ec = std::make_error_code(std::errc::not_supported);
858 #endif
859 return p;
862 void
863 fs::current_path(const path& p)
865 error_code ec;
866 current_path(p, ec);
867 if (ec.value())
868 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
871 void
872 fs::current_path(const path& p, error_code& ec) noexcept
874 #ifdef _GLIBCXX_HAVE_UNISTD_H
875 if (::chdir(p.c_str()))
876 ec.assign(errno, std::generic_category());
877 else
878 ec.clear();
879 #else
880 ec = std::make_error_code(std::errc::not_supported);
881 #endif
884 bool
885 fs::equivalent(const path& p1, const path& p2)
887 error_code ec;
888 auto result = equivalent(p1, p2, ec);
889 if (ec.value())
890 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
891 p1, p2, ec));
892 return result;
895 bool
896 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
898 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
899 stat_type st1, st2;
900 if (::stat(p1.c_str(), &st1) == 0 && ::stat(p2.c_str(), &st2) == 0)
902 file_status s1 = make_file_status(st1);
903 file_status s2 = make_file_status(st2);
904 if (is_other(s1) && is_other(s2))
906 ec = std::make_error_code(std::errc::not_supported);
907 return false;
909 ec.clear();
910 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
912 else if (is_not_found_errno(errno))
914 ec = std::make_error_code(std::errc::no_such_file_or_directory);
915 return false;
917 ec.assign(errno, std::generic_category());
918 #else
919 ec = std::make_error_code(std::errc::not_supported);
920 #endif
921 return false;
924 std::uintmax_t
925 fs::file_size(const path& p)
927 error_code ec;
928 auto sz = file_size(p, ec);
929 if (ec.value())
930 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
931 return sz;
934 namespace
936 template<typename Accessor, typename T>
937 inline T
938 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
940 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
941 stat_type st;
942 if (::stat(p.c_str(), &st))
944 ec.assign(errno, std::generic_category());
945 return deflt;
947 ec.clear();
948 return f(st);
949 #else
950 ec = std::make_error_code(std::errc::not_supported);
951 return deflt;
952 #endif
956 std::uintmax_t
957 fs::file_size(const path& p, error_code& ec) noexcept
959 struct S
961 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
962 S() : type(file_type::not_found) { }
963 file_type type;
964 size_t size;
966 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
967 if (s.type == file_type::regular)
968 return s.size;
969 if (!ec)
971 if (s.type == file_type::directory)
972 ec = std::make_error_code(std::errc::is_a_directory);
973 else
974 ec = std::make_error_code(std::errc::not_supported);
976 return -1;
979 std::uintmax_t
980 fs::hard_link_count(const path& p)
982 error_code ec;
983 auto count = hard_link_count(p, ec);
984 if (ec.value())
985 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
986 return count;
989 std::uintmax_t
990 fs::hard_link_count(const path& p, error_code& ec) noexcept
992 return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
993 static_cast<uintmax_t>(-1));
996 bool
997 fs::is_empty(const path& p)
999 return fs::is_directory(status(p))
1000 ? fs::directory_iterator(p) == fs::directory_iterator()
1001 : fs::file_size(p) == 0;
1004 bool
1005 fs::is_empty(const path& p, error_code& ec) noexcept
1007 auto s = status(p, ec);
1008 if (ec.value())
1009 return false;
1010 return fs::is_directory(s)
1011 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1012 : fs::file_size(p, ec) == 0;
1015 fs::file_time_type
1016 fs::last_write_time(const path& p)
1018 error_code ec;
1019 auto t = last_write_time(p, ec);
1020 if (ec.value())
1021 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1022 return t;
1025 fs::file_time_type
1026 fs::last_write_time(const path& p, error_code& ec) noexcept
1028 return do_stat(p, ec, [](const auto& st) { return file_time(st); },
1029 file_time_type::min());
1032 void
1033 fs::last_write_time(const path& p, file_time_type new_time)
1035 error_code ec;
1036 last_write_time(p, new_time, ec);
1037 if (ec.value())
1038 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1041 void
1042 fs::last_write_time(const path& p __attribute__((__unused__)),
1043 file_time_type new_time, error_code& ec) noexcept
1045 auto d = new_time.time_since_epoch();
1046 auto s = chrono::duration_cast<chrono::seconds>(d);
1047 #if _GLIBCXX_USE_UTIMENSAT
1048 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1049 struct ::timespec ts[2];
1050 ts[0].tv_sec = 0;
1051 ts[0].tv_nsec = UTIME_OMIT;
1052 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1053 ts[1].tv_nsec = static_cast<long>(ns.count());
1054 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1055 ec.assign(errno, std::generic_category());
1056 else
1057 ec.clear();
1058 #elif _GLIBCXX_HAVE_UTIME_H
1059 ::utimbuf times;
1060 times.modtime = s.count();
1061 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1062 times.modtime);
1063 if (::utime(p.c_str(), &times))
1064 ec.assign(errno, std::generic_category());
1065 else
1066 ec.clear();
1067 #else
1068 ec = std::make_error_code(std::errc::not_supported);
1069 #endif
1072 void
1073 fs::permissions(const path& p, perms prms)
1075 error_code ec;
1076 permissions(p, prms, ec);
1077 if (ec.value())
1078 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1081 void fs::permissions(const path& p, perms prms, error_code& ec) noexcept
1083 #if _GLIBCXX_USE_FCHMODAT
1084 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), 0))
1085 #else
1086 if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
1087 #endif
1088 ec.assign(errno, std::generic_category());
1089 else
1090 ec.clear();
1093 fs::path
1094 fs::read_symlink(const path& p)
1096 error_code ec;
1097 path tgt = read_symlink(p, ec);
1098 if (ec.value())
1099 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1100 return tgt;
1103 fs::path fs::read_symlink(const path& p, error_code& ec)
1105 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1106 stat_type st;
1107 if (::lstat(p.c_str(), &st))
1109 ec.assign(errno, std::generic_category());
1110 return {};
1112 std::string buf(st.st_size, '\0');
1113 ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size());
1114 if (len == -1)
1116 ec.assign(errno, std::generic_category());
1117 return {};
1119 return path{buf.data(), buf.data()+len};
1120 #else
1121 ec = std::make_error_code(std::errc::not_supported);
1122 return {};
1123 #endif
1127 bool
1128 fs::remove(const path& p)
1130 error_code ec;
1131 bool result = fs::remove(p, ec);
1132 if (ec.value())
1133 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1134 return result;
1137 bool
1138 fs::remove(const path& p, error_code& ec) noexcept
1140 if (exists(symlink_status(p, ec)))
1142 if (::remove(p.c_str()) == 0)
1144 ec.clear();
1145 return true;
1147 else
1148 ec.assign(errno, std::generic_category());
1150 return false;
1154 std::uintmax_t
1155 fs::remove_all(const path& p)
1157 error_code ec;
1158 bool result = remove_all(p, ec);
1159 if (ec.value())
1160 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1161 return result;
1164 std::uintmax_t
1165 fs::remove_all(const path& p, error_code& ec) noexcept
1167 auto fs = symlink_status(p, ec);
1168 uintmax_t count = 0;
1169 if (ec.value() == 0 && fs.type() == file_type::directory)
1170 for (directory_iterator d(p, ec), end; ec.value() == 0 && d != end; ++d)
1171 count += fs::remove(d->path(), ec);
1172 if (ec.value())
1173 return -1;
1174 return fs::remove(p, ec) ? ++count : -1; // fs:remove() calls ec.clear()
1177 void
1178 fs::rename(const path& from, const path& to)
1180 error_code ec;
1181 rename(from, to, ec);
1182 if (ec.value())
1183 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1186 void
1187 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1189 if (::rename(from.c_str(), to.c_str()))
1190 ec.assign(errno, std::generic_category());
1191 else
1192 ec.clear();
1195 void
1196 fs::resize_file(const path& p, uintmax_t size)
1198 error_code ec;
1199 resize_file(p, size, ec);
1200 if (ec.value())
1201 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1204 void
1205 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1207 #ifdef _GLIBCXX_HAVE_UNISTD_H
1208 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1209 ec.assign(EINVAL, std::generic_category());
1210 else if (::truncate(p.c_str(), size))
1211 ec.assign(errno, std::generic_category());
1212 else
1213 ec.clear();
1214 #else
1215 ec = std::make_error_code(std::errc::not_supported);
1216 #endif
1220 fs::space_info
1221 fs::space(const path& p)
1223 error_code ec;
1224 space_info s = space(p, ec);
1225 if (ec.value())
1226 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1227 return s;
1230 fs::space_info
1231 fs::space(const path& p, error_code& ec) noexcept
1233 space_info info = {
1234 static_cast<uintmax_t>(-1),
1235 static_cast<uintmax_t>(-1),
1236 static_cast<uintmax_t>(-1)
1238 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1239 struct ::statvfs f;
1240 if (::statvfs(p.c_str(), &f))
1241 ec.assign(errno, std::generic_category());
1242 else
1244 info = space_info{
1245 f.f_blocks * f.f_frsize,
1246 f.f_bfree * f.f_frsize,
1247 f.f_bavail * f.f_frsize
1249 ec.clear();
1251 #else
1252 ec = std::make_error_code(std::errc::not_supported);
1253 #endif
1254 return info;
1257 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1258 fs::file_status
1259 fs::status(const fs::path& p, std::error_code& ec) noexcept
1261 file_status status;
1262 stat_type st;
1263 if (::stat(p.c_str(), &st))
1265 int err = errno;
1266 ec.assign(err, std::generic_category());
1267 if (is_not_found_errno(err))
1268 status.type(file_type::not_found);
1270 else
1272 status = make_file_status(st);
1273 ec.clear();
1275 return status;
1278 fs::file_status
1279 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1281 file_status status;
1282 stat_type st;
1283 if (::lstat(p.c_str(), &st))
1285 int err = errno;
1286 ec.assign(err, std::generic_category());
1287 if (is_not_found_errno(err))
1288 status.type(file_type::not_found);
1290 else
1292 status = make_file_status(st);
1293 ec.clear();
1295 return status;
1297 #endif
1299 fs::file_status
1300 fs::status(const fs::path& p)
1302 std::error_code ec;
1303 auto result = status(p, ec);
1304 if (result.type() == file_type::none)
1305 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1306 return result;
1309 fs::file_status
1310 fs::symlink_status(const fs::path& p)
1312 std::error_code ec;
1313 auto result = symlink_status(p, ec);
1314 if (result.type() == file_type::none)
1315 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1316 return result;
1319 fs::path
1320 fs::system_complete(const path& p)
1322 error_code ec;
1323 path comp = system_complete(p, ec);
1324 if (ec.value())
1325 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1326 return comp;
1329 fs::path
1330 fs::system_complete(const path& p, error_code& ec)
1332 path base = current_path(ec);
1333 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1334 if (p.is_absolute() || !p.has_root_name()
1335 || p.root_name() == base.root_name())
1336 return absolute(p, base);
1337 // else TODO
1338 ec = std::make_error_code(std::errc::not_supported);
1339 return {};
1340 #else
1341 if (ec.value())
1342 return {};
1343 return absolute(p, base);
1344 #endif
1347 fs::path fs::temp_directory_path()
1349 error_code ec;
1350 path tmp = temp_directory_path(ec);
1351 if (ec.value())
1352 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1353 return tmp;
1356 fs::path fs::temp_directory_path(error_code& ec)
1358 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1359 ec = std::make_error_code(std::errc::not_supported);
1360 return {}; // TODO
1361 #else
1362 const char* tmpdir = nullptr;
1363 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1364 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1365 tmpdir = ::getenv(*e);
1366 path p = tmpdir ? tmpdir : "/tmp";
1367 if (exists(p) && is_directory(p))
1369 ec.clear();
1370 return p;
1372 ec = std::make_error_code(std::errc::not_a_directory);
1373 return {};
1374 #endif