1 // Filesystem 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 <filesystem>
33 #include <functional>
34 #include <ostream>
35 #include <stack>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <limits.h> // PATH_MAX
40 #ifdef _GLIBCXX_HAVE_FCNTL_H
41 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW
42 #endif
43 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
44 # include <sys/stat.h> // stat, utimensat, fchmodat
45 #endif
46 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h> // statvfs
48 #endif
49 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
50 # include <utime.h> // utime
51 #endif
52 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
53 # include <windows.h>
54 #endif
55
56 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
57 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
58 #include "../filesystem/ops-common.h"
59
60 #pragma GCC diagnostic ignored "-Wunused-parameter"
61
62 namespace fs = std::filesystem;
63 namespace posix = std::filesystem::__gnu_posix;
64
65 fs::path
absolute(const path & p)66 fs::absolute(const path& p)
67 {
68 error_code ec;
69 path ret = absolute(p, ec);
70 if (ec)
71 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
72 ec));
73 return ret;
74 }
75
76 fs::path
absolute(const path & p,error_code & ec)77 fs::absolute(const path& p, error_code& ec)
78 {
79 path ret;
80 if (p.empty())
81 {
82 ec = make_error_code(std::errc::invalid_argument);
83 return ret;
84 }
85 ec.clear();
86 if (p.is_absolute())
87 {
88 ret = p;
89 return ret;
90 }
91
92 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
93 // s must remain null-terminated
94 wstring_view s = p.native();
95
96 if (p.has_root_directory()) // implies !p.has_root_name()
97 {
98 // GetFullPathNameW("//") gives unwanted result (PR 88884).
99 // If there are multiple directory separators at the start,
100 // skip all but the last of them.
101 const auto pos = s.find_first_not_of(L"/\\");
102 __glibcxx_assert(pos != 0);
103 s.remove_prefix(std::min(s.length(), pos) - 1);
104 }
105
106 uint32_t len = 1024;
107 wstring buf;
108 do
109 {
110 buf.resize(len);
111 len = GetFullPathNameW(s.data(), len, buf.data(), nullptr);
112 }
113 while (len > buf.size());
114
115 if (len == 0)
116 ec.assign((int)GetLastError(), std::system_category());
117 else
118 {
119 buf.resize(len);
120 ret = std::move(buf);
121 }
122 #else
123 ret = current_path(ec);
124 ret /= p;
125 #endif
126 return ret;
127 }
128
129 namespace
130 {
131 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_dot(wchar_t c)132 inline bool is_dot(wchar_t c) { return c == L'.'; }
133 #else
134 inline bool is_dot(char c) { return c == '.'; }
135 #endif
136
is_dot(const fs::path & path)137 inline bool is_dot(const fs::path& path)
138 {
139 const auto& filename = path.native();
140 return filename.size() == 1 && is_dot(filename[0]);
141 }
142
is_dotdot(const fs::path & path)143 inline bool is_dotdot(const fs::path& path)
144 {
145 const auto& filename = path.native();
146 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
147 }
148
149 struct free_as_in_malloc
150 {
operator ()__anon8f2560e00111::free_as_in_malloc151 void operator()(void* p) const { ::free(p); }
152 };
153
154 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
155 }
156
157 fs::path
canonical(const path & p,error_code & ec)158 fs::canonical(const path& p, error_code& ec)
159 {
160 path result;
161 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
162 const path pa = absolute(p.lexically_normal(), ec);
163 #else
164 const path pa = absolute(p, ec);
165 #endif
166 if (ec)
167 return result;
168
169 #ifdef _GLIBCXX_USE_REALPATH
170 char_ptr buf{ nullptr };
171 # if _XOPEN_VERSION < 700
172 // Not safe to call realpath(path, NULL)
173 using char_type = fs::path::value_type;
174 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
175 # endif
176 if (char* rp = ::realpath(pa.c_str(), buf.get()))
177 {
178 if (buf == nullptr)
179 buf.reset(rp);
180 result.assign(rp);
181 ec.clear();
182 return result;
183 }
184 if (errno != ENAMETOOLONG)
185 {
186 ec.assign(errno, std::generic_category());
187 return result;
188 }
189 #endif
190
191 if (!exists(pa, ec))
192 {
193 if (!ec)
194 ec = make_error_code(std::errc::no_such_file_or_directory);
195 return result;
196 }
197 // else: we know there are (currently) no unresolvable symlink loops
198
199 result = pa.root_path();
200
201 deque<path> cmpts;
202 for (auto& f : pa.relative_path())
203 cmpts.push_back(f);
204
205 int max_allowed_symlinks = 40;
206
207 while (!cmpts.empty() && !ec)
208 {
209 path f = std::move(cmpts.front());
210 cmpts.pop_front();
211
212 if (f.empty())
213 {
214 // ignore empty element
215 }
216 else if (is_dot(f))
217 {
218 if (!is_directory(result, ec) && !ec)
219 ec.assign(ENOTDIR, std::generic_category());
220 }
221 else if (is_dotdot(f))
222 {
223 auto parent = result.parent_path();
224 if (parent.empty())
225 result = pa.root_path();
226 else
227 result.swap(parent);
228 }
229 else
230 {
231 result /= f;
232
233 if (is_symlink(result, ec))
234 {
235 path link = read_symlink(result, ec);
236 if (!ec)
237 {
238 if (--max_allowed_symlinks == 0)
239 ec.assign(ELOOP, std::generic_category());
240 else
241 {
242 if (link.is_absolute())
243 {
244 result = link.root_path();
245 link = link.relative_path();
246 }
247 else
248 result = result.parent_path();
249
250 cmpts.insert(cmpts.begin(), link.begin(), link.end());
251 }
252 }
253 }
254 }
255 }
256
257 if (ec || !exists(result, ec))
258 result.clear();
259
260 return result;
261 }
262
263 fs::path
canonical(const path & p)264 fs::canonical(const path& p)
265 {
266 error_code ec;
267 path res = canonical(p, ec);
268 if (ec)
269 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
270 p, ec));
271 return res;
272 }
273
274 void
copy(const path & from,const path & to,copy_options options)275 fs::copy(const path& from, const path& to, copy_options options)
276 {
277 error_code ec;
278 copy(from, to, options, ec);
279 if (ec)
280 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
281 }
282
283 namespace std::filesystem
284 {
285 // Need this as there's no 'perm_options::none' enumerator.
is_set(fs::perm_options obj,fs::perm_options bits)286 static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
287 {
288 return (obj & bits) != fs::perm_options{};
289 }
290 }
291
292 namespace
293 {
294 struct internal_file_clock : fs::__file_clock
295 {
296 using __file_clock::_S_to_sys;
297 using __file_clock::_S_from_sys;
298
299 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
300 static fs::file_time_type
from_stat__anon8f2560e00211::internal_file_clock301 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
302 {
303 const auto sys_time = fs::file_time(st, ec);
304 if (sys_time == sys_time.min())
305 return fs::file_time_type::min();
306 return _S_from_sys(sys_time);
307 }
308 #endif
309 };
310 }
311
312 void
copy(const path & from,const path & to,copy_options options,error_code & ec)313 fs::copy(const path& from, const path& to, copy_options options,
314 error_code& ec)
315 {
316 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
317 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
318 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
319 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
320 const bool use_lstat = create_symlinks || skip_symlinks;
321
322 file_status f, t;
323 stat_type from_st, to_st;
324 // _GLIBCXX_RESOLVE_LIB_DEFECTS
325 // 2681. filesystem::copy() cannot copy symlinks
326 if (use_lstat || copy_symlinks
327 ? posix::lstat(from.c_str(), &from_st)
328 : posix::stat(from.c_str(), &from_st))
329 {
330 ec.assign(errno, std::generic_category());
331 return;
332 }
333 if (use_lstat
334 ? posix::lstat(to.c_str(), &to_st)
335 : posix::stat(to.c_str(), &to_st))
336 {
337 if (!is_not_found_errno(errno))
338 {
339 ec.assign(errno, std::generic_category());
340 return;
341 }
342 t = file_status{file_type::not_found};
343 }
344 else
345 t = make_file_status(to_st);
346 f = make_file_status(from_st);
347
348 if (exists(t) && !is_other(t) && !is_other(f)
349 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
350 {
351 ec = std::make_error_code(std::errc::file_exists);
352 return;
353 }
354 if (is_other(f) || is_other(t))
355 {
356 ec = std::make_error_code(std::errc::not_supported);
357 return;
358 }
359 if (is_directory(f) && is_regular_file(t))
360 {
361 ec = std::make_error_code(std::errc::is_a_directory);
362 return;
363 }
364
365 if (is_symlink(f))
366 {
367 if (skip_symlinks)
368 ec.clear();
369 else if (!exists(t) && copy_symlinks)
370 copy_symlink(from, to, ec);
371 else
372 // Not clear what should be done here.
373 // "Otherwise report an error as specified in Error reporting (7)."
374 ec = std::make_error_code(std::errc::invalid_argument);
375 }
376 else if (is_regular_file(f))
377 {
378 if (is_set(options, copy_options::directories_only))
379 ec.clear();
380 else if (create_symlinks)
381 create_symlink(from, to, ec);
382 else if (is_set(options, copy_options::create_hard_links))
383 create_hard_link(from, to, ec);
384 else if (is_directory(t))
385 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
386 copy_file_options(options), &from_st, nullptr, ec);
387 else
388 {
389 auto ptr = exists(t) ? &to_st : &from_st;
390 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
391 &from_st, ptr, ec);
392 }
393 }
394 // _GLIBCXX_RESOLVE_LIB_DEFECTS
395 // 2682. filesystem::copy() won't create a symlink to a directory
396 else if (is_directory(f) && create_symlinks)
397 ec = std::make_error_code(errc::is_a_directory);
398 else if (is_directory(f) && (is_set(options, copy_options::recursive)
399 || options == copy_options::none))
400 {
401 if (!exists(t))
402 if (!create_directory(to, from, ec))
403 return;
404 // set an unused bit in options to disable further recursion
405 if (!is_set(options, copy_options::recursive))
406 options |= static_cast<copy_options>(4096);
407 for (const directory_entry& x : directory_iterator(from, ec))
408 {
409 copy(x.path(), to/x.path().filename(), options, ec);
410 if (ec)
411 return;
412 }
413 }
414 // _GLIBCXX_RESOLVE_LIB_DEFECTS
415 // 2683. filesystem::copy() says "no effects"
416 else
417 ec.clear();
418 #else
419 ec = std::make_error_code(std::errc::not_supported);
420 #endif
421 }
422
423 bool
copy_file(const path & from,const path & to,copy_options option)424 fs::copy_file(const path& from, const path& to, copy_options option)
425 {
426 error_code ec;
427 bool result = copy_file(from, to, option, ec);
428 if (ec)
429 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
430 ec));
431 return result;
432 }
433
434 bool
copy_file(const path & from,const path & to,copy_options options,error_code & ec)435 fs::copy_file(const path& from, const path& to, copy_options options,
436 error_code& ec)
437 {
438 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
439 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
440 nullptr, nullptr, ec);
441 #else
442 ec = std::make_error_code(std::errc::not_supported);
443 return false;
444 #endif
445 }
446
447
448 void
copy_symlink(const path & existing_symlink,const path & new_symlink)449 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
450 {
451 error_code ec;
452 copy_symlink(existing_symlink, new_symlink, ec);
453 if (ec)
454 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
455 existing_symlink, new_symlink, ec));
456 }
457
458 void
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)459 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
460 error_code& ec) noexcept
461 {
462 auto p = read_symlink(existing_symlink, ec);
463 if (ec)
464 return;
465 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
466 if (is_directory(p))
467 {
468 create_directory_symlink(p, new_symlink, ec);
469 return;
470 }
471 #endif
472 create_symlink(p, new_symlink, ec);
473 }
474
475
476 bool
create_directories(const path & p)477 fs::create_directories(const path& p)
478 {
479 error_code ec;
480 bool result = create_directories(p, ec);
481 if (ec)
482 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
483 ec));
484 return result;
485 }
486
487 bool
create_directories(const path & p,error_code & ec)488 fs::create_directories(const path& p, error_code& ec)
489 {
490 if (p.empty())
491 {
492 ec = std::make_error_code(errc::invalid_argument);
493 return false;
494 }
495
496 file_status st = status(p, ec);
497 if (is_directory(st))
498 return false;
499 else if (ec && !status_known(st))
500 return false;
501 else if (exists(st))
502 {
503 if (!ec)
504 ec = std::make_error_code(std::errc::not_a_directory);
505 return false;
506 }
507
508 __glibcxx_assert(st.type() == file_type::not_found);
509 // !exists(p) so there must be at least one non-existent component in p.
510
511 std::stack<path> missing;
512 path pp = p;
513
514 // Strip any trailing slash
515 if (pp.has_relative_path() && !pp.has_filename())
516 pp = pp.parent_path();
517
518 do
519 {
520 const auto& filename = pp.filename();
521 if (is_dot(filename) || is_dotdot(filename))
522 pp = pp.parent_path();
523 else
524 {
525 missing.push(std::move(pp));
526 if (missing.size() > 1000) // sanity check
527 {
528 ec = std::make_error_code(std::errc::filename_too_long);
529 return false;
530 }
531 pp = missing.top().parent_path();
532 }
533
534 if (pp.empty())
535 break;
536
537 st = status(pp, ec);
538 if (exists(st))
539 {
540 if (ec)
541 return false;
542 if (!is_directory(st))
543 {
544 ec = std::make_error_code(std::errc::not_a_directory);
545 return false;
546 }
547 }
548
549 if (ec && exists(st))
550 return false;
551 }
552 while (st.type() == file_type::not_found);
553
554 __glibcxx_assert(!missing.empty());
555
556 bool created;
557 do
558 {
559 const path& top = missing.top();
560 created = create_directory(top, ec);
561 if (ec)
562 return false;
563 missing.pop();
564 }
565 while (!missing.empty());
566
567 return created;
568 }
569
570 namespace
571 {
572 bool
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)573 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
574 {
575 bool created = false;
576 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
577 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
578 if (posix::mkdir(p.c_str(), mode))
579 {
580 const int err = errno;
581 if (err != EEXIST || !is_directory(p, ec))
582 ec.assign(err, std::generic_category());
583 }
584 else
585 {
586 ec.clear();
587 created = true;
588 }
589 #else
590 ec = std::make_error_code(std::errc::not_supported);
591 #endif
592 return created;
593 }
594 } // namespace
595
596 bool
create_directory(const path & p)597 fs::create_directory(const path& p)
598 {
599 error_code ec;
600 bool result = create_directory(p, ec);
601 if (ec)
602 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
603 ec));
604 return result;
605 }
606
607 bool
create_directory(const path & p,error_code & ec)608 fs::create_directory(const path& p, error_code& ec) noexcept
609 {
610 return create_dir(p, perms::all, ec);
611 }
612
613
614 bool
create_directory(const path & p,const path & attributes)615 fs::create_directory(const path& p, const path& attributes)
616 {
617 error_code ec;
618 bool result = create_directory(p, attributes, ec);
619 if (ec)
620 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
621 ec));
622 return result;
623 }
624
625 bool
create_directory(const path & p,const path & attributes,error_code & ec)626 fs::create_directory(const path& p, const path& attributes,
627 error_code& ec) noexcept
628 {
629 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
630 stat_type st;
631 if (posix::stat(attributes.c_str(), &st))
632 {
633 ec.assign(errno, std::generic_category());
634 return false;
635 }
636 return create_dir(p, static_cast<perms>(st.st_mode), ec);
637 #else
638 ec = std::make_error_code(std::errc::not_supported);
639 return false;
640 #endif
641 }
642
643
644 void
create_directory_symlink(const path & to,const path & new_symlink)645 fs::create_directory_symlink(const path& to, const path& new_symlink)
646 {
647 error_code ec;
648 create_directory_symlink(to, new_symlink, ec);
649 if (ec)
650 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
651 to, new_symlink, ec));
652 }
653
654 void
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)655 fs::create_directory_symlink(const path& to, const path& new_symlink,
656 error_code& ec) noexcept
657 {
658 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
659 ec = std::make_error_code(std::errc::not_supported);
660 #else
661 create_symlink(to, new_symlink, ec);
662 #endif
663 }
664
665
666 void
create_hard_link(const path & to,const path & new_hard_link)667 fs::create_hard_link(const path& to, const path& new_hard_link)
668 {
669 error_code ec;
670 create_hard_link(to, new_hard_link, ec);
671 if (ec)
672 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
673 to, new_hard_link, ec));
674 }
675
676 void
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)677 fs::create_hard_link(const path& to, const path& new_hard_link,
678 error_code& ec) noexcept
679 {
680 #ifdef _GLIBCXX_HAVE_LINK
681 if (::link(to.c_str(), new_hard_link.c_str()))
682 ec.assign(errno, std::generic_category());
683 else
684 ec.clear();
685 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
686 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
687 ec.clear();
688 else
689 ec.assign((int)GetLastError(), system_category());
690 #else
691 ec = std::make_error_code(std::errc::not_supported);
692 #endif
693 }
694
695 void
create_symlink(const path & to,const path & new_symlink)696 fs::create_symlink(const path& to, const path& new_symlink)
697 {
698 error_code ec;
699 create_symlink(to, new_symlink, ec);
700 if (ec)
701 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
702 to, new_symlink, ec));
703 }
704
705 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)706 fs::create_symlink(const path& to, const path& new_symlink,
707 error_code& ec) noexcept
708 {
709 #ifdef _GLIBCXX_HAVE_SYMLINK
710 if (::symlink(to.c_str(), new_symlink.c_str()))
711 ec.assign(errno, std::generic_category());
712 else
713 ec.clear();
714 #else
715 ec = std::make_error_code(std::errc::not_supported);
716 #endif
717 }
718
719
720 fs::path
current_path()721 fs::current_path()
722 {
723 error_code ec;
724 path p = current_path(ec);
725 if (ec)
726 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
727 return p;
728 }
729
730 fs::path
current_path(error_code & ec)731 fs::current_path(error_code& ec)
732 {
733 path p;
734 #ifdef _GLIBCXX_HAVE_UNISTD_H
735 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
736 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
737 {
738 p.assign(cwd.get());
739 ec.clear();
740 }
741 else
742 ec.assign(errno, std::generic_category());
743 #else
744 #ifdef _PC_PATH_MAX
745 long path_max = pathconf(".", _PC_PATH_MAX);
746 size_t size;
747 if (path_max == -1)
748 size = 1024;
749 else if (path_max > 10240)
750 size = 10240;
751 else
752 size = path_max;
753 #elif defined(PATH_MAX)
754 size_t size = PATH_MAX;
755 #else
756 size_t size = 1024;
757 #endif
758 for (char_ptr buf; p.empty(); size *= 2)
759 {
760 using char_type = fs::path::value_type;
761 buf.reset((char_type*)malloc(size * sizeof(char_type)));
762 if (buf)
763 {
764 if (getcwd(buf.get(), size))
765 {
766 p.assign(buf.get());
767 ec.clear();
768 }
769 else if (errno != ERANGE)
770 {
771 ec.assign(errno, std::generic_category());
772 return {};
773 }
774 }
775 else
776 {
777 ec = std::make_error_code(std::errc::not_enough_memory);
778 return {};
779 }
780 }
781 #endif // __GLIBC__
782 #else // _GLIBCXX_HAVE_UNISTD_H
783 ec = std::make_error_code(std::errc::not_supported);
784 #endif
785 return p;
786 }
787
788 void
current_path(const path & p)789 fs::current_path(const path& p)
790 {
791 error_code ec;
792 current_path(p, ec);
793 if (ec)
794 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
795 }
796
797 void
current_path(const path & p,error_code & ec)798 fs::current_path(const path& p, error_code& ec) noexcept
799 {
800 #ifdef _GLIBCXX_HAVE_UNISTD_H
801 if (posix::chdir(p.c_str()))
802 ec.assign(errno, std::generic_category());
803 else
804 ec.clear();
805 #else
806 ec = std::make_error_code(std::errc::not_supported);
807 #endif
808 }
809
810 bool
equivalent(const path & p1,const path & p2)811 fs::equivalent(const path& p1, const path& p2)
812 {
813 error_code ec;
814 auto result = equivalent(p1, p2, ec);
815 if (ec)
816 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
817 p1, p2, ec));
818 return result;
819 }
820
821 bool
equivalent(const path & p1,const path & p2,error_code & ec)822 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
823 {
824 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
825 int err = 0;
826 file_status s1, s2;
827 stat_type st1, st2;
828 if (posix::stat(p1.c_str(), &st1) == 0)
829 s1 = make_file_status(st1);
830 else if (is_not_found_errno(errno))
831 s1.type(file_type::not_found);
832 else
833 err = errno;
834
835 if (posix::stat(p2.c_str(), &st2) == 0)
836 s2 = make_file_status(st2);
837 else if (is_not_found_errno(errno))
838 s2.type(file_type::not_found);
839 else
840 err = errno;
841
842 if (exists(s1) && exists(s2))
843 {
844 if (is_other(s1) && is_other(s2))
845 {
846 ec = std::make_error_code(std::errc::not_supported);
847 return false;
848 }
849 ec.clear();
850 if (is_other(s1) || is_other(s2))
851 return false;
852 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
853 // st_ino is not set, so can't be used to distinguish files
854 if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev)
855 return false;
856
857 struct auto_handle {
858 explicit auto_handle(const path& p_)
859 : handle(CreateFileW(p_.c_str(), 0,
860 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
861 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0))
862 { }
863
864 ~auto_handle()
865 { if (*this) CloseHandle(handle); }
866
867 explicit operator bool() const
868 { return handle != INVALID_HANDLE_VALUE; }
869
870 bool get_info()
871 { return GetFileInformationByHandle(handle, &info); }
872
873 HANDLE handle;
874 BY_HANDLE_FILE_INFORMATION info;
875 };
876 auto_handle h1(p1);
877 auto_handle h2(p2);
878 if (!h1 || !h2)
879 {
880 if (!h1 && !h2)
881 ec.assign((int)GetLastError(), system_category());
882 return false;
883 }
884 if (!h1.get_info() || !h2.get_info())
885 {
886 ec.assign((int)GetLastError(), system_category());
887 return false;
888 }
889 return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber
890 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh
891 && h1.info.nFileIndexLow == h2.info.nFileIndexLow;
892 #else
893 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
894 #endif
895 }
896 else if (!exists(s1) && !exists(s2))
897 ec = std::make_error_code(std::errc::no_such_file_or_directory);
898 else if (err)
899 ec.assign(err, std::generic_category());
900 else
901 ec.clear();
902 return false;
903 #else
904 ec = std::make_error_code(std::errc::not_supported);
905 #endif
906 return false;
907 }
908
909 std::uintmax_t
file_size(const path & p)910 fs::file_size(const path& p)
911 {
912 error_code ec;
913 auto sz = file_size(p, ec);
914 if (ec)
915 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
916 return sz;
917 }
918
919 namespace
920 {
921 template<typename Accessor, typename T>
922 inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)923 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
924 {
925 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
926 posix::stat_type st;
927 if (posix::stat(p.c_str(), &st))
928 {
929 ec.assign(errno, std::generic_category());
930 return deflt;
931 }
932 ec.clear();
933 return f(st);
934 #else
935 ec = std::make_error_code(std::errc::not_supported);
936 return deflt;
937 #endif
938 }
939 }
940
941 std::uintmax_t
file_size(const path & p,error_code & ec)942 fs::file_size(const path& p, error_code& ec) noexcept
943 {
944 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
945 struct S
946 {
947 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
948 S() : type(file_type::not_found) { }
949 file_type type;
950 uintmax_t size;
951 };
952 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
953 if (s.type == file_type::regular)
954 return s.size;
955 if (!ec)
956 {
957 if (s.type == file_type::directory)
958 ec = std::make_error_code(std::errc::is_a_directory);
959 else
960 ec = std::make_error_code(std::errc::not_supported);
961 }
962 #else
963 ec = std::make_error_code(std::errc::not_supported);
964 #endif
965 return -1;
966 }
967
968 std::uintmax_t
hard_link_count(const path & p)969 fs::hard_link_count(const path& p)
970 {
971 error_code ec;
972 auto count = hard_link_count(p, ec);
973 if (ec)
974 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
975 return count;
976 }
977
978 std::uintmax_t
hard_link_count(const path & p,error_code & ec)979 fs::hard_link_count(const path& p, error_code& ec) noexcept
980 {
981 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
982 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
983 static_cast<uintmax_t>(-1));
984 #else
985 ec = std::make_error_code(std::errc::not_supported);
986 return static_cast<uintmax_t>(-1);
987 #endif
988 }
989
990 bool
is_empty(const path & p)991 fs::is_empty(const path& p)
992 {
993 error_code ec;
994 bool e = is_empty(p, ec);
995 if (ec)
996 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
997 p, ec));
998 return e;
999 }
1000
1001 bool
is_empty(const path & p,error_code & ec)1002 fs::is_empty(const path& p, error_code& ec)
1003 {
1004 auto s = status(p, ec);
1005 if (ec)
1006 return false;
1007 bool empty = fs::is_directory(s)
1008 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1009 : fs::file_size(p, ec) == 0;
1010 return ec ? false : empty;
1011 }
1012
1013 fs::file_time_type
last_write_time(const path & p)1014 fs::last_write_time(const path& p)
1015 {
1016 error_code ec;
1017 auto t = last_write_time(p, ec);
1018 if (ec)
1019 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1020 return t;
1021 }
1022
1023 fs::file_time_type
last_write_time(const path & p,error_code & ec)1024 fs::last_write_time(const path& p, error_code& ec) noexcept
1025 {
1026 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1027 return do_stat(p, ec,
1028 [&ec](const auto& st) {
1029 return internal_file_clock::from_stat(st, ec);
1030 },
1031 file_time_type::min());
1032 #else
1033 ec = std::make_error_code(std::errc::not_supported);
1034 return file_time_type::min();
1035 #endif
1036 }
1037
1038 void
last_write_time(const path & p,file_time_type new_time)1039 fs::last_write_time(const path& p, file_time_type new_time)
1040 {
1041 error_code ec;
1042 last_write_time(p, new_time, ec);
1043 if (ec)
1044 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1045 }
1046
1047 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)1048 fs::last_write_time(const path& p,
1049 file_time_type new_time, error_code& ec) noexcept
1050 {
1051 auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
1052 auto s = chrono::duration_cast<chrono::seconds>(d);
1053 #if _GLIBCXX_USE_UTIMENSAT
1054 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1055 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1056 {
1057 --s;
1058 ns += chrono::seconds(1);
1059 }
1060 struct ::timespec ts[2];
1061 ts[0].tv_sec = 0;
1062 ts[0].tv_nsec = UTIME_OMIT;
1063 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1064 ts[1].tv_nsec = static_cast<long>(ns.count());
1065 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1066 ec.assign(errno, std::generic_category());
1067 else
1068 ec.clear();
1069 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
1070 posix::utimbuf times;
1071 times.modtime = s.count();
1072 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1073 times.modtime);
1074 if (posix::utime(p.c_str(), ×))
1075 ec.assign(errno, std::generic_category());
1076 else
1077 ec.clear();
1078 #else
1079 ec = std::make_error_code(std::errc::not_supported);
1080 #endif
1081 }
1082
1083 void
permissions(const path & p,perms prms,perm_options opts)1084 fs::permissions(const path& p, perms prms, perm_options opts)
1085 {
1086 error_code ec;
1087 permissions(p, prms, opts, ec);
1088 if (ec)
1089 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1090 }
1091
1092 void
permissions(const path & p,perms prms,perm_options opts,error_code & ec)1093 fs::permissions(const path& p, perms prms, perm_options opts,
1094 error_code& ec) noexcept
1095 {
1096 const bool replace = is_set(opts, perm_options::replace);
1097 const bool add = is_set(opts, perm_options::add);
1098 const bool remove = is_set(opts, perm_options::remove);
1099 const bool nofollow = is_set(opts, perm_options::nofollow);
1100 if (((int)replace + (int)add + (int)remove) != 1)
1101 {
1102 ec = std::make_error_code(std::errc::invalid_argument);
1103 return;
1104 }
1105
1106 prms &= perms::mask;
1107
1108 file_status st;
1109 if (add || remove || nofollow)
1110 {
1111 st = nofollow ? symlink_status(p, ec) : status(p, ec);
1112 if (ec)
1113 return;
1114 auto curr = st.permissions();
1115 if (add)
1116 prms |= curr;
1117 else if (remove)
1118 prms = curr & ~prms;
1119 }
1120
1121 int err = 0;
1122 #if _GLIBCXX_USE_FCHMODAT
1123 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1124 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1125 err = errno;
1126 #else
1127 if (nofollow && is_symlink(st))
1128 ec = std::make_error_code(std::errc::not_supported);
1129 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
1130 err = errno;
1131 #endif
1132
1133 if (err)
1134 ec.assign(err, std::generic_category());
1135 else
1136 ec.clear();
1137 }
1138
1139 fs::path
proximate(const path & p,const path & base)1140 fs::proximate(const path& p, const path& base)
1141 {
1142 return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1143 }
1144
1145 fs::path
proximate(const path & p,const path & base,error_code & ec)1146 fs::proximate(const path& p, const path& base, error_code& ec)
1147 {
1148 path result;
1149 const auto p2 = weakly_canonical(p, ec);
1150 if (!ec)
1151 {
1152 const auto base2 = weakly_canonical(base, ec);
1153 if (!ec)
1154 result = p2.lexically_proximate(base2);
1155 }
1156 return result;
1157 }
1158
1159 fs::path
read_symlink(const path & p)1160 fs::read_symlink(const path& p)
1161 {
1162 error_code ec;
1163 path tgt = read_symlink(p, ec);
1164 if (ec)
1165 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1166 return tgt;
1167 }
1168
read_symlink(const path & p,error_code & ec)1169 fs::path fs::read_symlink(const path& p, error_code& ec)
1170 {
1171 path result;
1172 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1173 stat_type st;
1174 if (posix::lstat(p.c_str(), &st))
1175 {
1176 ec.assign(errno, std::generic_category());
1177 return result;
1178 }
1179 else if (!fs::is_symlink(make_file_status(st)))
1180 {
1181 ec.assign(EINVAL, std::generic_category());
1182 return result;
1183 }
1184
1185 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1186 do
1187 {
1188 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1189 if (len == -1)
1190 {
1191 ec.assign(errno, std::generic_category());
1192 return result;
1193 }
1194 else if (len == (ssize_t)buf.size())
1195 {
1196 if (buf.size() > 4096)
1197 {
1198 ec.assign(ENAMETOOLONG, std::generic_category());
1199 return result;
1200 }
1201 buf.resize(buf.size() * 2);
1202 }
1203 else
1204 {
1205 buf.resize(len);
1206 result.assign(buf);
1207 ec.clear();
1208 break;
1209 }
1210 }
1211 while (true);
1212 #else
1213 ec = std::make_error_code(std::errc::not_supported);
1214 #endif
1215 return result;
1216 }
1217
1218 fs::path
relative(const path & p,const path & base)1219 fs::relative(const path& p, const path& base)
1220 {
1221 return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1222 }
1223
1224 fs::path
relative(const path & p,const path & base,error_code & ec)1225 fs::relative(const path& p, const path& base, error_code& ec)
1226 {
1227 auto result = weakly_canonical(p, ec);
1228 fs::path cbase;
1229 if (!ec)
1230 cbase = weakly_canonical(base, ec);
1231 if (!ec)
1232 result = result.lexically_relative(cbase);
1233 if (ec)
1234 result.clear();
1235 return result;
1236 }
1237
1238 bool
remove(const path & p)1239 fs::remove(const path& p)
1240 {
1241 error_code ec;
1242 const bool result = fs::remove(p, ec);
1243 if (ec)
1244 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1245 return result;
1246 }
1247
1248 bool
remove(const path & p,error_code & ec)1249 fs::remove(const path& p, error_code& ec) noexcept
1250 {
1251 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1252 auto st = symlink_status(p, ec);
1253 if (exists(st))
1254 {
1255 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1256 || DeleteFileW(p.c_str()))
1257 {
1258 ec.clear();
1259 return true;
1260 }
1261 else if (!ec)
1262 ec.assign((int)GetLastError(), system_category());
1263 }
1264 else if (status_known(st))
1265 ec.clear();
1266 #else
1267 if (::remove(p.c_str()) == 0)
1268 {
1269 ec.clear();
1270 return true;
1271 }
1272 else if (errno == ENOENT)
1273 ec.clear();
1274 else
1275 ec.assign(errno, std::generic_category());
1276 #endif
1277 return false;
1278 }
1279
1280 namespace std::filesystem
1281 {
1282 namespace
1283 {
1284 struct ErrorReporter
1285 {
1286 explicit
ErrorReporterstd::filesystem::__anon8f2560e00911::ErrorReporter1287 ErrorReporter(error_code& ec) : code(&ec)
1288 { }
1289
1290 explicit
ErrorReporterstd::filesystem::__anon8f2560e00911::ErrorReporter1291 ErrorReporter(const char* s, const path& p)
1292 : code(nullptr), msg(s), path1(&p)
1293 { }
1294
1295 error_code* code;
1296 const char* msg;
1297 const path* path1;
1298
1299 void
reportstd::filesystem::__anon8f2560e00911::ErrorReporter1300 report(const error_code& ec) const
1301 {
1302 if (code)
1303 *code = ec;
1304 else
1305 _GLIBCXX_THROW_OR_ABORT(filesystem_error(msg, *path1, ec));
1306 }
1307
1308 void
reportstd::filesystem::__anon8f2560e00911::ErrorReporter1309 report(const error_code& ec, const path& path2) const
1310 {
1311 if (code)
1312 *code = ec;
1313 else if (path2 != *path1)
1314 _GLIBCXX_THROW_OR_ABORT(filesystem_error(msg, *path1, path2, ec));
1315 else
1316 _GLIBCXX_THROW_OR_ABORT(filesystem_error(msg, *path1, ec));
1317 }
1318 };
1319
1320 uintmax_t
do_remove_all(const path & p,const ErrorReporter & err)1321 do_remove_all(const path& p, const ErrorReporter& err)
1322 {
1323 error_code ec;
1324 const auto s = symlink_status(p, ec);
1325 if (!status_known(s))
1326 {
1327 if (ec)
1328 err.report(ec, p);
1329 return -1;
1330 }
1331
1332 ec.clear();
1333 if (s.type() == file_type::not_found)
1334 return 0;
1335
1336 uintmax_t count = 0;
1337 if (s.type() == file_type::directory)
1338 {
1339 directory_iterator d(p, ec), end;
1340 while (d != end)
1341 {
1342 const auto removed = fs::do_remove_all(d->path(), err);
1343 if (removed == numeric_limits<uintmax_t>::max())
1344 return -1;
1345 count += removed;
1346
1347 d.increment(ec);
1348 if (ec)
1349 {
1350 err.report(ec, p);
1351 return -1;
1352 }
1353 }
1354 }
1355
1356 if (fs::remove(p, ec))
1357 ++count;
1358 if (ec)
1359 {
1360 err.report(ec, p);
1361 return -1;
1362 }
1363 return count;
1364 }
1365 }
1366 }
1367
1368 std::uintmax_t
remove_all(const path & p)1369 fs::remove_all(const path& p)
1370 {
1371 return fs::do_remove_all(p, ErrorReporter{"cannot remove all", p});
1372 }
1373
1374 std::uintmax_t
remove_all(const path & p,error_code & ec)1375 fs::remove_all(const path& p, error_code& ec)
1376 {
1377 ec.clear();
1378 return fs::do_remove_all(p, ErrorReporter{ec});
1379 }
1380
1381 void
rename(const path & from,const path & to)1382 fs::rename(const path& from, const path& to)
1383 {
1384 error_code ec;
1385 rename(from, to, ec);
1386 if (ec)
1387 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1388 }
1389
1390 void
rename(const path & from,const path & to,error_code & ec)1391 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1392 {
1393 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1394 const auto to_status = fs::status(to, ec);
1395 if (to_status.type() == file_type::not_found)
1396 ec.clear();
1397 else if (ec)
1398 return;
1399
1400 if (fs::exists(to_status))
1401 {
1402 const auto from_status = fs::status(from, ec);
1403 if (ec)
1404 return;
1405
1406 if (fs::is_directory(to_status))
1407 {
1408 if (!fs::is_directory(from_status))
1409 {
1410 // Cannot rename a non-directory over an existing directory.
1411 ec = std::make_error_code(std::errc::is_a_directory);
1412 return;
1413 }
1414 }
1415 else if (fs::is_directory(from_status))
1416 {
1417 // Cannot rename a directory over an existing non-directory.
1418 ec = std::make_error_code(std::errc::not_a_directory);
1419 return;
1420 }
1421 }
1422 #endif
1423 if (posix::rename(from.c_str(), to.c_str()))
1424 ec.assign(errno, std::generic_category());
1425 else
1426 ec.clear();
1427 }
1428
1429 void
resize_file(const path & p,uintmax_t size)1430 fs::resize_file(const path& p, uintmax_t size)
1431 {
1432 error_code ec;
1433 resize_file(p, size, ec);
1434 if (ec)
1435 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1436 }
1437
1438 void
resize_file(const path & p,uintmax_t size,error_code & ec)1439 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1440 {
1441 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1442 ec.assign(EINVAL, std::generic_category());
1443 else if (posix::truncate(p.c_str(), size))
1444 ec.assign(errno, std::generic_category());
1445 else
1446 ec.clear();
1447 }
1448
1449
1450 fs::space_info
space(const path & p)1451 fs::space(const path& p)
1452 {
1453 error_code ec;
1454 space_info s = space(p, ec);
1455 if (ec)
1456 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1457 return s;
1458 }
1459
1460 fs::space_info
space(const path & p,error_code & ec)1461 fs::space(const path& p, error_code& ec) noexcept
1462 {
1463 space_info info = {
1464 static_cast<uintmax_t>(-1),
1465 static_cast<uintmax_t>(-1),
1466 static_cast<uintmax_t>(-1)
1467 };
1468 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1469 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1470 path dir = absolute(p);
1471 dir.remove_filename();
1472 auto str = dir.c_str();
1473 #else
1474 auto str = p.c_str();
1475 #endif
1476
1477 do_space(str, info.capacity, info.free, info.available, ec);
1478 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1479
1480 return info;
1481 }
1482
1483 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1484 fs::file_status
status(const fs::path & p,error_code & ec)1485 fs::status(const fs::path& p, error_code& ec) noexcept
1486 {
1487 file_status status;
1488 auto str = p.c_str();
1489
1490 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1491 // stat() fails if there's a trailing slash (PR 88881)
1492 path p2;
1493 if (p.has_relative_path() && !p.has_filename())
1494 {
1495 __try
1496 {
1497 p2 = p.parent_path();
1498 str = p2.c_str();
1499 }
1500 __catch(const bad_alloc&)
1501 {
1502 ec = std::make_error_code(std::errc::not_enough_memory);
1503 return status;
1504 }
1505 str = p2.c_str();
1506 }
1507 #endif
1508
1509 stat_type st;
1510 if (posix::stat(str, &st))
1511 {
1512 int err = errno;
1513 ec.assign(err, std::generic_category());
1514 if (is_not_found_errno(err))
1515 status.type(file_type::not_found);
1516 #ifdef EOVERFLOW
1517 else if (err == EOVERFLOW)
1518 status.type(file_type::unknown);
1519 #endif
1520 }
1521 else
1522 {
1523 status = make_file_status(st);
1524 ec.clear();
1525 }
1526 return status;
1527 }
1528
1529 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1530 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1531 {
1532 file_status status;
1533 auto str = p.c_str();
1534
1535 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1536 // stat() fails if there's a trailing slash (PR 88881)
1537 path p2;
1538 if (p.has_relative_path() && !p.has_filename())
1539 {
1540 __try
1541 {
1542 p2 = p.parent_path();
1543 str = p2.c_str();
1544 }
1545 __catch(const bad_alloc&)
1546 {
1547 ec = std::make_error_code(std::errc::not_enough_memory);
1548 return status;
1549 }
1550 str = p2.c_str();
1551 }
1552 #endif
1553
1554 stat_type st;
1555 if (posix::lstat(str, &st))
1556 {
1557 int err = errno;
1558 ec.assign(err, std::generic_category());
1559 if (is_not_found_errno(err))
1560 status.type(file_type::not_found);
1561 }
1562 else
1563 {
1564 status = make_file_status(st);
1565 ec.clear();
1566 }
1567 return status;
1568 }
1569 #endif
1570
1571 fs::file_status
status(const fs::path & p)1572 fs::status(const fs::path& p)
1573 {
1574 std::error_code ec;
1575 auto result = status(p, ec);
1576 if (result.type() == file_type::none)
1577 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1578 return result;
1579 }
1580
1581 fs::file_status
symlink_status(const fs::path & p)1582 fs::symlink_status(const fs::path& p)
1583 {
1584 std::error_code ec;
1585 auto result = symlink_status(p, ec);
1586 if (result.type() == file_type::none)
1587 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1588 return result;
1589 }
1590
temp_directory_path()1591 fs::path fs::temp_directory_path()
1592 {
1593 error_code ec;
1594 path tmp = temp_directory_path(ec);
1595 if (ec)
1596 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1597 return tmp;
1598 }
1599
temp_directory_path(error_code & ec)1600 fs::path fs::temp_directory_path(error_code& ec)
1601 {
1602 path p;
1603 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1604 unsigned len = 1024;
1605 std::wstring buf;
1606 do
1607 {
1608 buf.resize(len);
1609 len = GetTempPathW(buf.size(), buf.data());
1610 } while (len > buf.size());
1611
1612 if (len == 0)
1613 {
1614 ec.assign((int)GetLastError(), std::system_category());
1615 return p;
1616 }
1617 buf.resize(len);
1618 p = std::move(buf);
1619 #else
1620 const char* tmpdir = nullptr;
1621 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1622 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1623 tmpdir = ::getenv(*e);
1624 p = tmpdir ? tmpdir : "/tmp";
1625 #endif
1626 auto st = status(p, ec);
1627 if (ec)
1628 p.clear();
1629 else if (!is_directory(st))
1630 {
1631 p.clear();
1632 ec = std::make_error_code(std::errc::not_a_directory);
1633 }
1634 return p;
1635 }
1636
1637 fs::path
weakly_canonical(const path & p)1638 fs::weakly_canonical(const path& p)
1639 {
1640 path result;
1641 if (exists(status(p)))
1642 return canonical(p);
1643
1644 path tmp;
1645 auto iter = p.begin(), end = p.end();
1646 // find leading elements of p that exist:
1647 while (iter != end)
1648 {
1649 tmp = result / *iter;
1650 if (exists(status(tmp)))
1651 swap(result, tmp);
1652 else
1653 break;
1654 ++iter;
1655 }
1656 // canonicalize:
1657 if (!result.empty())
1658 result = canonical(result);
1659 // append the non-existing elements:
1660 while (iter != end)
1661 result /= *iter++;
1662 // normalize:
1663 return result.lexically_normal();
1664 }
1665
1666 fs::path
weakly_canonical(const path & p,error_code & ec)1667 fs::weakly_canonical(const path& p, error_code& ec)
1668 {
1669 path result;
1670 file_status st = status(p, ec);
1671 if (exists(st))
1672 return canonical(p, ec);
1673 else if (status_known(st))
1674 ec.clear();
1675 else
1676 return result;
1677
1678 path tmp;
1679 auto iter = p.begin(), end = p.end();
1680 // find leading elements of p that exist:
1681 while (iter != end)
1682 {
1683 tmp = result / *iter;
1684 st = status(tmp, ec);
1685 if (exists(st))
1686 swap(result, tmp);
1687 else
1688 {
1689 if (status_known(st))
1690 ec.clear();
1691 break;
1692 }
1693 ++iter;
1694 }
1695 // canonicalize:
1696 if (!ec && !result.empty())
1697 result = canonical(result, ec);
1698 if (ec)
1699 result.clear();
1700 else
1701 {
1702 // append the non-existing elements:
1703 while (iter != end)
1704 result /= *iter++;
1705 // normalize:
1706 result = result.lexically_normal();
1707 }
1708 return result;
1709 }
1710