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