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