]> git.ipfire.org Git - thirdparty/gcc.git/blame_incremental - libstdc++-v3/src/filesystem/ops.cc
Ada: Switch from ACATS 2.6 to ACATS 4.2 testsuite
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
... / ...
CommitLineData
1// Filesystem TS operations -*- C++ -*-
2
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.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27# 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
34
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
61
62#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
63 namespace experimental { namespace filesystem {
64#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
65#include "ops-common.h"
66
67#include <filesystem> // std::filesystem::remove_all
68
69namespace fs = std::experimental::filesystem;
70namespace posix = std::filesystem::__gnu_posix;
71
72fs::path
73fs::absolute(const path& p, const path& base)
74{
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
81 {
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;
90 }
91 return abs;
92}
93
94namespace
95{
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
101
102 inline bool is_dot(const fs::path& path)
103 {
104 const auto& filename = path.native();
105 return filename.size() == 1 && is_dot(filename[0]);
106 }
107
108 inline bool is_dotdot(const fs::path& path)
109 {
110 const auto& filename = path.native();
111 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
112 }
113
114 struct free_as_in_malloc
115 {
116 void operator()(void* p) const { ::free(p); }
117 };
118
119 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
120}
121
122fs::path
123fs::canonical(const path& p, const path& base, error_code& ec)
124{
125 const path pa = absolute(p, base);
126 path result;
127
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()))
136 {
137 if (buf == nullptr)
138 buf.reset(rp);
139 result.assign(rp);
140 ec.clear();
141 return result;
142 }
143 if (errno != ENAMETOOLONG)
144 {
145 ec.assign(errno, std::generic_category());
146 return result;
147 }
148#endif
149
150 if (!exists(pa, ec))
151 {
152 if (!ec)
153 ec = make_error_code(std::errc::no_such_file_or_directory);
154 return result;
155 }
156 // else: we know there are (currently) no unresolvable symlink loops
157
158 result = pa.root_path();
159
160 deque<path> cmpts;
161 for (auto& f : pa.relative_path())
162 cmpts.push_back(f);
163
164 int max_allowed_symlinks = 40;
165
166 while (!cmpts.empty() && !ec)
167 {
168 path f = std::move(cmpts.front());
169 cmpts.pop_front();
170
171 if (is_dot(f))
172 {
173 if (!is_directory(result, ec) && !ec)
174 ec.assign(ENOTDIR, std::generic_category());
175 }
176 else if (is_dotdot(f))
177 {
178 auto parent = result.parent_path();
179 if (parent.empty())
180 result = pa.root_path();
181 else
182 result.swap(parent);
183 }
184 else
185 {
186 result /= f;
187
188 if (is_symlink(result, ec))
189 {
190 path link = read_symlink(result, ec);
191 if (!ec)
192 {
193 if (--max_allowed_symlinks == 0)
194 ec.assign(ELOOP, std::generic_category());
195 else
196 {
197 if (link.is_absolute())
198 {
199 result = link.root_path();
200 link = link.relative_path();
201 }
202 else
203 result.remove_filename();
204
205 cmpts.insert(cmpts.begin(), link.begin(), link.end());
206 }
207 }
208 }
209 }
210 }
211
212 if (ec || !exists(result, ec))
213 result.clear();
214
215 return result;
216}
217
218fs::path
219fs::canonical(const path& p, error_code& ec)
220{
221 path cur = current_path(ec);
222 if (ec.value())
223 return {};
224 return canonical(p, cur, ec);
225}
226
227fs::path
228fs::canonical(const path& p, const path& base)
229{
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;
236}
237
238void
239fs::copy(const path& from, const path& to, copy_options options)
240{
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));
245}
246
247namespace
248{
249 using std::filesystem::is_set;
250
251#ifdef _GLIBCXX_HAVE_SYS_STAT_H
252 using posix::stat_type;
253
254 using std::filesystem::is_not_found_errno;
255 using std::filesystem::file_time;
256#endif // _GLIBCXX_HAVE_SYS_STAT_H
257
258} // namespace
259
260#ifdef _GLIBCXX_HAVE_SYS_STAT_H
261#ifdef NEED_DO_COPY_FILE // Only define this once, not in cow-ops.o too
262bool
263fs::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)
266{
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;
270}
271#endif
272#endif
273
274void
275fs::copy(const path& from, const path& to, copy_options options,
276 error_code& ec) noexcept
277{
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;
282
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))
290 {
291 ec.assign(errno, std::generic_category());
292 return;
293 }
294 if (use_lstat
295 ? posix::lstat(to.c_str(), &to_st)
296 : posix::stat(to.c_str(), &to_st))
297 {
298 if (!is_not_found_errno(errno))
299 {
300 ec.assign(errno, std::generic_category());
301 return;
302 }
303 t = file_status{file_type::not_found};
304 }
305 else
306 t = make_file_status(to_st);
307 f = make_file_status(from_st);
308
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))
311 {
312 ec = std::make_error_code(std::errc::file_exists);
313 return;
314 }
315 if (is_other(f) || is_other(t))
316 {
317 ec = std::make_error_code(std::errc::invalid_argument);
318 return;
319 }
320 if (is_directory(f) && is_regular_file(t))
321 {
322 ec = std::make_error_code(std::errc::is_a_directory);
323 return;
324 }
325
326 if (is_symlink(f))
327 {
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);
336 }
337 else if (is_regular_file(f))
338 {
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
349 {
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);
353 }
354 }
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))
361 {
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))
369 {
370 copy(x.path(), to/x.path().filename(), options, ec);
371 if (ec)
372 return;
373 }
374 }
375 // _GLIBCXX_RESOLVE_LIB_DEFECTS
376 // 2683. filesystem::copy() says "no effects"
377 else
378 ec.clear();
379}
380
381bool
382fs::copy_file(const path& from, const path& to, copy_options option)
383{
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;
390}
391
392bool
393fs::copy_file(const path& from, const path& to, copy_options options,
394 error_code& ec)
395{
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
403}
404
405
406void
407fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
408{
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));
414}
415
416void
417fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
418 error_code& ec) noexcept
419{
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))
425 {
426 create_directory_symlink(p, new_symlink, ec);
427 return;
428 }
429#endif
430 create_symlink(p, new_symlink, ec);
431}
432
433
434bool
435fs::create_directories(const path& p)
436{
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;
443}
444
445bool
446fs::create_directories(const path& p, error_code& ec)
447{
448 if (p.empty())
449 {
450 ec = std::make_error_code(errc::invalid_argument);
451 return false;
452 }
453
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))
460 {
461 if (!ec)
462 ec = std::make_error_code(std::errc::not_a_directory);
463 return false;
464 }
465
466 std::stack<path> missing;
467 path pp = p;
468
469 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
470 {
471 ec.clear();
472 const auto& filename = pp.filename();
473 if (!is_dot(filename) && !is_dotdot(filename))
474 {
475 missing.push(std::move(pp));
476 pp = missing.top().parent_path();
477 }
478 else
479 pp = pp.parent_path();
480 }
481
482 if (ec || missing.empty())
483 return false;
484
485 bool created;
486 do
487 {
488 const path& top = missing.top();
489 created = create_directory(top, ec);
490 if (ec)
491 return false;
492 missing.pop();
493 }
494 while (!missing.empty());
495
496 return created;
497}
498
499namespace
500{
501 bool
502 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
503 {
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))
508 {
509 const int err = errno;
510 if (err != EEXIST || !is_directory(p, ec))
511 ec.assign(err, std::generic_category());
512 }
513 else
514 {
515 ec.clear();
516 created = true;
517 }
518#else
519 ec = std::make_error_code(std::errc::function_not_supported);
520#endif
521 return created;
522 }
523} // namespace
524
525bool
526fs::create_directory(const path& p)
527{
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;
534}
535
536bool
537fs::create_directory(const path& p, error_code& ec) noexcept
538{
539 return create_dir(p, perms::all, ec);
540}
541
542
543bool
544fs::create_directory(const path& p, const path& attributes)
545{
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;
552}
553
554bool
555fs::create_directory(const path& p, const path& attributes,
556 error_code& ec) noexcept
557{
558#ifdef _GLIBCXX_HAVE_SYS_STAT_H
559 stat_type st;
560 if (posix::stat(attributes.c_str(), &st))
561 {
562 ec.assign(errno, std::generic_category());
563 return false;
564 }
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
570}
571
572
573void
574fs::create_directory_symlink(const path& to, const path& new_symlink)
575{
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));
581}
582
583void
584fs::create_directory_symlink(const path& to, const path& new_symlink,
585 error_code& ec) noexcept
586{
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
592}
593
594
595void
596fs::create_hard_link(const path& to, const path& new_hard_link)
597{
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));
603}
604
605void
606fs::create_hard_link(const path& to, const path& new_hard_link,
607 error_code& ec) noexcept
608{
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
622}
623
624void
625fs::create_symlink(const path& to, const path& new_symlink)
626{
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));
632}
633
634void
635fs::create_symlink(const path& to, const path& new_symlink,
636 error_code& ec) noexcept
637{
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
646}
647
648fs::path
649fs::current_path()
650{
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;
656}
657
658fs::path
659fs::current_path(error_code& ec)
660{
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)})
665 {
666 p.assign(cwd.get());
667 ec.clear();
668 }
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)
687 {
688 using char_type = fs::path::value_type;
689 buf.reset((char_type*)malloc(size * sizeof(char_type)));
690 if (buf)
691 {
692 if (getcwd(buf.get(), size))
693 {
694 p.assign(buf.get());
695 ec.clear();
696 }
697 else if (errno != ERANGE)
698 {
699 ec.assign(errno, std::generic_category());
700 return {};
701 }
702 }
703 else
704 {
705 ec = std::make_error_code(std::errc::not_enough_memory);
706 return {};
707 }
708 }
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;
714}
715
716void
717fs::current_path(const path& p)
718{
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));
723}
724
725void
726fs::current_path(const path& p, error_code& ec) noexcept
727{
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
736}
737
738bool
739fs::equivalent(const path& p1, const path& p2)
740{
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;
747}
748
749bool
750fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
751{
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;
762
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;
769
770 if (exists(s1) && exists(s2))
771 {
772 if (is_other(s1) && is_other(s2))
773 {
774 ec = std::__unsupported();
775 return false;
776 }
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);
781 }
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;
793}
794
795std::uintmax_t
796fs::file_size(const path& p)
797{
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;
803}
804
805namespace
806{
807 template<typename Accessor, typename T>
808 inline T
809 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
810 {
811#ifdef _GLIBCXX_HAVE_SYS_STAT_H
812 stat_type st;
813 if (posix::stat(p.c_str(), &st))
814 {
815 ec.assign(errno, std::generic_category());
816 return deflt;
817 }
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
824 }
825}
826
827std::uintmax_t
828fs::file_size(const path& p, error_code& ec) noexcept
829{
830 struct S
831 {
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;
836 };
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)
841 {
842 if (s.type == file_type::directory)
843 ec = std::make_error_code(std::errc::is_a_directory);
844 else
845 ec = std::__unsupported();
846 }
847 return -1;
848}
849
850std::uintmax_t
851fs::hard_link_count(const path& p)
852{
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;
858}
859
860std::uintmax_t
861fs::hard_link_count(const path& p, error_code& ec) noexcept
862{
863 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
864 static_cast<uintmax_t>(-1));
865}
866
867bool
868fs::is_empty(const path& p)
869{
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;
876}
877
878bool
879fs::is_empty(const path& p, error_code& ec) noexcept
880{
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;
888}
889
890fs::file_time_type
891fs::last_write_time(const path& p)
892{
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;
898}
899
900fs::file_time_type
901fs::last_write_time(const path& p, error_code& ec) noexcept
902{
903 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
904 file_time_type::min());
905}
906
907void
908fs::last_write_time(const path& p, file_time_type new_time)
909{
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));
914}
915
916void
917fs::last_write_time(const path& p __attribute__((__unused__)),
918 file_time_type new_time, error_code& ec) noexcept
919{
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.
925 {
926 --s;
927 ns += chrono::seconds(1);
928 }
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
950}
951
952void
953fs::permissions(const path& p, perms prms)
954{
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));
959}
960
961void
962fs::permissions(const path& p, perms prms, error_code& ec) noexcept
963{
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)
969 {
970 ec = std::make_error_code(std::errc::invalid_argument);
971 return;
972 }
973
974 prms &= perms::mask;
975
976 file_status st;
977 if (add || remove || nofollow)
978 {
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;
987 }
988
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
1000
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
1008}
1009
1010fs::path
1011fs::read_symlink(const path& p)
1012{
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;
1018}
1019
1020fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
1021{
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))
1026 {
1027 ec.assign(errno, std::generic_category());
1028 return result;
1029 }
1030 else if (!fs::is_symlink(make_file_status(st)))
1031 {
1032 ec.assign(EINVAL, std::generic_category());
1033 return result;
1034 }
1035
1036 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1037 do
1038 {
1039 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1040 if (len == -1)
1041 {
1042 ec.assign(errno, std::generic_category());
1043 return result;
1044 }
1045 else if (len == (ssize_t)buf.size())
1046 {
1047 if (buf.size() > 4096)
1048 {
1049 ec.assign(ENAMETOOLONG, std::generic_category());
1050 return result;
1051 }
1052 buf.resize(buf.size() * 2);
1053 }
1054 else
1055 {
1056 buf.resize(len);
1057 result.assign(buf);
1058 ec.clear();
1059 break;
1060 }
1061 }
1062 while (true);
1063#else
1064 ec = std::make_error_code(std::errc::function_not_supported);
1065#endif
1066 return result;
1067}
1068
1069
1070bool
1071fs::remove(const path& p)
1072{
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;
1078}
1079
1080bool
1081fs::remove(const path& p, error_code& ec) noexcept
1082{
1083#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1084 auto st = symlink_status(p, ec);
1085 if (exists(st))
1086 {
1087 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1088 || DeleteFileW(p.c_str()))
1089 {
1090 ec.clear();
1091 return true;
1092 }
1093 else if (!ec)
1094 ec = __last_system_error();
1095 }
1096 else if (status_known(st))
1097 ec.clear();
1098#else
1099 if (::remove(p.c_str()) == 0)
1100 {
1101 ec.clear();
1102 return true;
1103 }
1104 else if (errno == ENOENT)
1105 ec.clear();
1106 else
1107 ec.assign(errno, std::generic_category());
1108#endif
1109 return false;
1110}
1111
1112
1113std::uintmax_t
1114fs::remove_all(const path& p)
1115{
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;
1121}
1122
1123std::uintmax_t
1124fs::remove_all(const path& p, error_code& ec)
1125{
1126 // Use the C++17 implementation.
1127 return std::filesystem::remove_all(p.native(), ec);
1128}
1129
1130void
1131fs::rename(const path& from, const path& to)
1132{
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));
1137}
1138
1139void
1140fs::rename(const path& from, const path& to, error_code& ec) noexcept
1141{
1142 if (posix::rename(from.c_str(), to.c_str()))
1143 ec.assign(errno, std::generic_category());
1144 else
1145 ec.clear();
1146}
1147
1148void
1149fs::resize_file(const path& p, uintmax_t size)
1150{
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));
1155}
1156
1157void
1158fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1159{
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();
1166}
1167
1168
1169fs::space_info
1170fs::space(const path& p)
1171{
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;
1177}
1178
1179fs::space_info
1180fs::space(const path& p, error_code& ec) noexcept
1181{
1182 space_info info = {
1183 static_cast<uintmax_t>(-1),
1184 static_cast<uintmax_t>(-1),
1185 static_cast<uintmax_t>(-1)
1186 };
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;
1196}
1197
1198#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1199static bool has_trailing_slash(const fs::path& p)
1200{
1201 wchar_t c = p.native().back();
1202 return c == '/' || c == L'\\';
1203}
1204#endif
1205
1206#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1207fs::file_status
1208fs::status(const fs::path& p, error_code& ec) noexcept
1209{
1210 file_status status;
1211
1212 auto str = p.c_str();
1213
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))
1218 {
1219 __try
1220 {
1221 p2 = p.parent_path();
1222 str = p2.c_str();
1223 }
1224 __catch(const bad_alloc&)
1225 {
1226 ec = std::make_error_code(std::errc::not_enough_memory);
1227 return status;
1228 }
1229 str = p2.c_str();
1230 }
1231#endif
1232
1233 stat_type st;
1234 if (posix::stat(str, &st))
1235 {
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
1244 }
1245 else
1246 {
1247 status = make_file_status(st);
1248 ec.clear();
1249 }
1250 return status;
1251}
1252
1253fs::file_status
1254fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1255{
1256 file_status status;
1257
1258 auto str = p.c_str();
1259
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))
1264 {
1265 __try
1266 {
1267 p2 = p.parent_path();
1268 str = p2.c_str();
1269 }
1270 __catch(const bad_alloc&)
1271 {
1272 ec = std::make_error_code(std::errc::not_enough_memory);
1273 return status;
1274 }
1275 str = p2.c_str();
1276 }
1277#endif
1278
1279 stat_type st;
1280 if (posix::lstat(str, &st))
1281 {
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);
1286 }
1287 else
1288 {
1289 status = make_file_status(st);
1290 ec.clear();
1291 }
1292 return status;
1293}
1294#endif
1295
1296fs::file_status
1297fs::status(const fs::path& p)
1298{
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;
1304}
1305
1306fs::file_status
1307fs::symlink_status(const fs::path& p)
1308{
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;
1314}
1315
1316fs::path
1317fs::system_complete(const path& p)
1318{
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;
1324}
1325
1326fs::path
1327fs::system_complete(const path& p, error_code& ec)
1328{
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
1342}
1343
1344fs::path
1345fs::temp_directory_path()
1346{
1347 error_code ec;
1348 path p = fs::get_temp_directory_from_env(ec);
1349 if (!ec)
1350 {
1351 auto st = status(p, ec);
1352 if (!ec && !is_directory(st))
1353 ec = std::make_error_code(std::errc::not_a_directory);
1354 }
1355 if (ec)
1356 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec));
1357 return p;
1358}
1359
1360fs::path
1361fs::temp_directory_path(error_code& ec)
1362{
1363 path p = fs::get_temp_directory_from_env(ec);
1364 if (!ec)
1365 {
1366 auto st = status(p, ec);
1367 if (ec)
1368 p.clear();
1369 else if (!is_directory(st))
1370 {
1371 p.clear();
1372 ec = std::make_error_code(std::errc::not_a_directory);
1373 }
1374 }
1375 return p;
1376}