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