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