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