1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include <__utility/unreachable.h>
17 #include <string_view>
18 #include <type_traits>
22 #include "file_descriptor.h"
23 #include "path_parser.h"
24 #include "posix_compat.h"
25 #include "time_utils.h"
27 #if defined(_LIBCPP_WIN32API)
28 # define WIN32_LEAN_AND_MEAN
33 # include <sys/stat.h>
34 # include <sys/statvfs.h>
38 #include <fcntl.h> /* values for fchmodat */
40 #if __has_include(<sys/sendfile.h>)
41 # include <sys/sendfile.h>
42 # define _LIBCPP_FILESYSTEM_USE_SENDFILE
43 #elif defined(__APPLE__) || __has_include(<copyfile.h>)
44 # include <copyfile.h>
45 # define _LIBCPP_FILESYSTEM_USE_COPYFILE
48 # define _LIBCPP_FILESYSTEM_USE_FSTREAM
51 #if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB)
52 # pragma comment(lib, "rt")
55 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
57 using detail::capture_errno
;
58 using detail::ErrorHandler
;
60 using detail::TimeSpec
;
61 using parser::createView
;
62 using parser::PathParser
;
63 using parser::string_view_t
;
65 static path
__do_absolute(const path
& p
, path
* cwd
, error_code
* ec
) {
70 *cwd
= __current_path(ec
);
76 path
__absolute(const path
& p
, error_code
* ec
) {
78 return __do_absolute(p
, &cwd
, ec
);
81 path
__canonical(path
const& orig_p
, error_code
* ec
) {
83 ErrorHandler
<path
> err("canonical", ec
, &orig_p
, &cwd
);
85 path p
= __do_absolute(orig_p
, &cwd
, ec
);
86 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
87 std::unique_ptr
<path::value_type
, decltype(&::free
)>
88 hold(detail::realpath(p
.c_str(), nullptr), &::free
);
89 if (hold
.get() == nullptr)
90 return err
.report(capture_errno());
93 #if defined(__MVS__) && !defined(PATH_MAX)
94 path::value_type buff
[ _XOPEN_PATH_MAX
+ 1 ];
96 path::value_type buff
[PATH_MAX
+ 1];
98 path::value_type
* ret
;
99 if ((ret
= detail::realpath(p
.c_str(), buff
)) == nullptr)
100 return err
.report(capture_errno());
105 void __copy(const path
& from
, const path
& to
, copy_options options
,
107 ErrorHandler
<void> err("copy", ec
, &from
, &to
);
109 const bool sym_status
= bool(
110 options
& (copy_options::create_symlinks
| copy_options::skip_symlinks
));
112 const bool sym_status2
= bool(options
& copy_options::copy_symlinks
);
116 const file_status f
= sym_status
|| sym_status2
117 ? detail::posix_lstat(from
, f_st
, &m_ec1
)
118 : detail::posix_stat(from
, f_st
, &m_ec1
);
120 return err
.report(m_ec1
);
123 const file_status t
= sym_status
? detail::posix_lstat(to
, t_st
, &m_ec1
)
124 : detail::posix_stat(to
, t_st
, &m_ec1
);
126 if (not status_known(t
))
127 return err
.report(m_ec1
);
129 if (!exists(f
) || is_other(f
) || is_other(t
) ||
130 (is_directory(f
) && is_regular_file(t
)) ||
131 detail::stat_equivalent(f_st
, t_st
)) {
132 return err
.report(errc::function_not_supported
);
139 if (bool(copy_options::skip_symlinks
& options
)) {
141 } else if (not exists(t
)) {
142 __copy_symlink(from
, to
, ec
);
144 return err
.report(errc::file_exists
);
147 } else if (is_regular_file(f
)) {
148 if (bool(copy_options::directories_only
& options
)) {
150 } else if (bool(copy_options::create_symlinks
& options
)) {
151 __create_symlink(from
, to
, ec
);
152 } else if (bool(copy_options::create_hard_links
& options
)) {
153 __create_hard_link(from
, to
, ec
);
154 } else if (is_directory(t
)) {
155 __copy_file(from
, to
/ from
.filename(), options
, ec
);
157 __copy_file(from
, to
, options
, ec
);
160 } else if (is_directory(f
) && bool(copy_options::create_symlinks
& options
)) {
161 return err
.report(errc::is_a_directory
);
162 } else if (is_directory(f
) && (bool(copy_options::recursive
& options
) ||
163 copy_options::none
== options
)) {
166 // create directory to with attributes from 'from'.
167 __create_directory(to
, from
, ec
);
172 directory_iterator it
=
173 ec
? directory_iterator(from
, *ec
) : directory_iterator(from
);
178 for (; it
!= directory_iterator(); it
.increment(m_ec2
)) {
180 return err
.report(m_ec2
);
182 __copy(it
->path(), to
/ it
->path().filename(),
183 options
| copy_options::__in_recursive_copy
, ec
);
194 #if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
195 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
196 size_t count
= read_fd
.get_stat().st_size
;
199 if ((res
= ::sendfile(write_fd
.fd
, read_fd
.fd
, nullptr, count
)) == -1) {
200 ec
= capture_errno();
210 #elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
211 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
212 struct CopyFileState
{
213 copyfile_state_t state
;
214 CopyFileState() { state
= copyfile_state_alloc(); }
215 ~CopyFileState() { copyfile_state_free(state
); }
218 CopyFileState(CopyFileState
const&) = delete;
219 CopyFileState
& operator=(CopyFileState
const&) = delete;
223 if (fcopyfile(read_fd
.fd
, write_fd
.fd
, cfs
.state
, COPYFILE_DATA
) < 0) {
224 ec
= capture_errno();
231 #elif defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)
232 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
234 in
.__open(read_fd
.fd
, ios::binary
);
236 // This assumes that __open didn't reset the error code.
237 ec
= capture_errno();
242 out
.__open(write_fd
.fd
, ios::binary
);
243 if (!out
.is_open()) {
244 ec
= capture_errno();
249 if (in
.good() && out
.good()) {
250 using InIt
= istreambuf_iterator
<char>;
251 using OutIt
= ostreambuf_iterator
<char>;
255 copy(bin
, ein
, bout
);
257 if (out
.fail() || in
.fail()) {
258 ec
= make_error_code(errc::io_error
);
266 # error "Unknown implementation for copy_file_impl"
267 #endif // copy_file_impl implementation
269 } // end anonymous namespace
270 } // end namespace detail
272 bool __copy_file(const path
& from
, const path
& to
, copy_options options
,
274 using detail::FileDescriptor
;
275 ErrorHandler
<bool> err("copy_file", ec
, &to
, &from
);
278 FileDescriptor from_fd
= FileDescriptor::create_with_status(
279 &from
, m_ec
, O_RDONLY
| O_NONBLOCK
| O_BINARY
);
281 return err
.report(m_ec
);
283 auto from_st
= from_fd
.get_status();
284 StatT
const& from_stat
= from_fd
.get_stat();
285 if (!is_regular_file(from_st
)) {
287 m_ec
= make_error_code(errc::not_supported
);
288 return err
.report(m_ec
);
291 const bool skip_existing
= bool(copy_options::skip_existing
& options
);
292 const bool update_existing
= bool(copy_options::update_existing
& options
);
293 const bool overwrite_existing
=
294 bool(copy_options::overwrite_existing
& options
);
297 file_status to_st
= detail::posix_stat(to
, to_stat_path
, &m_ec
);
298 if (!status_known(to_st
))
299 return err
.report(m_ec
);
301 const bool to_exists
= exists(to_st
);
302 if (to_exists
&& !is_regular_file(to_st
))
303 return err
.report(errc::not_supported
);
305 if (to_exists
&& detail::stat_equivalent(from_stat
, to_stat_path
))
306 return err
.report(errc::file_exists
);
308 if (to_exists
&& skip_existing
)
311 bool ShouldCopy
= [&]() {
312 if (to_exists
&& update_existing
) {
313 auto from_time
= detail::extract_mtime(from_stat
);
314 auto to_time
= detail::extract_mtime(to_stat_path
);
315 if (from_time
.tv_sec
< to_time
.tv_sec
)
317 if (from_time
.tv_sec
== to_time
.tv_sec
&&
318 from_time
.tv_nsec
<= to_time
.tv_nsec
)
322 if (!to_exists
|| overwrite_existing
)
324 return err
.report(errc::file_exists
);
329 // Don't truncate right away. We may not be opening the file we originally
330 // looked at; we'll check this later.
331 int to_open_flags
= O_WRONLY
| O_BINARY
;
333 to_open_flags
|= O_CREAT
;
334 FileDescriptor to_fd
= FileDescriptor::create_with_status(
335 &to
, m_ec
, to_open_flags
, from_stat
.st_mode
);
337 return err
.report(m_ec
);
340 // Check that the file we initially stat'ed is equivalent to the one
342 // FIXME: report this better.
343 if (!detail::stat_equivalent(to_stat_path
, to_fd
.get_stat()))
344 return err
.report(errc::bad_file_descriptor
);
346 // Set the permissions and truncate the file we opened.
347 if (detail::posix_fchmod(to_fd
, from_stat
, m_ec
))
348 return err
.report(m_ec
);
349 if (detail::posix_ftruncate(to_fd
, 0, m_ec
))
350 return err
.report(m_ec
);
353 if (!detail::copy_file_impl(from_fd
, to_fd
, m_ec
)) {
354 // FIXME: Remove the dest file if we failed, and it didn't exist previously.
355 return err
.report(m_ec
);
361 void __copy_symlink(const path
& existing_symlink
, const path
& new_symlink
,
363 const path
real_path(__read_symlink(existing_symlink
, ec
));
367 #if defined(_LIBCPP_WIN32API)
369 if (is_directory(real_path
, local_ec
))
370 __create_directory_symlink(real_path
, new_symlink
, ec
);
373 __create_symlink(real_path
, new_symlink
, ec
);
376 bool __create_directories(const path
& p
, error_code
* ec
) {
377 ErrorHandler
<bool> err("create_directories", ec
, &p
);
380 auto const st
= detail::posix_stat(p
, &m_ec
);
381 if (!status_known(st
))
382 return err
.report(m_ec
);
383 else if (is_directory(st
))
386 return err
.report(errc::file_exists
);
388 const path parent
= p
.parent_path();
389 if (!parent
.empty()) {
390 const file_status parent_st
= status(parent
, m_ec
);
391 if (not status_known(parent_st
))
392 return err
.report(m_ec
);
393 if (not exists(parent_st
)) {
395 return err
.report(errc::invalid_argument
);
396 __create_directories(parent
, ec
);
400 } else if (not is_directory(parent_st
))
401 return err
.report(errc::not_a_directory
);
403 bool ret
= __create_directory(p
, &m_ec
);
405 return err
.report(m_ec
);
409 bool __create_directory(const path
& p
, error_code
* ec
) {
410 ErrorHandler
<bool> err("create_directory", ec
, &p
);
412 if (detail::mkdir(p
.c_str(), static_cast<int>(perms::all
)) == 0)
416 return err
.report(capture_errno());
417 error_code mec
= capture_errno();
418 error_code ignored_ec
;
419 const file_status st
= status(p
, ignored_ec
);
420 if (!is_directory(st
))
421 return err
.report(mec
);
425 bool __create_directory(path
const& p
, path
const& attributes
, error_code
* ec
) {
426 ErrorHandler
<bool> err("create_directory", ec
, &p
, &attributes
);
430 file_status st
= detail::posix_stat(attributes
, attr_stat
, &mec
);
431 if (!status_known(st
))
432 return err
.report(mec
);
433 if (!is_directory(st
))
434 return err
.report(errc::not_a_directory
,
435 "the specified attribute path is invalid");
437 if (detail::mkdir(p
.c_str(), attr_stat
.st_mode
) == 0)
441 return err
.report(capture_errno());
443 mec
= capture_errno();
444 error_code ignored_ec
;
445 st
= status(p
, ignored_ec
);
446 if (!is_directory(st
))
447 return err
.report(mec
);
451 void __create_directory_symlink(path
const& from
, path
const& to
,
453 ErrorHandler
<void> err("create_directory_symlink", ec
, &from
, &to
);
454 if (detail::symlink_dir(from
.c_str(), to
.c_str()) == -1)
455 return err
.report(capture_errno());
458 void __create_hard_link(const path
& from
, const path
& to
, error_code
* ec
) {
459 ErrorHandler
<void> err("create_hard_link", ec
, &from
, &to
);
460 if (detail::link(from
.c_str(), to
.c_str()) == -1)
461 return err
.report(capture_errno());
464 void __create_symlink(path
const& from
, path
const& to
, error_code
* ec
) {
465 ErrorHandler
<void> err("create_symlink", ec
, &from
, &to
);
466 if (detail::symlink_file(from
.c_str(), to
.c_str()) == -1)
467 return err
.report(capture_errno());
470 path
__current_path(error_code
* ec
) {
471 ErrorHandler
<path
> err("current_path", ec
);
473 #if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
474 // Common extension outside of POSIX getcwd() spec, without needing to
475 // preallocate a buffer. Also supported by a number of other POSIX libcs.
477 path::value_type
* ptr
= nullptr;
478 typedef decltype(&::free
) Deleter
;
479 Deleter deleter
= &::free
;
481 auto size
= ::pathconf(".", _PC_PATH_MAX
);
482 _LIBCPP_ASSERT_UNCATEGORIZED(size
>= 0, "pathconf returned a 0 as max size");
484 auto buff
= unique_ptr
<path::value_type
[]>(new path::value_type
[size
+ 1]);
485 path::value_type
* ptr
= buff
.get();
487 // Preallocated buffer, don't free the buffer in the second unique_ptr
489 struct Deleter
{ void operator()(void*) const {} };
493 unique_ptr
<path::value_type
, Deleter
> hold(detail::getcwd(ptr
, size
),
495 if (hold
.get() == nullptr)
496 return err
.report(capture_errno(), "call to getcwd failed");
501 void __current_path(const path
& p
, error_code
* ec
) {
502 ErrorHandler
<void> err("current_path", ec
, &p
);
503 if (detail::chdir(p
.c_str()) == -1)
504 err
.report(capture_errno());
507 bool __equivalent(const path
& p1
, const path
& p2
, error_code
* ec
) {
508 ErrorHandler
<bool> err("equivalent", ec
, &p1
, &p2
);
511 StatT st1
= {}, st2
= {};
512 auto s1
= detail::posix_stat(p1
.native(), st1
, &ec1
);
514 return err
.report(errc::not_supported
);
515 auto s2
= detail::posix_stat(p2
.native(), st2
, &ec2
);
517 return err
.report(errc::not_supported
);
519 return detail::stat_equivalent(st1
, st2
);
522 uintmax_t __file_size(const path
& p
, error_code
* ec
) {
523 ErrorHandler
<uintmax_t> err("file_size", ec
, &p
);
527 file_status fst
= detail::posix_stat(p
, st
, &m_ec
);
528 if (!exists(fst
) || !is_regular_file(fst
)) {
530 is_directory(fst
) ? errc::is_a_directory
: errc::not_supported
;
532 m_ec
= make_error_code(error_kind
);
533 return err
.report(m_ec
);
535 // is_regular_file(p) == true
536 return static_cast<uintmax_t>(st
.st_size
);
539 uintmax_t __hard_link_count(const path
& p
, error_code
* ec
) {
540 ErrorHandler
<uintmax_t> err("hard_link_count", ec
, &p
);
544 detail::posix_stat(p
, st
, &m_ec
);
546 return err
.report(m_ec
);
547 return static_cast<uintmax_t>(st
.st_nlink
);
550 bool __fs_is_empty(const path
& p
, error_code
* ec
) {
551 ErrorHandler
<bool> err("is_empty", ec
, &p
);
555 auto st
= detail::posix_stat(p
, pst
, &m_ec
);
557 return err
.report(m_ec
);
558 else if (!is_directory(st
) && !is_regular_file(st
))
559 return err
.report(errc::not_supported
);
560 else if (is_directory(st
)) {
561 auto it
= ec
? directory_iterator(p
, *ec
) : directory_iterator(p
);
564 return it
== directory_iterator
{};
565 } else if (is_regular_file(st
))
566 return static_cast<uintmax_t>(pst
.st_size
) == 0;
568 __libcpp_unreachable();
571 file_time_type
__last_write_time(const path
& p
, error_code
* ec
) {
572 using namespace chrono
;
573 ErrorHandler
<file_time_type
> err("last_write_time", ec
, &p
);
577 detail::posix_stat(p
, st
, &m_ec
);
579 return err
.report(m_ec
);
580 return detail::__extract_last_write_time(p
, st
, ec
);
583 void __last_write_time(const path
& p
, file_time_type new_time
, error_code
* ec
) {
584 using detail::fs_time
;
585 ErrorHandler
<void> err("last_write_time", ec
, &p
);
587 #if defined(_LIBCPP_WIN32API)
589 if (!fs_time::convert_to_timespec(ts
, new_time
))
590 return err
.report(errc::value_too_large
);
591 detail::WinHandle
h(p
.c_str(), FILE_WRITE_ATTRIBUTES
, 0);
593 return err
.report(detail::make_windows_error(GetLastError()));
594 FILETIME last_write
= timespec_to_filetime(ts
);
595 if (!SetFileTime(h
, nullptr, nullptr, &last_write
))
596 return err
.report(detail::make_windows_error(GetLastError()));
599 array
<TimeSpec
, 2> tbuf
;
600 #if !defined(_LIBCPP_USE_UTIMENSAT)
601 // This implementation has a race condition between determining the
602 // last access time and attempting to set it to the same value using
605 file_status fst
= detail::posix_stat(p
, st
, &m_ec
);
607 return err
.report(m_ec
);
608 tbuf
[0] = detail::extract_atime(st
);
611 tbuf
[0].tv_nsec
= UTIME_OMIT
;
613 if (!fs_time::convert_to_timespec(tbuf
[1], new_time
))
614 return err
.report(errc::value_too_large
);
616 detail::set_file_times(p
, tbuf
, m_ec
);
618 return err
.report(m_ec
);
622 void __permissions(const path
& p
, perms prms
, perm_options opts
,
624 ErrorHandler
<void> err("permissions", ec
, &p
);
626 auto has_opt
= [&](perm_options o
) { return bool(o
& opts
); };
627 const bool resolve_symlinks
= !has_opt(perm_options::nofollow
);
628 const bool add_perms
= has_opt(perm_options::add
);
629 const bool remove_perms
= has_opt(perm_options::remove
);
630 _LIBCPP_ASSERT_UNCATEGORIZED(
631 (add_perms
+ remove_perms
+ has_opt(perm_options::replace
)) == 1,
632 "One and only one of the perm_options constants replace, add, or remove "
633 "is present in opts");
635 bool set_sym_perms
= false;
637 if (!resolve_symlinks
|| (add_perms
|| remove_perms
)) {
639 file_status st
= resolve_symlinks
? detail::posix_stat(p
, &m_ec
)
640 : detail::posix_lstat(p
, &m_ec
);
641 set_sym_perms
= is_symlink(st
);
643 return err
.report(m_ec
);
644 _LIBCPP_ASSERT_UNCATEGORIZED(st
.permissions() != perms::unknown
,
645 "Permissions unexpectedly unknown");
647 prms
|= st
.permissions();
648 else if (remove_perms
)
649 prms
= st
.permissions() & ~prms
;
651 const auto real_perms
= static_cast<detail::ModeT
>(prms
& perms::mask
);
653 #if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
654 const int flags
= set_sym_perms
? AT_SYMLINK_NOFOLLOW
: 0;
655 if (detail::fchmodat(AT_FDCWD
, p
.c_str(), real_perms
, flags
) == -1) {
656 return err
.report(capture_errno());
660 return err
.report(errc::operation_not_supported
);
661 if (::chmod(p
.c_str(), real_perms
) == -1) {
662 return err
.report(capture_errno());
667 path
__read_symlink(const path
& p
, error_code
* ec
) {
668 ErrorHandler
<path
> err("read_symlink", ec
, &p
);
670 #if defined(PATH_MAX) || defined(MAX_SYMLINK_SIZE)
671 struct NullDeleter
{ void operator()(void*) const {} };
672 #ifdef MAX_SYMLINK_SIZE
673 const size_t size
= MAX_SYMLINK_SIZE
+ 1;
675 const size_t size
= PATH_MAX
+ 1;
677 path::value_type stack_buff
[size
];
678 auto buff
= std::unique_ptr
<path::value_type
[], NullDeleter
>(stack_buff
);
681 if (detail::lstat(p
.c_str(), &sb
) == -1) {
682 return err
.report(capture_errno());
684 const size_t size
= sb
.st_size
+ 1;
685 auto buff
= unique_ptr
<path::value_type
[]>(new path::value_type
[size
]);
688 if ((ret
= detail::readlink(p
.c_str(), buff
.get(), size
)) == -1)
689 return err
.report(capture_errno());
690 _LIBCPP_ASSERT_UNCATEGORIZED(ret
> 0, "TODO");
691 if (static_cast<size_t>(ret
) >= size
)
692 return err
.report(errc::value_too_large
);
697 bool __remove(const path
& p
, error_code
* ec
) {
698 ErrorHandler
<bool> err("remove", ec
, &p
);
699 if (detail::remove(p
.c_str()) == -1) {
701 err
.report(capture_errno());
707 // We currently have two implementations of `__remove_all`. The first one is general and
708 // used on platforms where we don't have access to the `openat()` family of POSIX functions.
709 // That implementation uses `directory_iterator`, however it is vulnerable to some race
710 // conditions, see https://reviews.llvm.org/D118134 for details.
712 // The second implementation is used on platforms where `openat()` & friends are available,
713 // and it threads file descriptors through recursive calls to avoid such race conditions.
714 #if defined(_LIBCPP_WIN32API) || defined (__MVS__)
715 # define REMOVE_ALL_USE_DIRECTORY_ITERATOR
718 #if defined(REMOVE_ALL_USE_DIRECTORY_ITERATOR)
722 uintmax_t remove_all_impl(path
const& p
, error_code
& ec
) {
723 const auto npos
= static_cast<uintmax_t>(-1);
724 const file_status st
= __symlink_status(p
, &ec
);
728 if (is_directory(st
)) {
729 for (directory_iterator
it(p
, ec
); !ec
&& it
!= directory_iterator();
731 auto other_count
= remove_all_impl(it
->path(), ec
);
734 count
+= other_count
;
739 if (!__remove(p
, &ec
))
746 uintmax_t __remove_all(const path
& p
, error_code
* ec
) {
747 ErrorHandler
<uintmax_t> err("remove_all", ec
, &p
);
750 auto count
= remove_all_impl(p
, mec
);
752 if (mec
== errc::no_such_file_or_directory
)
754 return err
.report(mec
);
759 #else // !REMOVE_ALL_USE_DIRECTORY_ITERATOR
763 template <class Cleanup
>
765 explicit scope_exit(Cleanup
const& cleanup
)
769 ~scope_exit() { cleanup_(); }
774 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(scope_exit
);
776 uintmax_t remove_all_impl(int parent_directory
, const path
& p
, error_code
& ec
) {
777 // First, try to open the path as a directory.
778 const int options
= O_CLOEXEC
| O_RDONLY
| O_DIRECTORY
| O_NOFOLLOW
;
779 int fd
= ::openat(parent_directory
, p
.c_str(), options
);
781 // If that worked, iterate over the contents of the directory and
782 // remove everything in it, recursively.
783 DIR* stream
= ::fdopendir(fd
);
784 if (stream
== nullptr) {
786 ec
= detail::capture_errno();
789 // Note: `::closedir` will also close the associated file descriptor, so
790 // there should be no call to `close(fd)`.
791 scope_exit
close_stream([=] { ::closedir(stream
); });
795 auto [str
, type
] = detail::posix_readdir(stream
, ec
);
796 static_assert(std::is_same_v
<decltype(str
), std::string_view
>);
797 if (str
== "." || str
== "..") {
799 } else if (ec
|| str
.empty()) {
800 break; // we're done iterating through the directory
802 count
+= remove_all_impl(fd
, str
, ec
);
806 // Then, remove the now-empty directory itself.
807 if (::unlinkat(parent_directory
, p
.c_str(), AT_REMOVEDIR
) == -1) {
808 ec
= detail::capture_errno();
812 return count
+ 1; // the contents of the directory + the directory itself
815 ec
= detail::capture_errno();
817 // If we failed to open `p` because it didn't exist, it's not an
818 // error -- it might have moved or have been deleted already.
819 if (ec
== errc::no_such_file_or_directory
) {
824 // If opening `p` failed because it wasn't a directory, remove it as
825 // a normal file instead. Note that `openat()` can return either ENOTDIR
826 // or ELOOP depending on the exact reason of the failure.
827 if (ec
== errc::not_a_directory
|| ec
== errc::too_many_symbolic_link_levels
) {
829 if (::unlinkat(parent_directory
, p
.c_str(), /* flags = */0) == -1) {
830 ec
= detail::capture_errno();
836 // Otherwise, it's a real error -- we don't remove anything.
842 uintmax_t __remove_all(const path
& p
, error_code
* ec
) {
843 ErrorHandler
<uintmax_t> err("remove_all", ec
, &p
);
845 uintmax_t count
= remove_all_impl(AT_FDCWD
, p
, mec
);
847 return err
.report(mec
);
851 #endif // REMOVE_ALL_USE_DIRECTORY_ITERATOR
853 void __rename(const path
& from
, const path
& to
, error_code
* ec
) {
854 ErrorHandler
<void> err("rename", ec
, &from
, &to
);
855 if (detail::rename(from
.c_str(), to
.c_str()) == -1)
856 err
.report(capture_errno());
859 void __resize_file(const path
& p
, uintmax_t size
, error_code
* ec
) {
860 ErrorHandler
<void> err("resize_file", ec
, &p
);
861 if (detail::truncate(p
.c_str(), static_cast< ::off_t
>(size
)) == -1)
862 return err
.report(capture_errno());
865 space_info
__space(const path
& p
, error_code
* ec
) {
866 ErrorHandler
<void> err("space", ec
, &p
);
868 detail::StatVFS m_svfs
= {};
869 if (detail::statvfs(p
.c_str(), &m_svfs
) == -1) {
870 err
.report(capture_errno());
871 si
.capacity
= si
.free
= si
.available
= static_cast<uintmax_t>(-1);
874 // Multiply with overflow checking.
875 auto do_mult
= [&](uintmax_t& out
, uintmax_t other
) {
876 out
= other
* m_svfs
.f_frsize
;
877 if (other
== 0 || out
/ other
!= m_svfs
.f_frsize
)
878 out
= static_cast<uintmax_t>(-1);
880 do_mult(si
.capacity
, m_svfs
.f_blocks
);
881 do_mult(si
.free
, m_svfs
.f_bfree
);
882 do_mult(si
.available
, m_svfs
.f_bavail
);
886 file_status
__status(const path
& p
, error_code
* ec
) {
887 return detail::posix_stat(p
, ec
);
890 file_status
__symlink_status(const path
& p
, error_code
* ec
) {
891 return detail::posix_lstat(p
, ec
);
894 path
__temp_directory_path(error_code
* ec
) {
895 ErrorHandler
<path
> err("temp_directory_path", ec
);
897 #if defined(_LIBCPP_WIN32API)
898 wchar_t buf
[MAX_PATH
];
899 DWORD retval
= GetTempPathW(MAX_PATH
, buf
);
901 return err
.report(detail::make_windows_error(GetLastError()));
902 if (retval
> MAX_PATH
)
903 return err
.report(errc::filename_too_long
);
904 // GetTempPathW returns a path with a trailing slash, which we
905 // shouldn't include for consistency.
906 if (buf
[retval
-1] == L
'\\')
907 buf
[retval
-1] = L
'\0';
910 const char* env_paths
[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
911 const char* ret
= nullptr;
913 for (auto& ep
: env_paths
)
914 if ((ret
= getenv(ep
)))
916 if (ret
== nullptr) {
917 #if defined(__ANDROID__)
918 ret
= "/data/local/tmp";
927 file_status st
= detail::posix_stat(p
, &m_ec
);
928 if (!status_known(st
))
929 return err
.report(m_ec
, "cannot access path " PATH_CSTR_FMT
, p
.c_str());
931 if (!exists(st
) || !is_directory(st
))
932 return err
.report(errc::not_a_directory
,
933 "path " PATH_CSTR_FMT
" is not a directory", p
.c_str());
938 path
__weakly_canonical(const path
& p
, error_code
* ec
) {
939 ErrorHandler
<path
> err("weakly_canonical", ec
, &p
);
942 return __canonical("", ec
);
946 tmp
.__reserve(p
.native().size());
947 auto PP
= PathParser::CreateEnd(p
.native());
949 vector
<string_view_t
> DNEParts
;
951 while (PP
.State
!= PathParser::PS_BeforeBegin
) {
952 tmp
.assign(createView(p
.native().data(), &PP
.RawEntry
.back()));
954 file_status st
= __status(tmp
, &m_ec
);
955 if (!status_known(st
)) {
956 return err
.report(m_ec
);
957 } else if (exists(st
)) {
958 result
= __canonical(tmp
, ec
);
961 DNEParts
.push_back(*PP
);
964 if (PP
.State
== PathParser::PS_BeforeBegin
)
965 result
= __canonical("", ec
);
968 if (DNEParts
.empty())
970 for (auto It
= DNEParts
.rbegin(); It
!= DNEParts
.rend(); ++It
)
972 return result
.lexically_normal();
975 _LIBCPP_END_NAMESPACE_FILESYSTEM