]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/filesystem/ops.cc
4e21e43b97a967ab5a8a6a520849e313709a4790
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
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
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 #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)
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
274 void
275 fs::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
381 bool
382 fs::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
392 bool
393 fs::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
406 void
407 fs::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
416 void
417 fs::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
434 bool
435 fs::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
445 bool
446 fs::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
499 namespace
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
525 bool
526 fs::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
536 bool
537 fs::create_directory(const path& p, error_code& ec) noexcept
538 {
539 return create_dir(p, perms::all, ec);
540 }
541
542
543 bool
544 fs::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
554 bool
555 fs::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
573 void
574 fs::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
583 void
584 fs::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
595 void
596 fs::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
605 void
606 fs::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
624 void
625 fs::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
634 void
635 fs::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
648 fs::path
649 fs::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
658 fs::path
659 fs::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
716 void
717 fs::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
725 void
726 fs::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
738 bool
739 fs::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
749 bool
750 fs::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
795 std::uintmax_t
796 fs::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
805 namespace
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
827 std::uintmax_t
828 fs::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
850 std::uintmax_t
851 fs::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
860 std::uintmax_t
861 fs::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
867 bool
868 fs::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
878 bool
879 fs::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
890 fs::file_time_type
891 fs::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
900 fs::file_time_type
901 fs::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
907 void
908 fs::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
916 void
917 fs::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
952 void
953 fs::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
961 void
962 fs::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
1010 fs::path
1011 fs::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
1020 fs::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
1070 bool
1071 fs::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
1080 bool
1081 fs::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
1113 std::uintmax_t
1114 fs::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
1123 std::uintmax_t
1124 fs::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
1130 void
1131 fs::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
1139 void
1140 fs::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
1148 void
1149 fs::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
1157 void
1158 fs::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
1169 fs::space_info
1170 fs::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
1179 fs::space_info
1180 fs::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
1199 static 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
1207 fs::file_status
1208 fs::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
1253 fs::file_status
1254 fs::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
1296 fs::file_status
1297 fs::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
1306 fs::file_status
1307 fs::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
1316 fs::path
1317 fs::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
1326 fs::path
1327 fs::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
1344 fs::path
1345 fs::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
1360 fs::path
1361 fs::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 }