]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/filesystem/ops.cc
eccdae3d91084046c0aa573fd47910a5fd4b339a
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
1 // Filesystem TS operations -*- C++ -*-
2
3 // Copyright (C) 2014-2024 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
69 namespace fs = std::experimental::filesystem;
70 namespace posix = std::filesystem::__gnu_posix;
71
72 fs::path
73 fs::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
94 namespace
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
122 fs::path
123 fs::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
218 fs::path
219 fs::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
227 fs::path
228 fs::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
238 void
239 fs::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
247 namespace
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 void
261 fs::copy(const path& from, const path& to, copy_options options,
262 error_code& ec) noexcept
263 {
264 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
265 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
266 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
267 const bool use_lstat = create_symlinks || skip_symlinks;
268
269 file_status f, t;
270 stat_type from_st, to_st;
271 // _GLIBCXX_RESOLVE_LIB_DEFECTS
272 // 2681. filesystem::copy() cannot copy symlinks
273 if (use_lstat || copy_symlinks
274 ? posix::lstat(from.c_str(), &from_st)
275 : posix::stat(from.c_str(), &from_st))
276 {
277 ec.assign(errno, std::generic_category());
278 return;
279 }
280 if (use_lstat
281 ? posix::lstat(to.c_str(), &to_st)
282 : posix::stat(to.c_str(), &to_st))
283 {
284 if (!is_not_found_errno(errno))
285 {
286 ec.assign(errno, std::generic_category());
287 return;
288 }
289 t = file_status{file_type::not_found};
290 }
291 else
292 t = make_file_status(to_st);
293 f = make_file_status(from_st);
294
295 if (exists(t) && !is_other(t) && !is_other(f)
296 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
297 {
298 ec = std::make_error_code(std::errc::file_exists);
299 return;
300 }
301 if (is_other(f) || is_other(t))
302 {
303 ec = std::make_error_code(std::errc::invalid_argument);
304 return;
305 }
306 if (is_directory(f) && is_regular_file(t))
307 {
308 ec = std::make_error_code(std::errc::is_a_directory);
309 return;
310 }
311
312 if (is_symlink(f))
313 {
314 if (skip_symlinks)
315 ec.clear();
316 else if (!exists(t) && copy_symlinks)
317 copy_symlink(from, to, ec);
318 else
319 // Not clear what should be done here.
320 // "Otherwise report an error as specified in Error reporting (7)."
321 ec = std::make_error_code(std::errc::invalid_argument);
322 }
323 else if (is_regular_file(f))
324 {
325 if (is_set(options, copy_options::directories_only))
326 ec.clear();
327 else if (create_symlinks)
328 create_symlink(from, to, ec);
329 else if (is_set(options, copy_options::create_hard_links))
330 create_hard_link(from, to, ec);
331 else if (is_directory(t))
332 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
333 copy_file_options(options), &from_st, nullptr, ec);
334 else
335 {
336 auto ptr = exists(t) ? &to_st : &from_st;
337 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
338 &from_st, ptr, ec);
339 }
340 }
341 // _GLIBCXX_RESOLVE_LIB_DEFECTS
342 // 2682. filesystem::copy() won't create a symlink to a directory
343 else if (is_directory(f) && create_symlinks)
344 ec = std::make_error_code(errc::is_a_directory);
345 else if (is_directory(f) && (is_set(options, copy_options::recursive)
346 || options == copy_options::none))
347 {
348 if (!exists(t))
349 if (!create_directory(to, from, ec))
350 return;
351 // set an unused bit in options to disable further recursion
352 if (!is_set(options, copy_options::recursive))
353 options |= static_cast<copy_options>(4096);
354 for (const directory_entry& x : directory_iterator(from, ec))
355 {
356 copy(x.path(), to/x.path().filename(), options, ec);
357 if (ec)
358 return;
359 }
360 }
361 // _GLIBCXX_RESOLVE_LIB_DEFECTS
362 // 2683. filesystem::copy() says "no effects"
363 else
364 ec.clear();
365 }
366
367 bool
368 fs::copy_file(const path& from, const path& to, copy_options option)
369 {
370 error_code ec;
371 bool result = copy_file(from, to, option, ec);
372 if (ec.value())
373 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
374 ec));
375 return result;
376 }
377
378 bool
379 fs::copy_file(const path& from, const path& to, copy_options options,
380 error_code& ec)
381 {
382 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
383 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
384 nullptr, nullptr, ec);
385 #else
386 ec = std::make_error_code(std::errc::function_not_supported);
387 return false;
388 #endif
389 }
390
391
392 void
393 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
394 {
395 error_code ec;
396 copy_symlink(existing_symlink, new_symlink, ec);
397 if (ec.value())
398 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
399 existing_symlink, new_symlink, ec));
400 }
401
402 void
403 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
404 error_code& ec) noexcept
405 {
406 auto p = read_symlink(existing_symlink, ec);
407 if (ec.value())
408 return;
409 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
410 if (is_directory(p))
411 {
412 create_directory_symlink(p, new_symlink, ec);
413 return;
414 }
415 #endif
416 create_symlink(p, new_symlink, ec);
417 }
418
419
420 bool
421 fs::create_directories(const path& p)
422 {
423 error_code ec;
424 bool result = create_directories(p, ec);
425 if (ec.value())
426 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
427 ec));
428 return result;
429 }
430
431 bool
432 fs::create_directories(const path& p, error_code& ec)
433 {
434 if (p.empty())
435 {
436 ec = std::make_error_code(errc::invalid_argument);
437 return false;
438 }
439
440 file_status st = status(p, ec);
441 if (is_directory(st))
442 return false;
443 else if (ec && !status_known(st))
444 return false;
445 else if (exists(st))
446 {
447 if (!ec)
448 ec = std::make_error_code(std::errc::not_a_directory);
449 return false;
450 }
451
452 std::stack<path> missing;
453 path pp = p;
454
455 while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
456 {
457 ec.clear();
458 const auto& filename = pp.filename();
459 if (!is_dot(filename) && !is_dotdot(filename))
460 {
461 missing.push(std::move(pp));
462 pp = missing.top().parent_path();
463 }
464 else
465 pp = pp.parent_path();
466 }
467
468 if (ec || missing.empty())
469 return false;
470
471 bool created;
472 do
473 {
474 const path& top = missing.top();
475 created = create_directory(top, ec);
476 if (ec)
477 return false;
478 missing.pop();
479 }
480 while (!missing.empty());
481
482 return created;
483 }
484
485 namespace
486 {
487 bool
488 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
489 {
490 bool created = false;
491 #if _GLIBCXX_USE_MKDIR
492 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
493 if (posix::mkdir(p.c_str(), mode))
494 {
495 const int err = errno;
496 if (err != EEXIST || !is_directory(p, ec))
497 ec.assign(err, std::generic_category());
498 }
499 else
500 {
501 ec.clear();
502 created = true;
503 }
504 #else
505 ec = std::make_error_code(std::errc::function_not_supported);
506 #endif
507 return created;
508 }
509 } // namespace
510
511 bool
512 fs::create_directory(const path& p)
513 {
514 error_code ec;
515 bool result = create_directory(p, ec);
516 if (ec.value())
517 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
518 ec));
519 return result;
520 }
521
522 bool
523 fs::create_directory(const path& p, error_code& ec) noexcept
524 {
525 return create_dir(p, perms::all, ec);
526 }
527
528
529 bool
530 fs::create_directory(const path& p, const path& attributes)
531 {
532 error_code ec;
533 bool result = create_directory(p, attributes, ec);
534 if (ec.value())
535 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
536 ec));
537 return result;
538 }
539
540 bool
541 fs::create_directory(const path& p, const path& attributes,
542 error_code& ec) noexcept
543 {
544 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
545 stat_type st;
546 if (posix::stat(attributes.c_str(), &st))
547 {
548 ec.assign(errno, std::generic_category());
549 return false;
550 }
551 return create_dir(p, static_cast<perms>(st.st_mode), ec);
552 #else
553 ec = std::make_error_code(std::errc::function_not_supported);
554 return false;
555 #endif
556 }
557
558
559 void
560 fs::create_directory_symlink(const path& to, const path& new_symlink)
561 {
562 error_code ec;
563 create_directory_symlink(to, new_symlink, ec);
564 if (ec.value())
565 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
566 to, new_symlink, ec));
567 }
568
569 void
570 fs::create_directory_symlink(const path& to, const path& new_symlink,
571 error_code& ec) noexcept
572 {
573 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
574 ec = std::make_error_code(std::errc::function_not_supported);
575 #else
576 create_symlink(to, new_symlink, ec);
577 #endif
578 }
579
580
581 void
582 fs::create_hard_link(const path& to, const path& new_hard_link)
583 {
584 error_code ec;
585 create_hard_link(to, new_hard_link, ec);
586 if (ec.value())
587 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
588 to, new_hard_link, ec));
589 }
590
591 void
592 fs::create_hard_link(const path& to, const path& new_hard_link,
593 error_code& ec) noexcept
594 {
595 #ifdef _GLIBCXX_HAVE_LINK
596 if (::link(to.c_str(), new_hard_link.c_str()))
597 ec.assign(errno, std::generic_category());
598 else
599 ec.clear();
600 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
601 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
602 ec.clear();
603 else
604 ec = __last_system_error();
605 #else
606 ec = std::make_error_code(std::errc::function_not_supported);
607 #endif
608 }
609
610 void
611 fs::create_symlink(const path& to, const path& new_symlink)
612 {
613 error_code ec;
614 create_symlink(to, new_symlink, ec);
615 if (ec.value())
616 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
617 to, new_symlink, ec));
618 }
619
620 void
621 fs::create_symlink(const path& to, const path& new_symlink,
622 error_code& ec) noexcept
623 {
624 #ifdef _GLIBCXX_HAVE_SYMLINK
625 if (::symlink(to.c_str(), new_symlink.c_str()))
626 ec.assign(errno, std::generic_category());
627 else
628 ec.clear();
629 #else
630 ec = std::make_error_code(std::errc::function_not_supported);
631 #endif
632 }
633
634 fs::path
635 fs::current_path()
636 {
637 error_code ec;
638 path p = current_path(ec);
639 if (ec.value())
640 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
641 return p;
642 }
643
644 fs::path
645 fs::current_path(error_code& ec)
646 {
647 path p;
648 #if _GLIBCXX_USE_GETCWD
649 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
650 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
651 {
652 p.assign(cwd.get());
653 ec.clear();
654 }
655 else
656 ec.assign(errno, std::generic_category());
657 #else
658 #ifdef _PC_PATH_MAX
659 long path_max = pathconf(".", _PC_PATH_MAX);
660 size_t size;
661 if (path_max == -1)
662 size = 1024;
663 else if (path_max > 10240)
664 size = 10240;
665 else
666 size = path_max;
667 #elif defined(PATH_MAX)
668 size_t size = PATH_MAX;
669 #else
670 size_t size = 1024;
671 #endif
672 for (char_ptr buf; p.empty(); size *= 2)
673 {
674 using char_type = fs::path::value_type;
675 buf.reset((char_type*)malloc(size * sizeof(char_type)));
676 if (buf)
677 {
678 if (getcwd(buf.get(), size))
679 {
680 p.assign(buf.get());
681 ec.clear();
682 }
683 else if (errno != ERANGE)
684 {
685 ec.assign(errno, std::generic_category());
686 return {};
687 }
688 }
689 else
690 {
691 ec = std::make_error_code(std::errc::not_enough_memory);
692 return {};
693 }
694 }
695 #endif // __GLIBC__
696 #else // _GLIBCXX_HAVE_UNISTD_H
697 ec = std::make_error_code(std::errc::function_not_supported);
698 #endif
699 return p;
700 }
701
702 void
703 fs::current_path(const path& p)
704 {
705 error_code ec;
706 current_path(p, ec);
707 if (ec.value())
708 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
709 }
710
711 void
712 fs::current_path(const path& p, error_code& ec) noexcept
713 {
714 #if _GLIBCXX_USE_CHDIR
715 if (posix::chdir(p.c_str()))
716 ec.assign(errno, std::generic_category());
717 else
718 ec.clear();
719 #else
720 ec = std::make_error_code(std::errc::function_not_supported);
721 #endif
722 }
723
724 bool
725 fs::equivalent(const path& p1, const path& p2)
726 {
727 error_code ec;
728 auto result = equivalent(p1, p2, ec);
729 if (ec)
730 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
731 p1, p2, ec));
732 return result;
733 }
734
735 bool
736 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
737 {
738 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
739 int err = 0;
740 file_status s1, s2;
741 stat_type st1, st2;
742 if (posix::stat(p1.c_str(), &st1) == 0)
743 s1 = make_file_status(st1);
744 else if (is_not_found_errno(errno))
745 s1.type(file_type::not_found);
746 else
747 err = errno;
748
749 if (posix::stat(p2.c_str(), &st2) == 0)
750 s2 = make_file_status(st2);
751 else if (is_not_found_errno(errno))
752 s2.type(file_type::not_found);
753 else
754 err = errno;
755
756 if (exists(s1) && exists(s2))
757 {
758 if (is_other(s1) && is_other(s2))
759 {
760 ec = std::__unsupported();
761 return false;
762 }
763 ec.clear();
764 if (is_other(s1) || is_other(s2))
765 return false;
766 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
767 }
768 else if (!exists(s1) && !exists(s2))
769 ec = std::make_error_code(std::errc::no_such_file_or_directory);
770 else if (err)
771 ec.assign(err, std::generic_category());
772 else
773 ec.clear();
774 return false;
775 #else
776 ec = std::make_error_code(std::errc::function_not_supported);
777 #endif
778 return false;
779 }
780
781 std::uintmax_t
782 fs::file_size(const path& p)
783 {
784 error_code ec;
785 auto sz = file_size(p, ec);
786 if (ec.value())
787 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
788 return sz;
789 }
790
791 namespace
792 {
793 template<typename Accessor, typename T>
794 inline T
795 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
796 {
797 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
798 stat_type st;
799 if (posix::stat(p.c_str(), &st))
800 {
801 ec.assign(errno, std::generic_category());
802 return deflt;
803 }
804 ec.clear();
805 return f(st);
806 #else
807 ec = std::make_error_code(std::errc::function_not_supported);
808 return deflt;
809 #endif
810 }
811 }
812
813 std::uintmax_t
814 fs::file_size(const path& p, error_code& ec) noexcept
815 {
816 struct S
817 {
818 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
819 S() : type(file_type::not_found) { }
820 file_type type;
821 uintmax_t size;
822 };
823 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
824 if (s.type == file_type::regular)
825 return s.size;
826 if (!ec)
827 {
828 if (s.type == file_type::directory)
829 ec = std::make_error_code(std::errc::is_a_directory);
830 else
831 ec = std::__unsupported();
832 }
833 return -1;
834 }
835
836 std::uintmax_t
837 fs::hard_link_count(const path& p)
838 {
839 error_code ec;
840 auto count = hard_link_count(p, ec);
841 if (ec.value())
842 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
843 return count;
844 }
845
846 std::uintmax_t
847 fs::hard_link_count(const path& p, error_code& ec) noexcept
848 {
849 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
850 static_cast<uintmax_t>(-1));
851 }
852
853 bool
854 fs::is_empty(const path& p)
855 {
856 error_code ec;
857 bool e = is_empty(p, ec);
858 if (ec)
859 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
860 p, ec));
861 return e;
862 }
863
864 bool
865 fs::is_empty(const path& p, error_code& ec) noexcept
866 {
867 auto s = status(p, ec);
868 if (ec)
869 return false;
870 bool empty = fs::is_directory(s)
871 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
872 : fs::file_size(p, ec) == 0;
873 return ec ? false : empty;
874 }
875
876 fs::file_time_type
877 fs::last_write_time(const path& p)
878 {
879 error_code ec;
880 auto t = last_write_time(p, ec);
881 if (ec.value())
882 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
883 return t;
884 }
885
886 fs::file_time_type
887 fs::last_write_time(const path& p, error_code& ec) noexcept
888 {
889 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
890 file_time_type::min());
891 }
892
893 void
894 fs::last_write_time(const path& p, file_time_type new_time)
895 {
896 error_code ec;
897 last_write_time(p, new_time, ec);
898 if (ec.value())
899 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
900 }
901
902 void
903 fs::last_write_time(const path& p __attribute__((__unused__)),
904 file_time_type new_time, error_code& ec) noexcept
905 {
906 auto d = new_time.time_since_epoch();
907 auto s = chrono::duration_cast<chrono::seconds>(d);
908 #if _GLIBCXX_USE_UTIMENSAT
909 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
910 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
911 {
912 --s;
913 ns += chrono::seconds(1);
914 }
915 struct ::timespec ts[2];
916 ts[0].tv_sec = 0;
917 ts[0].tv_nsec = UTIME_OMIT;
918 ts[1].tv_sec = static_cast<std::time_t>(s.count());
919 ts[1].tv_nsec = static_cast<long>(ns.count());
920 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
921 ec.assign(errno, std::generic_category());
922 else
923 ec.clear();
924 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
925 posix::utimbuf times;
926 times.modtime = s.count();
927 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
928 times.modtime);
929 if (posix::utime(p.c_str(), &times))
930 ec.assign(errno, std::generic_category());
931 else
932 ec.clear();
933 #else
934 ec = std::make_error_code(std::errc::function_not_supported);
935 #endif
936 }
937
938 void
939 fs::permissions(const path& p, perms prms)
940 {
941 error_code ec;
942 permissions(p, prms, ec);
943 if (ec.value())
944 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
945 }
946
947 void
948 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
949 {
950 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD
951 const bool add = is_set(prms, perms::add_perms);
952 const bool remove = is_set(prms, perms::remove_perms);
953 const bool nofollow = is_set(prms, perms::symlink_nofollow);
954 if (add && remove)
955 {
956 ec = std::make_error_code(std::errc::invalid_argument);
957 return;
958 }
959
960 prms &= perms::mask;
961
962 file_status st;
963 if (add || remove || nofollow)
964 {
965 st = nofollow ? symlink_status(p, ec) : status(p, ec);
966 if (ec)
967 return;
968 auto curr = st.permissions();
969 if (add)
970 prms |= curr;
971 else if (remove)
972 prms = curr & ~prms;
973 }
974
975 int err = 0;
976 #if _GLIBCXX_USE_FCHMODAT
977 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
978 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
979 err = errno;
980 #else
981 if (nofollow && is_symlink(st))
982 ec = std::__unsupported();
983 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
984 err = errno;
985 #endif
986
987 if (err)
988 ec.assign(err, std::generic_category());
989 else
990 ec.clear();
991 #else
992 ec = std::make_error_code(std::errc::function_not_supported);
993 #endif
994 }
995
996 fs::path
997 fs::read_symlink(const path& p)
998 {
999 error_code ec;
1000 path tgt = read_symlink(p, ec);
1001 if (ec.value())
1002 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1003 return tgt;
1004 }
1005
1006 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
1007 {
1008 path result;
1009 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1010 stat_type st;
1011 if (posix::lstat(p.c_str(), &st))
1012 {
1013 ec.assign(errno, std::generic_category());
1014 return result;
1015 }
1016 else if (!fs::is_symlink(make_file_status(st)))
1017 {
1018 ec.assign(EINVAL, std::generic_category());
1019 return result;
1020 }
1021
1022 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1023 do
1024 {
1025 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1026 if (len == -1)
1027 {
1028 ec.assign(errno, std::generic_category());
1029 return result;
1030 }
1031 else if (len == (ssize_t)buf.size())
1032 {
1033 if (buf.size() > 4096)
1034 {
1035 ec.assign(ENAMETOOLONG, std::generic_category());
1036 return result;
1037 }
1038 buf.resize(buf.size() * 2);
1039 }
1040 else
1041 {
1042 buf.resize(len);
1043 result.assign(buf);
1044 ec.clear();
1045 break;
1046 }
1047 }
1048 while (true);
1049 #else
1050 ec = std::make_error_code(std::errc::function_not_supported);
1051 #endif
1052 return result;
1053 }
1054
1055
1056 bool
1057 fs::remove(const path& p)
1058 {
1059 error_code ec;
1060 bool result = fs::remove(p, ec);
1061 if (ec)
1062 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1063 return result;
1064 }
1065
1066 bool
1067 fs::remove(const path& p, error_code& ec) noexcept
1068 {
1069 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1070 auto st = symlink_status(p, ec);
1071 if (exists(st))
1072 {
1073 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1074 || DeleteFileW(p.c_str()))
1075 {
1076 ec.clear();
1077 return true;
1078 }
1079 else if (!ec)
1080 ec = __last_system_error();
1081 }
1082 else if (status_known(st))
1083 ec.clear();
1084 #else
1085 if (::remove(p.c_str()) == 0)
1086 {
1087 ec.clear();
1088 return true;
1089 }
1090 else if (errno == ENOENT)
1091 ec.clear();
1092 else
1093 ec.assign(errno, std::generic_category());
1094 #endif
1095 return false;
1096 }
1097
1098
1099 std::uintmax_t
1100 fs::remove_all(const path& p)
1101 {
1102 error_code ec;
1103 const auto result = remove_all(p, ec);
1104 if (ec)
1105 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1106 return result;
1107 }
1108
1109 std::uintmax_t
1110 fs::remove_all(const path& p, error_code& ec)
1111 {
1112 // Use the C++17 implementation.
1113 return std::filesystem::remove_all(p.native(), ec);
1114 }
1115
1116 void
1117 fs::rename(const path& from, const path& to)
1118 {
1119 error_code ec;
1120 rename(from, to, ec);
1121 if (ec.value())
1122 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1123 }
1124
1125 void
1126 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1127 {
1128 if (posix::rename(from.c_str(), to.c_str()))
1129 ec.assign(errno, std::generic_category());
1130 else
1131 ec.clear();
1132 }
1133
1134 void
1135 fs::resize_file(const path& p, uintmax_t size)
1136 {
1137 error_code ec;
1138 resize_file(p, size, ec);
1139 if (ec.value())
1140 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1141 }
1142
1143 void
1144 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1145 {
1146 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max()))
1147 ec.assign(EINVAL, std::generic_category());
1148 else if (posix::truncate(p.c_str(), size))
1149 ec.assign(errno, std::generic_category());
1150 else
1151 ec.clear();
1152 }
1153
1154
1155 fs::space_info
1156 fs::space(const path& p)
1157 {
1158 error_code ec;
1159 space_info s = space(p, ec);
1160 if (ec.value())
1161 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1162 return s;
1163 }
1164
1165 fs::space_info
1166 fs::space(const path& p, error_code& ec) noexcept
1167 {
1168 space_info info = {
1169 static_cast<uintmax_t>(-1),
1170 static_cast<uintmax_t>(-1),
1171 static_cast<uintmax_t>(-1)
1172 };
1173 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1174 path dir = absolute(p);
1175 dir.remove_filename();
1176 auto str = dir.c_str();
1177 #else
1178 auto str = p.c_str();
1179 #endif
1180 fs::do_space(str, info.capacity, info.free, info.available, ec);
1181 return info;
1182 }
1183
1184 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1185 static bool has_trailing_slash(const fs::path& p)
1186 {
1187 wchar_t c = p.native().back();
1188 return c == '/' || c == L'\\';
1189 }
1190 #endif
1191
1192 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1193 fs::file_status
1194 fs::status(const fs::path& p, error_code& ec) noexcept
1195 {
1196 file_status status;
1197
1198 auto str = p.c_str();
1199
1200 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1201 // stat() fails if there's a trailing slash (PR 88881)
1202 path p2;
1203 if (p.has_relative_path() && has_trailing_slash(p))
1204 {
1205 __try
1206 {
1207 p2 = p.parent_path();
1208 str = p2.c_str();
1209 }
1210 __catch(const bad_alloc&)
1211 {
1212 ec = std::make_error_code(std::errc::not_enough_memory);
1213 return status;
1214 }
1215 str = p2.c_str();
1216 }
1217 #endif
1218
1219 stat_type st;
1220 if (posix::stat(str, &st))
1221 {
1222 int err = errno;
1223 ec.assign(err, std::generic_category());
1224 if (is_not_found_errno(err))
1225 status.type(file_type::not_found);
1226 #ifdef EOVERFLOW
1227 else if (err == EOVERFLOW)
1228 status.type(file_type::unknown);
1229 #endif
1230 }
1231 else
1232 {
1233 status = make_file_status(st);
1234 ec.clear();
1235 }
1236 return status;
1237 }
1238
1239 fs::file_status
1240 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1241 {
1242 file_status status;
1243
1244 auto str = p.c_str();
1245
1246 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1247 // stat() fails if there's a trailing slash (PR 88881)
1248 path p2;
1249 if (p.has_relative_path() && has_trailing_slash(p))
1250 {
1251 __try
1252 {
1253 p2 = p.parent_path();
1254 str = p2.c_str();
1255 }
1256 __catch(const bad_alloc&)
1257 {
1258 ec = std::make_error_code(std::errc::not_enough_memory);
1259 return status;
1260 }
1261 str = p2.c_str();
1262 }
1263 #endif
1264
1265 stat_type st;
1266 if (posix::lstat(str, &st))
1267 {
1268 int err = errno;
1269 ec.assign(err, std::generic_category());
1270 if (is_not_found_errno(err))
1271 status.type(file_type::not_found);
1272 }
1273 else
1274 {
1275 status = make_file_status(st);
1276 ec.clear();
1277 }
1278 return status;
1279 }
1280 #endif
1281
1282 fs::file_status
1283 fs::status(const fs::path& p)
1284 {
1285 std::error_code ec;
1286 auto result = status(p, ec);
1287 if (result.type() == file_type::none)
1288 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1289 return result;
1290 }
1291
1292 fs::file_status
1293 fs::symlink_status(const fs::path& p)
1294 {
1295 std::error_code ec;
1296 auto result = symlink_status(p, ec);
1297 if (result.type() == file_type::none)
1298 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1299 return result;
1300 }
1301
1302 fs::path
1303 fs::system_complete(const path& p)
1304 {
1305 error_code ec;
1306 path comp = system_complete(p, ec);
1307 if (ec.value())
1308 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1309 return comp;
1310 }
1311
1312 fs::path
1313 fs::system_complete(const path& p, error_code& ec)
1314 {
1315 path base = current_path(ec);
1316 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1317 if (p.is_absolute() || !p.has_root_name()
1318 || p.root_name() == base.root_name())
1319 return absolute(p, base);
1320 // else TODO
1321 ec = std::__unsupported();
1322 return {};
1323 #else
1324 if (ec.value())
1325 return {};
1326 return absolute(p, base);
1327 #endif
1328 }
1329
1330 fs::path
1331 fs::temp_directory_path()
1332 {
1333 error_code ec;
1334 path p = fs::get_temp_directory_from_env(ec);
1335 if (!ec)
1336 {
1337 auto st = status(p, ec);
1338 if (!ec && !is_directory(st))
1339 ec = std::make_error_code(std::errc::not_a_directory);
1340 }
1341 if (ec)
1342 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec));
1343 return p;
1344 }
1345
1346 fs::path
1347 fs::temp_directory_path(error_code& ec)
1348 {
1349 path p = fs::get_temp_directory_from_env(ec);
1350 if (!ec)
1351 {
1352 auto st = status(p, ec);
1353 if (ec)
1354 p.clear();
1355 else if (!is_directory(st))
1356 {
1357 p.clear();
1358 ec = std::make_error_code(std::errc::not_a_directory);
1359 }
1360 }
1361 return p;
1362 }