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