Fix union member access for EXEC_INQUIRE.
[gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
blob4e21e43b97a967ab5a8a6a520849e313709a4790
1 // Filesystem TS operations -*- C++ -*-
3 // Copyright (C) 2014-2025 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 # define NEED_DO_COPY_FILE
28 # define NEED_DO_SPACE
29 #endif
30 #ifndef _GNU_SOURCE
31 // Cygwin needs this for secure_getenv
32 # define _GNU_SOURCE 1
33 #endif
35 #include <bits/largefile-config.h>
36 #include <experimental/filesystem>
37 #include <functional>
38 #include <ostream>
39 #include <stack>
40 #include <ext/stdio_filebuf.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <limits.h> // PATH_MAX
45 #ifdef _GLIBCXX_HAVE_FCNTL_H
46 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW
47 #endif
48 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
49 # include <sys/stat.h> // stat, utimensat, fchmodat
50 #endif
51 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
52 # include <sys/statvfs.h> // statvfs
53 #endif
54 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
55 # include <utime.h> // utime
56 #endif
57 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
58 # define WIN32_LEAN_AND_MEAN
59 # include <windows.h>
60 #endif
62 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
63 namespace experimental { namespace filesystem {
64 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
65 #include "ops-common.h"
67 #include <filesystem> // std::filesystem::remove_all
69 namespace fs = std::experimental::filesystem;
70 namespace posix = std::filesystem::__gnu_posix;
72 fs::path
73 fs::absolute(const path& p, const path& base)
75 const bool has_root_dir = p.has_root_directory();
76 const bool has_root_name = p.has_root_name();
77 path abs;
78 if (has_root_dir && has_root_name)
79 abs = p;
80 else
82 abs = base.is_absolute() ? base : absolute(base);
83 if (has_root_dir)
84 abs = abs.root_name() / p;
85 else if (has_root_name)
86 abs = p.root_name() / abs.root_directory() / abs.relative_path()
87 / p.relative_path();
88 else
89 abs = abs / p;
91 return abs;
94 namespace
96 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
97 inline bool is_dot(wchar_t c) { return c == L'.'; }
98 #else
99 inline bool is_dot(char c) { return c == '.'; }
100 #endif
102 inline bool is_dot(const fs::path& path)
104 const auto& filename = path.native();
105 return filename.size() == 1 && is_dot(filename[0]);
108 inline bool is_dotdot(const fs::path& path)
110 const auto& filename = path.native();
111 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
114 struct free_as_in_malloc
116 void operator()(void* p) const { ::free(p); }
119 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
122 fs::path
123 fs::canonical(const path& p, const path& base, error_code& ec)
125 const path pa = absolute(p, base);
126 path result;
128 #ifdef _GLIBCXX_USE_REALPATH
129 char_ptr buf{ nullptr };
130 # if _XOPEN_VERSION < 700
131 // Not safe to call realpath(path, NULL)
132 using char_type = fs::path::value_type;
133 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
134 # endif
135 if (char* rp = ::realpath(pa.c_str(), buf.get()))
137 if (buf == nullptr)
138 buf.reset(rp);
139 result.assign(rp);
140 ec.clear();
141 return result;
143 if (errno != ENAMETOOLONG)
145 ec.assign(errno, std::generic_category());
146 return result;
148 #endif
150 if (!exists(pa, ec))
152 if (!ec)
153 ec = make_error_code(std::errc::no_such_file_or_directory);
154 return result;
156 // else: we know there are (currently) no unresolvable symlink loops
158 result = pa.root_path();
160 deque<path> cmpts;
161 for (auto& f : pa.relative_path())
162 cmpts.push_back(f);
164 int max_allowed_symlinks = 40;
166 while (!cmpts.empty() && !ec)
168 path f = std::move(cmpts.front());
169 cmpts.pop_front();
171 if (is_dot(f))
173 if (!is_directory(result, ec) && !ec)
174 ec.assign(ENOTDIR, std::generic_category());
176 else if (is_dotdot(f))
178 auto parent = result.parent_path();
179 if (parent.empty())
180 result = pa.root_path();
181 else
182 result.swap(parent);
184 else
186 result /= f;
188 if (is_symlink(result, ec))
190 path link = read_symlink(result, ec);
191 if (!ec)
193 if (--max_allowed_symlinks == 0)
194 ec.assign(ELOOP, std::generic_category());
195 else
197 if (link.is_absolute())
199 result = link.root_path();
200 link = link.relative_path();
202 else
203 result.remove_filename();
205 cmpts.insert(cmpts.begin(), link.begin(), link.end());
212 if (ec || !exists(result, ec))
213 result.clear();
215 return result;
218 fs::path
219 fs::canonical(const path& p, error_code& ec)
221 path cur = current_path(ec);
222 if (ec.value())
223 return {};
224 return canonical(p, cur, ec);
227 fs::path
228 fs::canonical(const path& p, const path& base)
230 error_code ec;
231 path can = canonical(p, base, ec);
232 if (ec)
233 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot canonicalize", p, base,
234 ec));
235 return can;
238 void
239 fs::copy(const path& from, const path& to, copy_options options)
241 error_code ec;
242 copy(from, to, options, ec);
243 if (ec.value())
244 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
247 namespace
249 using std::filesystem::is_set;
251 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
252 using posix::stat_type;
254 using std::filesystem::is_not_found_errno;
255 using std::filesystem::file_time;
256 #endif // _GLIBCXX_HAVE_SYS_STAT_H
258 } // namespace
260 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
261 #ifdef NEED_DO_COPY_FILE // Only define this once, not in cow-ops.o too
262 bool
263 fs::equiv_files([[maybe_unused]] const char_type* p1, const stat_type& st1,
264 [[maybe_unused]] const char_type* p2, const stat_type& st2,
265 [[maybe_unused]] error_code& ec)
267 // For POSIX the device ID and inode number uniquely identify a file.
268 // This doesn't work on Windows (see equiv_files in src/c++17/fs_ops.cc).
269 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
271 #endif
272 #endif
274 void
275 fs::copy(const path& from, const path& to, copy_options options,
276 error_code& ec) noexcept
278 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
279 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
280 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
281 const bool use_lstat = create_symlinks || skip_symlinks;
283 file_status f, t;
284 stat_type from_st, to_st;
285 // _GLIBCXX_RESOLVE_LIB_DEFECTS
286 // 2681. filesystem::copy() cannot copy symlinks
287 if (use_lstat || copy_symlinks
288 ? posix::lstat(from.c_str(), &from_st)
289 : posix::stat(from.c_str(), &from_st))
291 ec.assign(errno, std::generic_category());
292 return;
294 if (use_lstat
295 ? posix::lstat(to.c_str(), &to_st)
296 : posix::stat(to.c_str(), &to_st))
298 if (!is_not_found_errno(errno))
300 ec.assign(errno, std::generic_category());
301 return;
303 t = file_status{file_type::not_found};
305 else
306 t = make_file_status(to_st);
307 f = make_file_status(from_st);
309 if (exists(t) && !is_other(t) && !is_other(f)
310 && fs::equiv_files(from.c_str(), from_st, to.c_str(), to_st, ec))
312 ec = std::make_error_code(std::errc::file_exists);
313 return;
315 if (is_other(f) || is_other(t))
317 ec = std::make_error_code(std::errc::invalid_argument);
318 return;
320 if (is_directory(f) && is_regular_file(t))
322 ec = std::make_error_code(std::errc::is_a_directory);
323 return;
326 if (is_symlink(f))
328 if (skip_symlinks)
329 ec.clear();
330 else if (!exists(t) && copy_symlinks)
331 copy_symlink(from, to, ec);
332 else
333 // Not clear what should be done here.
334 // "Otherwise report an error as specified in Error reporting (7)."
335 ec = std::make_error_code(std::errc::invalid_argument);
337 else if (is_regular_file(f))
339 if (is_set(options, copy_options::directories_only))
340 ec.clear();
341 else if (create_symlinks)
342 create_symlink(from, to, ec);
343 else if (is_set(options, copy_options::create_hard_links))
344 create_hard_link(from, to, ec);
345 else if (is_directory(t))
346 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
347 copy_file_options(options), &from_st, nullptr, ec);
348 else
350 auto ptr = exists(t) ? &to_st : &from_st;
351 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
352 &from_st, ptr, ec);
355 // _GLIBCXX_RESOLVE_LIB_DEFECTS
356 // 2682. filesystem::copy() won't create a symlink to a directory
357 else if (is_directory(f) && create_symlinks)
358 ec = std::make_error_code(errc::is_a_directory);
359 else if (is_directory(f) && (is_set(options, copy_options::recursive)
360 || options == copy_options::none))
362 if (!exists(t))
363 if (!create_directory(to, from, ec))
364 return;
365 // set an unused bit in options to disable further recursion
366 if (!is_set(options, copy_options::recursive))
367 options |= static_cast<copy_options>(4096);
368 for (const directory_entry& x : directory_iterator(from, ec))
370 copy(x.path(), to/x.path().filename(), options, ec);
371 if (ec)
372 return;
375 // _GLIBCXX_RESOLVE_LIB_DEFECTS
376 // 2683. filesystem::copy() says "no effects"
377 else
378 ec.clear();
381 bool
382 fs::copy_file(const path& from, const path& to, copy_options option)
384 error_code ec;
385 bool result = copy_file(from, to, option, ec);
386 if (ec.value())
387 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
388 ec));
389 return result;
392 bool
393 fs::copy_file(const path& from, const path& to, copy_options options,
394 error_code& ec)
396 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
397 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
398 nullptr, nullptr, ec);
399 #else
400 ec = std::make_error_code(std::errc::function_not_supported);
401 return false;
402 #endif
406 void
407 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
409 error_code ec;
410 copy_symlink(existing_symlink, new_symlink, ec);
411 if (ec.value())
412 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
413 existing_symlink, new_symlink, ec));
416 void
417 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
418 error_code& ec) noexcept
420 auto p = read_symlink(existing_symlink, ec);
421 if (ec.value())
422 return;
423 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
424 if (is_directory(p))
426 create_directory_symlink(p, new_symlink, ec);
427 return;
429 #endif
430 create_symlink(p, new_symlink, ec);
434 bool
435 fs::create_directories(const path& p)
437 error_code ec;
438 bool result = create_directories(p, ec);
439 if (ec.value())
440 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
441 ec));
442 return result;
445 bool
446 fs::create_directories(const path& p, error_code& ec)
448 if (p.empty())
450 ec = std::make_error_code(errc::invalid_argument);
451 return false;
454 file_status st = status(p, ec);
455 if (is_directory(st))
456 return false;
457 else if (ec && !status_known(st))
458 return false;
459 else if (exists(st))
461 if (!ec)
462 ec = std::make_error_code(std::errc::not_a_directory);
463 return false;
466 std::stack<path> missing;
467 path pp = p;
469 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
471 ec.clear();
472 const auto& filename = pp.filename();
473 if (!is_dot(filename) && !is_dotdot(filename))
475 missing.push(std::move(pp));
476 pp = missing.top().parent_path();
478 else
479 pp = pp.parent_path();
482 if (ec || missing.empty())
483 return false;
485 bool created;
488 const path& top = missing.top();
489 created = create_directory(top, ec);
490 if (ec)
491 return false;
492 missing.pop();
494 while (!missing.empty());
496 return created;
499 namespace
501 bool
502 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
504 bool created = false;
505 #if _GLIBCXX_USE_MKDIR
506 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
507 if (posix::mkdir(p.c_str(), mode))
509 const int err = errno;
510 if (err != EEXIST || !is_directory(p, ec))
511 ec.assign(err, std::generic_category());
513 else
515 ec.clear();
516 created = true;
518 #else
519 ec = std::make_error_code(std::errc::function_not_supported);
520 #endif
521 return created;
523 } // namespace
525 bool
526 fs::create_directory(const path& p)
528 error_code ec;
529 bool result = create_directory(p, ec);
530 if (ec.value())
531 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
532 ec));
533 return result;
536 bool
537 fs::create_directory(const path& p, error_code& ec) noexcept
539 return create_dir(p, perms::all, ec);
543 bool
544 fs::create_directory(const path& p, const path& attributes)
546 error_code ec;
547 bool result = create_directory(p, attributes, ec);
548 if (ec.value())
549 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
550 ec));
551 return result;
554 bool
555 fs::create_directory(const path& p, const path& attributes,
556 error_code& ec) noexcept
558 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
559 stat_type st;
560 if (posix::stat(attributes.c_str(), &st))
562 ec.assign(errno, std::generic_category());
563 return false;
565 return create_dir(p, static_cast<perms>(st.st_mode), ec);
566 #else
567 ec = std::make_error_code(std::errc::function_not_supported);
568 return false;
569 #endif
573 void
574 fs::create_directory_symlink(const path& to, const path& new_symlink)
576 error_code ec;
577 create_directory_symlink(to, new_symlink, ec);
578 if (ec.value())
579 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
580 to, new_symlink, ec));
583 void
584 fs::create_directory_symlink(const path& to, const path& new_symlink,
585 error_code& ec) noexcept
587 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
588 ec = std::make_error_code(std::errc::function_not_supported);
589 #else
590 create_symlink(to, new_symlink, ec);
591 #endif
595 void
596 fs::create_hard_link(const path& to, const path& new_hard_link)
598 error_code ec;
599 create_hard_link(to, new_hard_link, ec);
600 if (ec.value())
601 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
602 to, new_hard_link, ec));
605 void
606 fs::create_hard_link(const path& to, const path& new_hard_link,
607 error_code& ec) noexcept
609 #ifdef _GLIBCXX_HAVE_LINK
610 if (::link(to.c_str(), new_hard_link.c_str()))
611 ec.assign(errno, std::generic_category());
612 else
613 ec.clear();
614 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
615 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
616 ec.clear();
617 else
618 ec = __last_system_error();
619 #else
620 ec = std::make_error_code(std::errc::function_not_supported);
621 #endif
624 void
625 fs::create_symlink(const path& to, const path& new_symlink)
627 error_code ec;
628 create_symlink(to, new_symlink, ec);
629 if (ec.value())
630 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
631 to, new_symlink, ec));
634 void
635 fs::create_symlink(const path& to, const path& new_symlink,
636 error_code& ec) noexcept
638 #ifdef _GLIBCXX_HAVE_SYMLINK
639 if (::symlink(to.c_str(), new_symlink.c_str()))
640 ec.assign(errno, std::generic_category());
641 else
642 ec.clear();
643 #else
644 ec = std::make_error_code(std::errc::function_not_supported);
645 #endif
648 fs::path
649 fs::current_path()
651 error_code ec;
652 path p = current_path(ec);
653 if (ec.value())
654 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
655 return p;
658 fs::path
659 fs::current_path(error_code& ec)
661 path p;
662 #if _GLIBCXX_USE_GETCWD
663 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
664 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
666 p.assign(cwd.get());
667 ec.clear();
669 else
670 ec.assign(errno, std::generic_category());
671 #else
672 #ifdef _PC_PATH_MAX
673 long path_max = pathconf(".", _PC_PATH_MAX);
674 size_t size;
675 if (path_max == -1)
676 size = 1024;
677 else if (path_max > 10240)
678 size = 10240;
679 else
680 size = path_max;
681 #elif defined(PATH_MAX)
682 size_t size = PATH_MAX;
683 #else
684 size_t size = 1024;
685 #endif
686 for (char_ptr buf; p.empty(); size *= 2)
688 using char_type = fs::path::value_type;
689 buf.reset((char_type*)malloc(size * sizeof(char_type)));
690 if (buf)
692 if (getcwd(buf.get(), size))
694 p.assign(buf.get());
695 ec.clear();
697 else if (errno != ERANGE)
699 ec.assign(errno, std::generic_category());
700 return {};
703 else
705 ec = std::make_error_code(std::errc::not_enough_memory);
706 return {};
709 #endif // __GLIBC__
710 #else // _GLIBCXX_HAVE_UNISTD_H
711 ec = std::make_error_code(std::errc::function_not_supported);
712 #endif
713 return p;
716 void
717 fs::current_path(const path& p)
719 error_code ec;
720 current_path(p, ec);
721 if (ec.value())
722 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
725 void
726 fs::current_path(const path& p, error_code& ec) noexcept
728 #if _GLIBCXX_USE_CHDIR
729 if (posix::chdir(p.c_str()))
730 ec.assign(errno, std::generic_category());
731 else
732 ec.clear();
733 #else
734 ec = std::make_error_code(std::errc::function_not_supported);
735 #endif
738 bool
739 fs::equivalent(const path& p1, const path& p2)
741 error_code ec;
742 auto result = equivalent(p1, p2, ec);
743 if (ec)
744 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
745 p1, p2, ec));
746 return result;
749 bool
750 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
752 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
753 int err = 0;
754 file_status s1, s2;
755 stat_type st1, st2;
756 if (posix::stat(p1.c_str(), &st1) == 0)
757 s1 = make_file_status(st1);
758 else if (is_not_found_errno(errno))
759 s1.type(file_type::not_found);
760 else
761 err = errno;
763 if (posix::stat(p2.c_str(), &st2) == 0)
764 s2 = make_file_status(st2);
765 else if (is_not_found_errno(errno))
766 s2.type(file_type::not_found);
767 else
768 err = errno;
770 if (exists(s1) && exists(s2))
772 if (is_other(s1) && is_other(s2))
774 ec = std::__unsupported();
775 return false;
777 ec.clear();
778 if (is_other(s1) || is_other(s2))
779 return false;
780 return fs::equiv_files(p1.c_str(), st1, p2.c_str(), st2, ec);
782 else if (!exists(s1) || !exists(s2))
783 ec = std::make_error_code(std::errc::no_such_file_or_directory);
784 else if (err)
785 ec.assign(err, std::generic_category());
786 else
787 ec.clear();
788 return false;
789 #else
790 ec = std::make_error_code(std::errc::function_not_supported);
791 #endif
792 return false;
795 std::uintmax_t
796 fs::file_size(const path& p)
798 error_code ec;
799 auto sz = file_size(p, ec);
800 if (ec.value())
801 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
802 return sz;
805 namespace
807 template<typename Accessor, typename T>
808 inline T
809 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
811 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
812 stat_type st;
813 if (posix::stat(p.c_str(), &st))
815 ec.assign(errno, std::generic_category());
816 return deflt;
818 ec.clear();
819 return f(st);
820 #else
821 ec = std::make_error_code(std::errc::function_not_supported);
822 return deflt;
823 #endif
827 std::uintmax_t
828 fs::file_size(const path& p, error_code& ec) noexcept
830 struct S
832 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
833 S() : type(file_type::not_found) { }
834 file_type type;
835 uintmax_t size;
837 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
838 if (s.type == file_type::regular)
839 return s.size;
840 if (!ec)
842 if (s.type == file_type::directory)
843 ec = std::make_error_code(std::errc::is_a_directory);
844 else
845 ec = std::__unsupported();
847 return -1;
850 std::uintmax_t
851 fs::hard_link_count(const path& p)
853 error_code ec;
854 auto count = hard_link_count(p, ec);
855 if (ec.value())
856 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
857 return count;
860 std::uintmax_t
861 fs::hard_link_count(const path& p, error_code& ec) noexcept
863 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
864 static_cast<uintmax_t>(-1));
867 bool
868 fs::is_empty(const path& p)
870 error_code ec;
871 bool e = is_empty(p, ec);
872 if (ec)
873 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
874 p, ec));
875 return e;
878 bool
879 fs::is_empty(const path& p, error_code& ec) noexcept
881 auto s = status(p, ec);
882 if (ec)
883 return false;
884 bool empty = fs::is_directory(s)
885 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
886 : fs::file_size(p, ec) == 0;
887 return ec ? false : empty;
890 fs::file_time_type
891 fs::last_write_time(const path& p)
893 error_code ec;
894 auto t = last_write_time(p, ec);
895 if (ec.value())
896 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
897 return t;
900 fs::file_time_type
901 fs::last_write_time(const path& p, error_code& ec) noexcept
903 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
904 file_time_type::min());
907 void
908 fs::last_write_time(const path& p, file_time_type new_time)
910 error_code ec;
911 last_write_time(p, new_time, ec);
912 if (ec.value())
913 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
916 void
917 fs::last_write_time(const path& p __attribute__((__unused__)),
918 file_time_type new_time, error_code& ec) noexcept
920 auto d = new_time.time_since_epoch();
921 auto s = chrono::duration_cast<chrono::seconds>(d);
922 #if _GLIBCXX_USE_UTIMENSAT
923 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
924 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
926 --s;
927 ns += chrono::seconds(1);
929 struct ::timespec ts[2];
930 ts[0].tv_sec = 0;
931 ts[0].tv_nsec = UTIME_OMIT;
932 ts[1].tv_sec = static_cast<std::time_t>(s.count());
933 ts[1].tv_nsec = static_cast<long>(ns.count());
934 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
935 ec.assign(errno, std::generic_category());
936 else
937 ec.clear();
938 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
939 posix::utimbuf times;
940 times.modtime = s.count();
941 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
942 times.modtime);
943 if (posix::utime(p.c_str(), &times))
944 ec.assign(errno, std::generic_category());
945 else
946 ec.clear();
947 #else
948 ec = std::make_error_code(std::errc::function_not_supported);
949 #endif
952 void
953 fs::permissions(const path& p, perms prms)
955 error_code ec;
956 permissions(p, prms, ec);
957 if (ec.value())
958 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
961 void
962 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
964 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD
965 const bool add = is_set(prms, perms::add_perms);
966 const bool remove = is_set(prms, perms::remove_perms);
967 const bool nofollow = is_set(prms, perms::symlink_nofollow);
968 if (add && remove)
970 ec = std::make_error_code(std::errc::invalid_argument);
971 return;
974 prms &= perms::mask;
976 file_status st;
977 if (add || remove || nofollow)
979 st = nofollow ? symlink_status(p, ec) : status(p, ec);
980 if (ec)
981 return;
982 auto curr = st.permissions();
983 if (add)
984 prms |= curr;
985 else if (remove)
986 prms = curr & ~prms;
989 int err = 0;
990 #if _GLIBCXX_USE_FCHMODAT
991 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
992 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
993 err = errno;
994 #else
995 if (nofollow && is_symlink(st))
996 ec = std::__unsupported();
997 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
998 err = errno;
999 #endif
1001 if (err)
1002 ec.assign(err, std::generic_category());
1003 else
1004 ec.clear();
1005 #else
1006 ec = std::make_error_code(std::errc::function_not_supported);
1007 #endif
1010 fs::path
1011 fs::read_symlink(const path& p)
1013 error_code ec;
1014 path tgt = read_symlink(p, ec);
1015 if (ec.value())
1016 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1017 return tgt;
1020 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
1022 path result;
1023 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1024 stat_type st;
1025 if (posix::lstat(p.c_str(), &st))
1027 ec.assign(errno, std::generic_category());
1028 return result;
1030 else if (!fs::is_symlink(make_file_status(st)))
1032 ec.assign(EINVAL, std::generic_category());
1033 return result;
1036 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1039 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1040 if (len == -1)
1042 ec.assign(errno, std::generic_category());
1043 return result;
1045 else if (len == (ssize_t)buf.size())
1047 if (buf.size() > 4096)
1049 ec.assign(ENAMETOOLONG, std::generic_category());
1050 return result;
1052 buf.resize(buf.size() * 2);
1054 else
1056 buf.resize(len);
1057 result.assign(buf);
1058 ec.clear();
1059 break;
1062 while (true);
1063 #else
1064 ec = std::make_error_code(std::errc::function_not_supported);
1065 #endif
1066 return result;
1070 bool
1071 fs::remove(const path& p)
1073 error_code ec;
1074 bool result = fs::remove(p, ec);
1075 if (ec)
1076 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1077 return result;
1080 bool
1081 fs::remove(const path& p, error_code& ec) noexcept
1083 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1084 auto st = symlink_status(p, ec);
1085 if (exists(st))
1087 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1088 || DeleteFileW(p.c_str()))
1090 ec.clear();
1091 return true;
1093 else if (!ec)
1094 ec = __last_system_error();
1096 else if (status_known(st))
1097 ec.clear();
1098 #else
1099 if (::remove(p.c_str()) == 0)
1101 ec.clear();
1102 return true;
1104 else if (errno == ENOENT)
1105 ec.clear();
1106 else
1107 ec.assign(errno, std::generic_category());
1108 #endif
1109 return false;
1113 std::uintmax_t
1114 fs::remove_all(const path& p)
1116 error_code ec;
1117 const auto result = remove_all(p, ec);
1118 if (ec)
1119 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1120 return result;
1123 std::uintmax_t
1124 fs::remove_all(const path& p, error_code& ec)
1126 // Use the C++17 implementation.
1127 return std::filesystem::remove_all(p.native(), ec);
1130 void
1131 fs::rename(const path& from, const path& to)
1133 error_code ec;
1134 rename(from, to, ec);
1135 if (ec.value())
1136 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1139 void
1140 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1142 if (posix::rename(from.c_str(), to.c_str()))
1143 ec.assign(errno, std::generic_category());
1144 else
1145 ec.clear();
1148 void
1149 fs::resize_file(const path& p, uintmax_t size)
1151 error_code ec;
1152 resize_file(p, size, ec);
1153 if (ec.value())
1154 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1157 void
1158 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1160 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max()))
1161 ec.assign(EINVAL, std::generic_category());
1162 else if (posix::truncate(p.c_str(), size))
1163 ec.assign(errno, std::generic_category());
1164 else
1165 ec.clear();
1169 fs::space_info
1170 fs::space(const path& p)
1172 error_code ec;
1173 space_info s = space(p, ec);
1174 if (ec.value())
1175 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1176 return s;
1179 fs::space_info
1180 fs::space(const path& p, error_code& ec) noexcept
1182 space_info info = {
1183 static_cast<uintmax_t>(-1),
1184 static_cast<uintmax_t>(-1),
1185 static_cast<uintmax_t>(-1)
1187 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1188 path dir = absolute(p);
1189 dir.remove_filename();
1190 auto str = dir.c_str();
1191 #else
1192 auto str = p.c_str();
1193 #endif
1194 fs::do_space(str, info.capacity, info.free, info.available, ec);
1195 return info;
1198 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1199 static bool has_trailing_slash(const fs::path& p)
1201 wchar_t c = p.native().back();
1202 return c == '/' || c == L'\\';
1204 #endif
1206 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1207 fs::file_status
1208 fs::status(const fs::path& p, error_code& ec) noexcept
1210 file_status status;
1212 auto str = p.c_str();
1214 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1215 // stat() fails if there's a trailing slash (PR 88881)
1216 path p2;
1217 if (p.has_relative_path() && has_trailing_slash(p))
1219 __try
1221 p2 = p.parent_path();
1222 str = p2.c_str();
1224 __catch(const bad_alloc&)
1226 ec = std::make_error_code(std::errc::not_enough_memory);
1227 return status;
1229 str = p2.c_str();
1231 #endif
1233 stat_type st;
1234 if (posix::stat(str, &st))
1236 int err = errno;
1237 ec.assign(err, std::generic_category());
1238 if (is_not_found_errno(err))
1239 status.type(file_type::not_found);
1240 #ifdef EOVERFLOW
1241 else if (err == EOVERFLOW)
1242 status.type(file_type::unknown);
1243 #endif
1245 else
1247 status = make_file_status(st);
1248 ec.clear();
1250 return status;
1253 fs::file_status
1254 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1256 file_status status;
1258 auto str = p.c_str();
1260 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1261 // stat() fails if there's a trailing slash (PR 88881)
1262 path p2;
1263 if (p.has_relative_path() && has_trailing_slash(p))
1265 __try
1267 p2 = p.parent_path();
1268 str = p2.c_str();
1270 __catch(const bad_alloc&)
1272 ec = std::make_error_code(std::errc::not_enough_memory);
1273 return status;
1275 str = p2.c_str();
1277 #endif
1279 stat_type st;
1280 if (posix::lstat(str, &st))
1282 int err = errno;
1283 ec.assign(err, std::generic_category());
1284 if (is_not_found_errno(err))
1285 status.type(file_type::not_found);
1287 else
1289 status = make_file_status(st);
1290 ec.clear();
1292 return status;
1294 #endif
1296 fs::file_status
1297 fs::status(const fs::path& p)
1299 std::error_code ec;
1300 auto result = status(p, ec);
1301 if (result.type() == file_type::none)
1302 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1303 return result;
1306 fs::file_status
1307 fs::symlink_status(const fs::path& p)
1309 std::error_code ec;
1310 auto result = symlink_status(p, ec);
1311 if (result.type() == file_type::none)
1312 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1313 return result;
1316 fs::path
1317 fs::system_complete(const path& p)
1319 error_code ec;
1320 path comp = system_complete(p, ec);
1321 if (ec.value())
1322 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1323 return comp;
1326 fs::path
1327 fs::system_complete(const path& p, error_code& ec)
1329 path base = current_path(ec);
1330 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1331 if (p.is_absolute() || !p.has_root_name()
1332 || p.root_name() == base.root_name())
1333 return absolute(p, base);
1334 // else TODO
1335 ec = std::__unsupported();
1336 return {};
1337 #else
1338 if (ec.value())
1339 return {};
1340 return absolute(p, base);
1341 #endif
1344 fs::path
1345 fs::temp_directory_path()
1347 error_code ec;
1348 path p = fs::get_temp_directory_from_env(ec);
1349 if (!ec)
1351 auto st = status(p, ec);
1352 if (!ec && !is_directory(st))
1353 ec = std::make_error_code(std::errc::not_a_directory);
1355 if (ec)
1356 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec));
1357 return p;
1360 fs::path
1361 fs::temp_directory_path(error_code& ec)
1363 path p = fs::get_temp_directory_from_env(ec);
1364 if (!ec)
1366 auto st = status(p, ec);
1367 if (ec)
1368 p.clear();
1369 else if (!is_directory(st))
1371 p.clear();
1372 ec = std::make_error_code(std::errc::not_a_directory);
1375 return p;