1 // Filesystem TS operations -*- C++ -*-
2
3 // Copyright (C) 2014-2021 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 ()__anon20e065ab0111::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))
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 = 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 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
482 if (posix::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 (posix::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_LINK
585 if (::link(to.c_str(), new_hard_link.c_str()))
586 ec.assign(errno, std::generic_category());
587 else
588 ec.clear();
589 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
590 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
591 ec.clear();
592 else
593 ec.assign((int)GetLastError(), system_category());
594 #else
595 ec = std::make_error_code(std::errc::not_supported);
596 #endif
597 }
598
599 void
create_symlink(const path & to,const path & new_symlink)600 fs::create_symlink(const path& to, const path& new_symlink)
601 {
602 error_code ec;
603 create_symlink(to, new_symlink, ec);
604 if (ec.value())
605 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
606 to, new_symlink, ec));
607 }
608
609 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)610 fs::create_symlink(const path& to, const path& new_symlink,
611 error_code& ec) noexcept
612 {
613 #ifdef _GLIBCXX_HAVE_SYMLINK
614 if (::symlink(to.c_str(), new_symlink.c_str()))
615 ec.assign(errno, std::generic_category());
616 else
617 ec.clear();
618 #else
619 ec = std::make_error_code(std::errc::not_supported);
620 #endif
621 }
622
623 fs::path
current_path()624 fs::current_path()
625 {
626 error_code ec;
627 path p = current_path(ec);
628 if (ec.value())
629 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
630 return p;
631 }
632
633 fs::path
current_path(error_code & ec)634 fs::current_path(error_code& ec)
635 {
636 path p;
637 #ifdef _GLIBCXX_HAVE_UNISTD_H
638 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
639 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
640 {
641 p.assign(cwd.get());
642 ec.clear();
643 }
644 else
645 ec.assign(errno, std::generic_category());
646 #else
647 #ifdef _PC_PATH_MAX
648 long path_max = pathconf(".", _PC_PATH_MAX);
649 size_t size;
650 if (path_max == -1)
651 size = 1024;
652 else if (path_max > 10240)
653 size = 10240;
654 else
655 size = path_max;
656 #elif defined(PATH_MAX)
657 size_t size = PATH_MAX;
658 #else
659 size_t size = 1024;
660 #endif
661 for (char_ptr buf; p.empty(); size *= 2)
662 {
663 using char_type = fs::path::value_type;
664 buf.reset((char_type*)malloc(size * sizeof(char_type)));
665 if (buf)
666 {
667 if (getcwd(buf.get(), size))
668 {
669 p.assign(buf.get());
670 ec.clear();
671 }
672 else if (errno != ERANGE)
673 {
674 ec.assign(errno, std::generic_category());
675 return {};
676 }
677 }
678 else
679 {
680 ec = std::make_error_code(std::errc::not_enough_memory);
681 return {};
682 }
683 }
684 #endif // __GLIBC__
685 #else // _GLIBCXX_HAVE_UNISTD_H
686 ec = std::make_error_code(std::errc::not_supported);
687 #endif
688 return p;
689 }
690
691 void
current_path(const path & p)692 fs::current_path(const path& p)
693 {
694 error_code ec;
695 current_path(p, ec);
696 if (ec.value())
697 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
698 }
699
700 void
current_path(const path & p,error_code & ec)701 fs::current_path(const path& p, error_code& ec) noexcept
702 {
703 #ifdef _GLIBCXX_HAVE_UNISTD_H
704 if (posix::chdir(p.c_str()))
705 ec.assign(errno, std::generic_category());
706 else
707 ec.clear();
708 #else
709 ec = std::make_error_code(std::errc::not_supported);
710 #endif
711 }
712
713 bool
equivalent(const path & p1,const path & p2)714 fs::equivalent(const path& p1, const path& p2)
715 {
716 error_code ec;
717 auto result = equivalent(p1, p2, ec);
718 if (ec)
719 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
720 p1, p2, ec));
721 return result;
722 }
723
724 bool
equivalent(const path & p1,const path & p2,error_code & ec)725 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
726 {
727 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
728 int err = 0;
729 file_status s1, s2;
730 stat_type st1, st2;
731 if (posix::stat(p1.c_str(), &st1) == 0)
732 s1 = make_file_status(st1);
733 else if (is_not_found_errno(errno))
734 s1.type(file_type::not_found);
735 else
736 err = errno;
737
738 if (posix::stat(p2.c_str(), &st2) == 0)
739 s2 = make_file_status(st2);
740 else if (is_not_found_errno(errno))
741 s2.type(file_type::not_found);
742 else
743 err = errno;
744
745 if (exists(s1) && exists(s2))
746 {
747 if (is_other(s1) && is_other(s2))
748 {
749 ec = std::make_error_code(std::errc::not_supported);
750 return false;
751 }
752 ec.clear();
753 if (is_other(s1) || is_other(s2))
754 return false;
755 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
756 }
757 else if (!exists(s1) && !exists(s2))
758 ec = std::make_error_code(std::errc::no_such_file_or_directory);
759 else if (err)
760 ec.assign(err, std::generic_category());
761 else
762 ec.clear();
763 return false;
764 #else
765 ec = std::make_error_code(std::errc::not_supported);
766 #endif
767 return false;
768 }
769
770 std::uintmax_t
file_size(const path & p)771 fs::file_size(const path& p)
772 {
773 error_code ec;
774 auto sz = file_size(p, ec);
775 if (ec.value())
776 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
777 return sz;
778 }
779
780 namespace
781 {
782 template<typename Accessor, typename T>
783 inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)784 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
785 {
786 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
787 stat_type st;
788 if (posix::stat(p.c_str(), &st))
789 {
790 ec.assign(errno, std::generic_category());
791 return deflt;
792 }
793 ec.clear();
794 return f(st);
795 #else
796 ec = std::make_error_code(std::errc::not_supported);
797 return deflt;
798 #endif
799 }
800 }
801
802 std::uintmax_t
file_size(const path & p,error_code & ec)803 fs::file_size(const path& p, error_code& ec) noexcept
804 {
805 struct S
806 {
807 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
808 S() : type(file_type::not_found) { }
809 file_type type;
810 uintmax_t size;
811 };
812 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
813 if (s.type == file_type::regular)
814 return s.size;
815 if (!ec)
816 {
817 if (s.type == file_type::directory)
818 ec = std::make_error_code(std::errc::is_a_directory);
819 else
820 ec = std::make_error_code(std::errc::not_supported);
821 }
822 return -1;
823 }
824
825 std::uintmax_t
hard_link_count(const path & p)826 fs::hard_link_count(const path& p)
827 {
828 error_code ec;
829 auto count = hard_link_count(p, ec);
830 if (ec.value())
831 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
832 return count;
833 }
834
835 std::uintmax_t
hard_link_count(const path & p,error_code & ec)836 fs::hard_link_count(const path& p, error_code& ec) noexcept
837 {
838 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
839 static_cast<uintmax_t>(-1));
840 }
841
842 bool
is_empty(const path & p)843 fs::is_empty(const path& p)
844 {
845 error_code ec;
846 bool e = is_empty(p, ec);
847 if (ec)
848 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
849 p, ec));
850 return e;
851 }
852
853 bool
is_empty(const path & p,error_code & ec)854 fs::is_empty(const path& p, error_code& ec) noexcept
855 {
856 auto s = status(p, ec);
857 if (ec)
858 return false;
859 bool empty = fs::is_directory(s)
860 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
861 : fs::file_size(p, ec) == 0;
862 return ec ? false : empty;
863 }
864
865 fs::file_time_type
last_write_time(const path & p)866 fs::last_write_time(const path& p)
867 {
868 error_code ec;
869 auto t = last_write_time(p, ec);
870 if (ec.value())
871 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
872 return t;
873 }
874
875 fs::file_time_type
last_write_time(const path & p,error_code & ec)876 fs::last_write_time(const path& p, error_code& ec) noexcept
877 {
878 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
879 file_time_type::min());
880 }
881
882 void
last_write_time(const path & p,file_time_type new_time)883 fs::last_write_time(const path& p, file_time_type new_time)
884 {
885 error_code ec;
886 last_write_time(p, new_time, ec);
887 if (ec.value())
888 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
889 }
890
891 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)892 fs::last_write_time(const path& p __attribute__((__unused__)),
893 file_time_type new_time, error_code& ec) noexcept
894 {
895 auto d = new_time.time_since_epoch();
896 auto s = chrono::duration_cast<chrono::seconds>(d);
897 #if _GLIBCXX_USE_UTIMENSAT
898 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
899 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
900 {
901 --s;
902 ns += chrono::seconds(1);
903 }
904 struct ::timespec ts[2];
905 ts[0].tv_sec = 0;
906 ts[0].tv_nsec = UTIME_OMIT;
907 ts[1].tv_sec = static_cast<std::time_t>(s.count());
908 ts[1].tv_nsec = static_cast<long>(ns.count());
909 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
910 ec.assign(errno, std::generic_category());
911 else
912 ec.clear();
913 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
914 posix::utimbuf times;
915 times.modtime = s.count();
916 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
917 times.modtime);
918 if (posix::utime(p.c_str(), ×))
919 ec.assign(errno, std::generic_category());
920 else
921 ec.clear();
922 #else
923 ec = std::make_error_code(std::errc::not_supported);
924 #endif
925 }
926
927 void
permissions(const path & p,perms prms)928 fs::permissions(const path& p, perms prms)
929 {
930 error_code ec;
931 permissions(p, prms, ec);
932 if (ec.value())
933 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
934 }
935
936 void
permissions(const path & p,perms prms,error_code & ec)937 fs::permissions(const path& p, perms prms, error_code& ec) noexcept
938 {
939 const bool add = is_set(prms, perms::add_perms);
940 const bool remove = is_set(prms, perms::remove_perms);
941 const bool nofollow = is_set(prms, perms::symlink_nofollow);
942 if (add && remove)
943 {
944 ec = std::make_error_code(std::errc::invalid_argument);
945 return;
946 }
947
948 prms &= perms::mask;
949
950 file_status st;
951 if (add || remove || nofollow)
952 {
953 st = nofollow ? symlink_status(p, ec) : status(p, ec);
954 if (ec)
955 return;
956 auto curr = st.permissions();
957 if (add)
958 prms |= curr;
959 else if (remove)
960 prms = curr & ~prms;
961 }
962
963 int err = 0;
964 #if _GLIBCXX_USE_FCHMODAT
965 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
966 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
967 err = errno;
968 #else
969 if (nofollow && is_symlink(st))
970 ec = std::make_error_code(std::errc::operation_not_supported);
971 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
972 err = errno;
973 #endif
974
975 if (err)
976 ec.assign(err, std::generic_category());
977 else
978 ec.clear();
979 }
980
981 fs::path
read_symlink(const path & p)982 fs::read_symlink(const path& p)
983 {
984 error_code ec;
985 path tgt = read_symlink(p, ec);
986 if (ec.value())
987 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
988 return tgt;
989 }
990
read_symlink(const path & p,error_code & ec)991 fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
992 {
993 path result;
994 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
995 stat_type st;
996 if (posix::lstat(p.c_str(), &st))
997 {
998 ec.assign(errno, std::generic_category());
999 return result;
1000 }
1001 else if (!fs::is_symlink(make_file_status(st)))
1002 {
1003 ec.assign(EINVAL, std::generic_category());
1004 return result;
1005 }
1006
1007 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1008 do
1009 {
1010 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1011 if (len == -1)
1012 {
1013 ec.assign(errno, std::generic_category());
1014 return result;
1015 }
1016 else if (len == (ssize_t)buf.size())
1017 {
1018 if (buf.size() > 4096)
1019 {
1020 ec.assign(ENAMETOOLONG, std::generic_category());
1021 return result;
1022 }
1023 buf.resize(buf.size() * 2);
1024 }
1025 else
1026 {
1027 buf.resize(len);
1028 result.assign(buf);
1029 ec.clear();
1030 break;
1031 }
1032 }
1033 while (true);
1034 #else
1035 ec = std::make_error_code(std::errc::not_supported);
1036 #endif
1037 return result;
1038 }
1039
1040
1041 bool
remove(const path & p)1042 fs::remove(const path& p)
1043 {
1044 error_code ec;
1045 bool result = fs::remove(p, ec);
1046 if (ec)
1047 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1048 return result;
1049 }
1050
1051 bool
remove(const path & p,error_code & ec)1052 fs::remove(const path& p, error_code& ec) noexcept
1053 {
1054 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1055 auto st = symlink_status(p, ec);
1056 if (exists(st))
1057 {
1058 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1059 || DeleteFileW(p.c_str()))
1060 {
1061 ec.clear();
1062 return true;
1063 }
1064 else if (!ec)
1065 ec.assign((int)GetLastError(), system_category());
1066 }
1067 else if (status_known(st))
1068 ec.clear();
1069 #else
1070 if (::remove(p.c_str()) == 0)
1071 {
1072 ec.clear();
1073 return true;
1074 }
1075 else if (errno == ENOENT)
1076 ec.clear();
1077 else
1078 ec.assign(errno, std::generic_category());
1079 #endif
1080 return false;
1081 }
1082
1083
1084 std::uintmax_t
remove_all(const path & p)1085 fs::remove_all(const path& p)
1086 {
1087 error_code ec;
1088 const auto result = remove_all(p, ec);
1089 if (ec)
1090 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1091 return result;
1092 }
1093
1094 std::uintmax_t
remove_all(const path & p,error_code & ec)1095 fs::remove_all(const path& p, error_code& ec) noexcept
1096 {
1097 const auto s = symlink_status(p, ec);
1098 if (!status_known(s))
1099 return -1;
1100
1101 ec.clear();
1102 if (s.type() == file_type::not_found)
1103 return 0;
1104
1105 uintmax_t count = 0;
1106 if (s.type() == file_type::directory)
1107 {
1108 directory_iterator d(p, ec), end;
1109 while (!ec && d != end)
1110 {
1111 const auto removed = fs::remove_all(d->path(), ec);
1112 if (removed == numeric_limits<uintmax_t>::max())
1113 return -1;
1114 count += removed;
1115 d.increment(ec);
1116 if (ec)
1117 return -1;
1118 }
1119 }
1120
1121 if (fs::remove(p, ec))
1122 ++count;
1123 return ec ? -1 : count;
1124 }
1125
1126 void
rename(const path & from,const path & to)1127 fs::rename(const path& from, const path& to)
1128 {
1129 error_code ec;
1130 rename(from, to, ec);
1131 if (ec.value())
1132 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1133 }
1134
1135 void
rename(const path & from,const path & to,error_code & ec)1136 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1137 {
1138 if (posix::rename(from.c_str(), to.c_str()))
1139 ec.assign(errno, std::generic_category());
1140 else
1141 ec.clear();
1142 }
1143
1144 void
resize_file(const path & p,uintmax_t size)1145 fs::resize_file(const path& p, uintmax_t size)
1146 {
1147 error_code ec;
1148 resize_file(p, size, ec);
1149 if (ec.value())
1150 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1151 }
1152
1153 void
resize_file(const path & p,uintmax_t size,error_code & ec)1154 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1155 {
1156 #ifdef _GLIBCXX_HAVE_UNISTD_H
1157 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1158 ec.assign(EINVAL, std::generic_category());
1159 else if (posix::truncate(p.c_str(), size))
1160 ec.assign(errno, std::generic_category());
1161 else
1162 ec.clear();
1163 #else
1164 ec = std::make_error_code(std::errc::not_supported);
1165 #endif
1166 }
1167
1168
1169 fs::space_info
space(const path & p)1170 fs::space(const path& p)
1171 {
1172 error_code ec;
1173 space_info s = space(p, ec);
1174 if (ec.value())
1175 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1176 return s;
1177 }
1178
1179 fs::space_info
space(const path & p,error_code & ec)1180 fs::space(const path& p, error_code& ec) noexcept
1181 {
1182 space_info info = {
1183 static_cast<uintmax_t>(-1),
1184 static_cast<uintmax_t>(-1),
1185 static_cast<uintmax_t>(-1)
1186 };
1187 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1188 path dir = absolute(p);
1189 dir.remove_filename();
1190 auto str = dir.c_str();
1191 #else
1192 auto str = p.c_str();
1193 #endif
1194 fs::do_space(str, info.capacity, info.free, info.available, ec);
1195 return info;
1196 }
1197
1198 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1199 fs::file_status
status(const fs::path & p,error_code & ec)1200 fs::status(const fs::path& p, error_code& ec) noexcept
1201 {
1202 file_status status;
1203 stat_type st;
1204 if (posix::stat(p.c_str(), &st))
1205 {
1206 int err = errno;
1207 ec.assign(err, std::generic_category());
1208 if (is_not_found_errno(err))
1209 status.type(file_type::not_found);
1210 #ifdef EOVERFLOW
1211 else if (err == EOVERFLOW)
1212 status.type(file_type::unknown);
1213 #endif
1214 }
1215 else
1216 {
1217 status = make_file_status(st);
1218 ec.clear();
1219 }
1220 return status;
1221 }
1222
1223 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1224 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1225 {
1226 file_status status;
1227 stat_type st;
1228 if (posix::lstat(p.c_str(), &st))
1229 {
1230 int err = errno;
1231 ec.assign(err, std::generic_category());
1232 if (is_not_found_errno(err))
1233 status.type(file_type::not_found);
1234 }
1235 else
1236 {
1237 status = make_file_status(st);
1238 ec.clear();
1239 }
1240 return status;
1241 }
1242 #endif
1243
1244 fs::file_status
status(const fs::path & p)1245 fs::status(const fs::path& p)
1246 {
1247 std::error_code ec;
1248 auto result = status(p, ec);
1249 if (result.type() == file_type::none)
1250 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1251 return result;
1252 }
1253
1254 fs::file_status
symlink_status(const fs::path & p)1255 fs::symlink_status(const fs::path& p)
1256 {
1257 std::error_code ec;
1258 auto result = symlink_status(p, ec);
1259 if (result.type() == file_type::none)
1260 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1261 return result;
1262 }
1263
1264 fs::path
system_complete(const path & p)1265 fs::system_complete(const path& p)
1266 {
1267 error_code ec;
1268 path comp = system_complete(p, ec);
1269 if (ec.value())
1270 _GLIBCXX_THROW_OR_ABORT(filesystem_error("system_complete", p, ec));
1271 return comp;
1272 }
1273
1274 fs::path
system_complete(const path & p,error_code & ec)1275 fs::system_complete(const path& p, error_code& ec)
1276 {
1277 path base = current_path(ec);
1278 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1279 if (p.is_absolute() || !p.has_root_name()
1280 || p.root_name() == base.root_name())
1281 return absolute(p, base);
1282 // else TODO
1283 ec = std::make_error_code(std::errc::not_supported);
1284 return {};
1285 #else
1286 if (ec.value())
1287 return {};
1288 return absolute(p, base);
1289 #endif
1290 }
1291
temp_directory_path()1292 fs::path fs::temp_directory_path()
1293 {
1294 error_code ec;
1295 path tmp = temp_directory_path(ec);
1296 if (ec.value())
1297 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1298 return tmp;
1299 }
1300
temp_directory_path(error_code & ec)1301 fs::path fs::temp_directory_path(error_code& ec)
1302 {
1303 path p;
1304 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1305 unsigned len = 1024;
1306 std::wstring buf;
1307 do
1308 {
1309 buf.resize(len);
1310 len = GetTempPathW(buf.size(), buf.data());
1311 } while (len > buf.size());
1312
1313 if (len == 0)
1314 {
1315 ec.assign((int)GetLastError(), std::system_category());
1316 return p;
1317 }
1318 buf.resize(len);
1319 p = std::move(buf);
1320 #else
1321 const char* tmpdir = nullptr;
1322 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1323 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1324 tmpdir = ::getenv(*e);
1325 p = tmpdir ? tmpdir : "/tmp";
1326 auto st = status(p, ec);
1327 if (ec)
1328 p.clear();
1329 else if (!is_directory(st))
1330 {
1331 p.clear();
1332 ec = std::make_error_code(std::errc::not_a_directory);
1333 }
1334 #endif
1335 return p;
1336 }
1337
1338