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