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