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 <system_error>
19 #include <type_traits>
23 #include "file_descriptor.h"
24 #include "path_parser.h"
25 #include "posix_compat.h"
26 #include "time_utils.h"
28 #if defined(_LIBCPP_WIN32API)
29 # define WIN32_LEAN_AND_MEAN
34 # include <sys/stat.h>
35 # include <sys/statvfs.h>
36 # include <sys/types.h>
39 #include <fcntl.h> /* values for fchmodat */
42 // since Linux 4.5 and FreeBSD 13, but the Linux libc wrapper is only provided by glibc and musl
43 #if (defined(__linux__) && (defined(__GLIBC__) || _LIBCPP_HAS_MUSL_LIBC)) || defined(__FreeBSD__)
44 # define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
46 #if __has_include(<sys/sendfile.h>)
47 # include <sys/sendfile.h>
48 # define _LIBCPP_FILESYSTEM_USE_SENDFILE
49 #elif defined(__APPLE__) || __has_include(<copyfile.h>)
50 # include <copyfile.h>
51 # define _LIBCPP_FILESYSTEM_USE_COPYFILE
53 # define _LIBCPP_FILESYSTEM_USE_FSTREAM
56 // sendfile and copy_file_range need to fall back
57 // to the fstream implementation for special files
58 #if (defined(_LIBCPP_FILESYSTEM_USE_SENDFILE) || defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE) || \
59 defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)) && \
60 _LIBCPP_HAS_LOCALIZATION
62 # define _LIBCPP_FILESYSTEM_NEED_FSTREAM
65 #if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB)
66 # pragma comment(lib, "rt")
69 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
71 using detail::capture_errno
;
72 using detail::ErrorHandler
;
74 using detail::TimeSpec
;
75 using parser::createView
;
76 using parser::PathParser
;
77 using parser::string_view_t
;
79 static path
__do_absolute(const path
& p
, path
* cwd
, error_code
* ec
) {
84 *cwd
= __current_path(ec
);
90 path
__absolute(const path
& p
, error_code
* ec
) {
92 return __do_absolute(p
, &cwd
, ec
);
95 path
__canonical(path
const& orig_p
, error_code
* ec
) {
97 ErrorHandler
<path
> err("canonical", ec
, &orig_p
, &cwd
);
99 path p
= __do_absolute(orig_p
, &cwd
, ec
);
100 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
101 std::unique_ptr
<path::value_type
, decltype(&::free
)> hold(detail::realpath(p
.c_str(), nullptr), &::free
);
102 if (hold
.get() == nullptr)
103 return err
.report(capture_errno());
106 # if defined(__MVS__) && !defined(PATH_MAX)
107 path::value_type buff
[_XOPEN_PATH_MAX
+ 1];
109 path::value_type buff
[PATH_MAX
+ 1];
111 path::value_type
* ret
;
112 if ((ret
= detail::realpath(p
.c_str(), buff
)) == nullptr)
113 return err
.report(capture_errno());
118 void __copy(const path
& from
, const path
& to
, copy_options options
, error_code
* ec
) {
119 ErrorHandler
<void> err("copy", ec
, &from
, &to
);
121 const bool sym_status
= bool(options
& (copy_options::create_symlinks
| copy_options::skip_symlinks
));
123 const bool sym_status2
= bool(options
& copy_options::copy_symlinks
);
127 const file_status f
=
128 sym_status
|| sym_status2
? detail::posix_lstat(from
, f_st
, &m_ec1
) : detail::posix_stat(from
, f_st
, &m_ec1
);
130 return err
.report(m_ec1
);
133 const file_status t
= sym_status
? detail::posix_lstat(to
, t_st
, &m_ec1
) : detail::posix_stat(to
, t_st
, &m_ec1
);
135 if (not status_known(t
))
136 return err
.report(m_ec1
);
138 if (!exists(f
) || is_other(f
) || is_other(t
) || (is_directory(f
) && is_regular_file(t
)) ||
139 (exists(t
) && detail::stat_equivalent(f_st
, t_st
))) {
140 return err
.report(errc::function_not_supported
);
144 if (bool(copy_options::skip_symlinks
& options
)) {
146 } else if (not exists(t
)) {
147 __copy_symlink(from
, to
, ec
);
149 return err
.report(errc::file_exists
);
152 } else if (is_regular_file(f
)) {
153 if (bool(copy_options::directories_only
& options
)) {
155 } else if (bool(copy_options::create_symlinks
& options
)) {
156 __create_symlink(from
, to
, ec
);
157 } else if (bool(copy_options::create_hard_links
& options
)) {
158 __create_hard_link(from
, to
, ec
);
159 } else if (is_directory(t
)) {
160 __copy_file(from
, to
/ from
.filename(), options
, ec
);
162 __copy_file(from
, to
, options
, ec
);
165 } else if (is_directory(f
) && bool(copy_options::create_symlinks
& options
)) {
166 return err
.report(errc::is_a_directory
);
167 } else if (is_directory(f
) && (bool(copy_options::recursive
& options
) || copy_options::none
== options
)) {
169 // create directory to with attributes from 'from'.
170 __create_directory(to
, from
, ec
);
175 directory_iterator it
= ec
? directory_iterator(from
, *ec
) : directory_iterator(from
);
180 for (; !m_ec2
&& it
!= directory_iterator(); it
.increment(m_ec2
)) {
181 __copy(it
->path(), to
/ it
->path().filename(), options
| copy_options::__in_recursive_copy
, ec
);
187 return err
.report(m_ec2
);
195 #if defined(_LIBCPP_FILESYSTEM_NEED_FSTREAM)
196 bool copy_file_impl_fstream(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
198 in
.__open(read_fd
.fd
, ios::binary
);
200 // This assumes that __open didn't reset the error code.
201 ec
= capture_errno();
206 out
.__open(write_fd
.fd
, ios::binary
);
207 if (!out
.is_open()) {
208 ec
= capture_errno();
213 if (in
.good() && out
.good()) {
214 using InIt
= istreambuf_iterator
<char>;
215 using OutIt
= ostreambuf_iterator
<char>;
219 copy(bin
, ein
, bout
);
221 if (out
.fail() || in
.fail()) {
222 ec
= make_error_code(errc::io_error
);
231 #if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
232 bool copy_file_impl_copy_file_range(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
233 size_t count
= read_fd
.get_stat().st_size
;
234 // a zero-length file is either empty, or not copyable by this syscall
235 // return early to avoid the syscall cost
237 ec
= {EINVAL
, generic_category()};
240 // do not modify the fd positions as copy_file_impl_sendfile may be called after a partial copy
241 # if defined(__linux__)
252 if ((res
= ::copy_file_range(read_fd
.fd
, &off_in
, write_fd
.fd
, &off_out
, count
, 0)) == -1) {
253 ec
= capture_errno();
265 #if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
266 bool copy_file_impl_sendfile(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
267 size_t count
= read_fd
.get_stat().st_size
;
268 // a zero-length file is either empty, or not copyable by this syscall
269 // return early to avoid the syscall cost
270 // however, we can't afford this luxury in the no-locale build,
271 // as we can't utilize the fstream impl to copy empty files
272 # if _LIBCPP_HAS_LOCALIZATION
274 ec
= {EINVAL
, generic_category()};
280 if ((res
= ::sendfile(write_fd
.fd
, read_fd
.fd
, nullptr, count
)) == -1) {
281 ec
= capture_errno();
293 #if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE) || defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
294 // If we have copy_file_range or sendfile, try both in succession (if available).
295 // If both fail, fall back to using fstream.
296 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
297 # if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
298 if (copy_file_impl_copy_file_range(read_fd
, write_fd
, ec
)) {
301 // EINVAL: src and dst are the same file (this is not cheaply
302 // detectable from userspace)
303 // EINVAL: copy_file_range is unsupported for this file type by the
304 // underlying filesystem
305 // ENOTSUP: undocumented, can arise with old kernels and NFS
306 // EOPNOTSUPP: filesystem does not implement copy_file_range
307 // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
308 // with normal copying)
309 // EXDEV: src and dst are on different filesystems that do not support
310 // cross-fs copy_file_range
311 // ENOENT: undocumented, can arise with CIFS
312 // ENOSYS: unsupported by kernel or blocked by seccomp
313 if (ec
.value() != EINVAL
&& ec
.value() != ENOTSUP
&& ec
.value() != EOPNOTSUPP
&& ec
.value() != ETXTBSY
&&
314 ec
.value() != EXDEV
&& ec
.value() != ENOENT
&& ec
.value() != ENOSYS
) {
320 # if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
321 if (copy_file_impl_sendfile(read_fd
, write_fd
, ec
)) {
324 // EINVAL: unsupported file type
325 if (ec
.value() != EINVAL
) {
331 # if defined(_LIBCPP_FILESYSTEM_NEED_FSTREAM)
332 return copy_file_impl_fstream(read_fd
, write_fd
, ec
);
334 // since iostreams are unavailable in the no-locale build, just fail after a failed sendfile
335 ec
.assign(EINVAL
, std::system_category());
339 #elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
340 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
341 struct CopyFileState
{
342 copyfile_state_t state
;
343 CopyFileState() { state
= copyfile_state_alloc(); }
344 ~CopyFileState() { copyfile_state_free(state
); }
347 CopyFileState(CopyFileState
const&) = delete;
348 CopyFileState
& operator=(CopyFileState
const&) = delete;
352 if (fcopyfile(read_fd
.fd
, write_fd
.fd
, cfs
.state
, COPYFILE_DATA
) < 0) {
353 ec
= capture_errno();
360 #elif defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)
361 bool copy_file_impl(FileDescriptor
& read_fd
, FileDescriptor
& write_fd
, error_code
& ec
) {
362 return copy_file_impl_fstream(read_fd
, write_fd
, ec
);
365 # error "Unknown implementation for copy_file_impl"
366 #endif // copy_file_impl implementation
368 } // end anonymous namespace
369 } // namespace detail
371 bool __copy_file(const path
& from
, const path
& to
, copy_options options
, error_code
* ec
) {
372 using detail::FileDescriptor
;
373 ErrorHandler
<bool> err("copy_file", ec
, &to
, &from
);
376 FileDescriptor from_fd
= FileDescriptor::create_with_status(&from
, m_ec
, O_RDONLY
| O_NONBLOCK
| O_BINARY
);
378 return err
.report(m_ec
);
380 auto from_st
= from_fd
.get_status();
381 StatT
const& from_stat
= from_fd
.get_stat();
382 if (!is_regular_file(from_st
)) {
384 m_ec
= make_error_code(errc::not_supported
);
385 return err
.report(m_ec
);
388 const bool skip_existing
= bool(copy_options::skip_existing
& options
);
389 const bool update_existing
= bool(copy_options::update_existing
& options
);
390 const bool overwrite_existing
= bool(copy_options::overwrite_existing
& options
);
393 file_status to_st
= detail::posix_stat(to
, to_stat_path
, &m_ec
);
394 if (!status_known(to_st
))
395 return err
.report(m_ec
);
397 const bool to_exists
= exists(to_st
);
398 if (to_exists
&& !is_regular_file(to_st
))
399 return err
.report(errc::not_supported
);
401 if (to_exists
&& detail::stat_equivalent(from_stat
, to_stat_path
))
402 return err
.report(errc::file_exists
);
404 if (to_exists
&& skip_existing
)
407 bool ShouldCopy
= [&]() {
408 if (to_exists
&& update_existing
) {
409 auto from_time
= detail::extract_mtime(from_stat
);
410 auto to_time
= detail::extract_mtime(to_stat_path
);
411 if (from_time
.tv_sec
< to_time
.tv_sec
)
413 if (from_time
.tv_sec
== to_time
.tv_sec
&& from_time
.tv_nsec
<= to_time
.tv_nsec
)
417 if (!to_exists
|| overwrite_existing
)
419 return err
.report(errc::file_exists
);
424 // Don't truncate right away. We may not be opening the file we originally
425 // looked at; we'll check this later.
426 int to_open_flags
= O_WRONLY
| O_BINARY
;
428 to_open_flags
|= O_CREAT
;
429 FileDescriptor to_fd
= FileDescriptor::create_with_status(&to
, m_ec
, to_open_flags
, from_stat
.st_mode
);
431 return err
.report(m_ec
);
434 // Check that the file we initially stat'ed is equivalent to the one
436 // FIXME: report this better.
437 if (!detail::stat_equivalent(to_stat_path
, to_fd
.get_stat()))
438 return err
.report(errc::bad_file_descriptor
);
440 // Set the permissions and truncate the file we opened.
441 if (detail::posix_fchmod(to_fd
, from_stat
, m_ec
))
442 return err
.report(m_ec
);
443 if (detail::posix_ftruncate(to_fd
, 0, m_ec
))
444 return err
.report(m_ec
);
447 if (!detail::copy_file_impl(from_fd
, to_fd
, m_ec
)) {
448 // FIXME: Remove the dest file if we failed, and it didn't exist previously.
449 return err
.report(m_ec
);
455 void __copy_symlink(const path
& existing_symlink
, const path
& new_symlink
, error_code
* ec
) {
456 const path
real_path(__read_symlink(existing_symlink
, ec
));
460 #if defined(_LIBCPP_WIN32API)
462 if (is_directory(real_path
, local_ec
))
463 __create_directory_symlink(real_path
, new_symlink
, ec
);
466 __create_symlink(real_path
, new_symlink
, ec
);
469 bool __create_directories(const path
& p
, error_code
* ec
) {
470 ErrorHandler
<bool> err("create_directories", ec
, &p
);
473 auto const st
= detail::posix_stat(p
, &m_ec
);
474 if (!status_known(st
))
475 return err
.report(m_ec
);
476 else if (is_directory(st
))
479 return err
.report(errc::file_exists
);
481 const path parent
= p
.parent_path();
482 if (!parent
.empty()) {
483 const file_status parent_st
= status(parent
, m_ec
);
484 if (not status_known(parent_st
))
485 return err
.report(m_ec
);
486 if (not exists(parent_st
)) {
488 return err
.report(errc::invalid_argument
);
489 __create_directories(parent
, ec
);
493 } else if (not is_directory(parent_st
))
494 return err
.report(errc::not_a_directory
);
496 bool ret
= __create_directory(p
, &m_ec
);
498 return err
.report(m_ec
);
502 bool __create_directory(const path
& p
, error_code
* ec
) {
503 ErrorHandler
<bool> err("create_directory", ec
, &p
);
505 if (detail::mkdir(p
.c_str(), static_cast<int>(perms::all
)) == 0)
509 return err
.report(capture_errno());
510 error_code mec
= capture_errno();
511 error_code ignored_ec
;
512 const file_status st
= status(p
, ignored_ec
);
513 if (!is_directory(st
))
514 return err
.report(mec
);
518 bool __create_directory(path
const& p
, path
const& attributes
, error_code
* ec
) {
519 ErrorHandler
<bool> err("create_directory", ec
, &p
, &attributes
);
523 file_status st
= detail::posix_stat(attributes
, attr_stat
, &mec
);
524 if (!status_known(st
))
525 return err
.report(mec
);
526 if (!is_directory(st
))
527 return err
.report(errc::not_a_directory
, "the specified attribute path is invalid");
529 if (detail::mkdir(p
.c_str(), attr_stat
.st_mode
) == 0)
533 return err
.report(capture_errno());
535 mec
= capture_errno();
536 error_code ignored_ec
;
537 st
= status(p
, ignored_ec
);
538 if (!is_directory(st
))
539 return err
.report(mec
);
543 void __create_directory_symlink(path
const& from
, path
const& to
, error_code
* ec
) {
544 ErrorHandler
<void> err("create_directory_symlink", ec
, &from
, &to
);
545 if (detail::symlink_dir(from
.c_str(), to
.c_str()) == -1)
546 return err
.report(capture_errno());
549 void __create_hard_link(const path
& from
, const path
& to
, error_code
* ec
) {
550 ErrorHandler
<void> err("create_hard_link", ec
, &from
, &to
);
551 if (detail::link(from
.c_str(), to
.c_str()) == -1)
552 return err
.report(capture_errno());
555 void __create_symlink(path
const& from
, path
const& to
, error_code
* ec
) {
556 ErrorHandler
<void> err("create_symlink", ec
, &from
, &to
);
557 if (detail::symlink_file(from
.c_str(), to
.c_str()) == -1)
558 return err
.report(capture_errno());
561 path
__current_path(error_code
* ec
) {
562 ErrorHandler
<path
> err("current_path", ec
);
564 #if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
565 // Common extension outside of POSIX getcwd() spec, without needing to
566 // preallocate a buffer. Also supported by a number of other POSIX libcs.
568 path::value_type
* ptr
= nullptr;
569 typedef decltype(&::free
) Deleter
;
570 Deleter deleter
= &::free
;
572 errno
= 0; // Note: POSIX mandates that modifying `errno` is thread-safe.
573 auto size
= ::pathconf(".", _PC_PATH_MAX
);
576 return err
.report(capture_errno(), "call to pathconf failed");
578 // `pathconf` returns `-1` without an error to indicate no limit.
580 # if defined(__MVS__) && !defined(PATH_MAX)
581 size
= _XOPEN_PATH_MAX
+ 1;
588 auto buff
= unique_ptr
<path::value_type
[]>(new path::value_type
[size
+ 1]);
589 path::value_type
* ptr
= buff
.get();
591 // Preallocated buffer, don't free the buffer in the second unique_ptr
594 void operator()(void*) const {}
599 unique_ptr
<path::value_type
, Deleter
> hold(detail::getcwd(ptr
, size
), deleter
);
600 if (hold
.get() == nullptr)
601 return err
.report(capture_errno(), "call to getcwd failed");
606 void __current_path(const path
& p
, error_code
* ec
) {
607 ErrorHandler
<void> err("current_path", ec
, &p
);
608 if (detail::chdir(p
.c_str()) == -1)
609 err
.report(capture_errno());
612 bool __equivalent(const path
& p1
, const path
& p2
, error_code
* ec
) {
613 ErrorHandler
<bool> err("equivalent", ec
, &p1
, &p2
);
616 StatT st1
= {}, st2
= {};
617 auto s1
= detail::posix_stat(p1
.native(), st1
, &ec1
);
619 return err
.report(errc::not_supported
);
620 auto s2
= detail::posix_stat(p2
.native(), st2
, &ec2
);
622 return err
.report(errc::not_supported
);
624 return detail::stat_equivalent(st1
, st2
);
627 uintmax_t __file_size(const path
& p
, error_code
* ec
) {
628 ErrorHandler
<uintmax_t> err("file_size", ec
, &p
);
632 file_status fst
= detail::posix_stat(p
, st
, &m_ec
);
633 if (!exists(fst
) || !is_regular_file(fst
)) {
634 errc error_kind
= is_directory(fst
) ? errc::is_a_directory
: errc::not_supported
;
636 m_ec
= make_error_code(error_kind
);
637 return err
.report(m_ec
);
639 // is_regular_file(p) == true
640 return static_cast<uintmax_t>(st
.st_size
);
643 uintmax_t __hard_link_count(const path
& p
, error_code
* ec
) {
644 ErrorHandler
<uintmax_t> err("hard_link_count", ec
, &p
);
648 detail::posix_stat(p
, st
, &m_ec
);
650 return err
.report(m_ec
);
651 return static_cast<uintmax_t>(st
.st_nlink
);
654 bool __fs_is_empty(const path
& p
, error_code
* ec
) {
655 ErrorHandler
<bool> err("is_empty", ec
, &p
);
659 auto st
= detail::posix_stat(p
, pst
, &m_ec
);
661 return err
.report(m_ec
);
662 else if (!is_directory(st
) && !is_regular_file(st
))
663 return err
.report(errc::not_supported
);
664 else if (is_directory(st
)) {
665 auto it
= ec
? directory_iterator(p
, *ec
) : directory_iterator(p
);
668 return it
== directory_iterator
{};
669 } else if (is_regular_file(st
))
670 return static_cast<uintmax_t>(pst
.st_size
) == 0;
672 __libcpp_unreachable();
675 file_time_type
__last_write_time(const path
& p
, error_code
* ec
) {
676 using namespace chrono
;
677 ErrorHandler
<file_time_type
> err("last_write_time", ec
, &p
);
681 detail::posix_stat(p
, st
, &m_ec
);
683 return err
.report(m_ec
);
684 return detail::__extract_last_write_time(p
, st
, ec
);
687 void __last_write_time(const path
& p
, file_time_type new_time
, error_code
* ec
) {
688 using detail::fs_time
;
689 ErrorHandler
<void> err("last_write_time", ec
, &p
);
691 #if defined(_LIBCPP_WIN32API)
693 if (!fs_time::convert_to_timespec(ts
, new_time
))
694 return err
.report(errc::value_too_large
);
695 detail::WinHandle
h(p
.c_str(), FILE_WRITE_ATTRIBUTES
, 0);
697 return err
.report(detail::make_windows_error(GetLastError()));
698 FILETIME last_write
= timespec_to_filetime(ts
);
699 if (!SetFileTime(h
, nullptr, nullptr, &last_write
))
700 return err
.report(detail::make_windows_error(GetLastError()));
703 array
<TimeSpec
, 2> tbuf
;
704 # if !defined(_LIBCPP_USE_UTIMENSAT)
705 // This implementation has a race condition between determining the
706 // last access time and attempting to set it to the same value using
709 file_status fst
= detail::posix_stat(p
, st
, &m_ec
);
711 return err
.report(m_ec
);
712 tbuf
[0] = detail::extract_atime(st
);
715 tbuf
[0].tv_nsec
= UTIME_OMIT
;
717 if (!fs_time::convert_to_timespec(tbuf
[1], new_time
))
718 return err
.report(errc::value_too_large
);
720 detail::set_file_times(p
, tbuf
, m_ec
);
722 return err
.report(m_ec
);
726 void __permissions(const path
& p
, perms prms
, perm_options opts
, error_code
* ec
) {
727 ErrorHandler
<void> err("permissions", ec
, &p
);
729 auto has_opt
= [&](perm_options o
) { return bool(o
& opts
); };
730 const bool resolve_symlinks
= !has_opt(perm_options::nofollow
);
731 const bool add_perms
= has_opt(perm_options::add
);
732 const bool remove_perms
= has_opt(perm_options::remove
);
733 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
734 (add_perms
+ remove_perms
+ has_opt(perm_options::replace
)) == 1,
735 "One and only one of the perm_options constants 'replace', 'add', or 'remove' must be present in opts");
737 bool set_sym_perms
= false;
739 if (!resolve_symlinks
|| (add_perms
|| remove_perms
)) {
741 file_status st
= resolve_symlinks
? detail::posix_stat(p
, &m_ec
) : detail::posix_lstat(p
, &m_ec
);
742 set_sym_perms
= is_symlink(st
);
744 return err
.report(m_ec
);
745 // TODO(hardening): double-check this assertion -- it might be a valid (if rare) case when the permissions are
747 _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(st
.permissions() != perms::unknown
, "Permissions unexpectedly unknown");
749 prms
|= st
.permissions();
750 else if (remove_perms
)
751 prms
= st
.permissions() & ~prms
;
753 const auto real_perms
= static_cast<detail::ModeT
>(prms
& perms::mask
);
755 #if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
756 const int flags
= set_sym_perms
? AT_SYMLINK_NOFOLLOW
: 0;
757 if (detail::fchmodat(AT_FDCWD
, p
.c_str(), real_perms
, flags
) == -1) {
758 return err
.report(capture_errno());
762 return err
.report(errc::operation_not_supported
);
763 if (::chmod(p
.c_str(), real_perms
) == -1) {
764 return err
.report(capture_errno());
769 path
__read_symlink(const path
& p
, error_code
* ec
) {
770 ErrorHandler
<path
> err("read_symlink", ec
, &p
);
772 #if defined(PATH_MAX) || defined(MAX_SYMLINK_SIZE)
774 void operator()(void*) const {}
776 # ifdef MAX_SYMLINK_SIZE
777 const size_t size
= MAX_SYMLINK_SIZE
+ 1;
779 const size_t size
= PATH_MAX
+ 1;
781 path::value_type stack_buff
[size
];
782 auto buff
= std::unique_ptr
<path::value_type
[], NullDeleter
>(stack_buff
);
785 if (detail::lstat(p
.c_str(), &sb
) == -1) {
786 return err
.report(capture_errno());
788 const size_t size
= sb
.st_size
+ 1;
789 auto buff
= unique_ptr
<path::value_type
[]>(new path::value_type
[size
]);
792 if ((ret
= detail::readlink(p
.c_str(), buff
.get(), size
)) == -1)
793 return err
.report(capture_errno());
794 // Note that `ret` returning `0` would work, resulting in a valid empty string being returned.
795 if (static_cast<size_t>(ret
) >= size
)
796 return err
.report(errc::value_too_large
);
801 bool __remove(const path
& p
, error_code
* ec
) {
802 ErrorHandler
<bool> err("remove", ec
, &p
);
803 if (detail::remove(p
.c_str()) == -1) {
805 err
.report(capture_errno());
811 // We currently have two implementations of `__remove_all`. The first one is general and
812 // used on platforms where we don't have access to the `openat()` family of POSIX functions.
813 // That implementation uses `directory_iterator`, however it is vulnerable to some race
814 // conditions, see https://reviews.llvm.org/D118134 for details.
816 // The second implementation is used on platforms where `openat()` & friends are available,
817 // and it threads file descriptors through recursive calls to avoid such race conditions.
818 #if defined(_LIBCPP_WIN32API) || defined(__MVS__)
819 # define REMOVE_ALL_USE_DIRECTORY_ITERATOR
822 #if defined(REMOVE_ALL_USE_DIRECTORY_ITERATOR)
826 uintmax_t remove_all_impl(path
const& p
, error_code
& ec
) {
827 const auto npos
= static_cast<uintmax_t>(-1);
828 const file_status st
= __symlink_status(p
, &ec
);
832 if (is_directory(st
)) {
833 for (directory_iterator
it(p
, ec
); !ec
&& it
!= directory_iterator(); it
.increment(ec
)) {
834 auto other_count
= remove_all_impl(it
->path(), ec
);
837 count
+= other_count
;
842 if (!__remove(p
, &ec
))
849 uintmax_t __remove_all(const path
& p
, error_code
* ec
) {
850 ErrorHandler
<uintmax_t> err("remove_all", ec
, &p
);
853 auto count
= remove_all_impl(p
, mec
);
855 if (mec
== errc::no_such_file_or_directory
)
857 return err
.report(mec
);
862 #else // !REMOVE_ALL_USE_DIRECTORY_ITERATOR
866 template <class Cleanup
>
868 explicit scope_exit(Cleanup
const& cleanup
) : cleanup_(cleanup
) {}
870 ~scope_exit() { cleanup_(); }
875 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(scope_exit
);
877 uintmax_t remove_all_impl(int parent_directory
, const path
& p
, error_code
& ec
) {
878 // First, try to open the path as a directory.
879 const int options
= O_CLOEXEC
| O_RDONLY
| O_DIRECTORY
| O_NOFOLLOW
;
880 int fd
= ::openat(parent_directory
, p
.c_str(), options
);
882 // If that worked, iterate over the contents of the directory and
883 // remove everything in it, recursively.
884 DIR* stream
= ::fdopendir(fd
);
885 if (stream
== nullptr) {
887 ec
= detail::capture_errno();
890 // Note: `::closedir` will also close the associated file descriptor, so
891 // there should be no call to `close(fd)`.
892 scope_exit
close_stream([=] { ::closedir(stream
); });
896 auto [str
, type
] = detail::posix_readdir(stream
, ec
);
897 static_assert(std::is_same_v
<decltype(str
), std::string_view
>);
898 if (str
== "." || str
== "..") {
900 } else if (ec
|| str
.empty()) {
901 break; // we're done iterating through the directory
903 count
+= remove_all_impl(fd
, str
, ec
);
907 // Then, remove the now-empty directory itself.
908 if (::unlinkat(parent_directory
, p
.c_str(), AT_REMOVEDIR
) == -1) {
909 ec
= detail::capture_errno();
913 return count
+ 1; // the contents of the directory + the directory itself
916 ec
= detail::capture_errno();
918 // If we failed to open `p` because it didn't exist, it's not an
919 // error -- it might have moved or have been deleted already.
920 if (ec
== errc::no_such_file_or_directory
) {
925 // If opening `p` failed because it wasn't a directory, remove it as
926 // a normal file instead. Note that `openat()` can return either ENOTDIR
927 // or ELOOP depending on the exact reason of the failure. On FreeBSD it
928 // may return EMLINK instead of ELOOP, contradicting POSIX.
929 if (ec
== errc::not_a_directory
|| ec
== errc::too_many_symbolic_link_levels
|| ec
== errc::too_many_links
) {
931 if (::unlinkat(parent_directory
, p
.c_str(), /* flags = */ 0) == -1) {
932 ec
= detail::capture_errno();
938 // Otherwise, it's a real error -- we don't remove anything.
944 uintmax_t __remove_all(const path
& p
, error_code
* ec
) {
945 ErrorHandler
<uintmax_t> err("remove_all", ec
, &p
);
947 uintmax_t count
= remove_all_impl(AT_FDCWD
, p
, mec
);
949 return err
.report(mec
);
953 #endif // REMOVE_ALL_USE_DIRECTORY_ITERATOR
955 void __rename(const path
& from
, const path
& to
, error_code
* ec
) {
956 ErrorHandler
<void> err("rename", ec
, &from
, &to
);
957 if (detail::rename(from
.c_str(), to
.c_str()) == -1)
958 err
.report(capture_errno());
961 void __resize_file(const path
& p
, uintmax_t size
, error_code
* ec
) {
962 ErrorHandler
<void> err("resize_file", ec
, &p
);
963 if (detail::truncate(p
.c_str(), static_cast< ::off_t
>(size
)) == -1)
964 return err
.report(capture_errno());
967 space_info
__space(const path
& p
, error_code
* ec
) {
968 ErrorHandler
<void> err("space", ec
, &p
);
970 detail::StatVFS m_svfs
= {};
971 if (detail::statvfs(p
.c_str(), &m_svfs
) == -1) {
972 err
.report(capture_errno());
973 si
.capacity
= si
.free
= si
.available
= static_cast<uintmax_t>(-1);
976 // Multiply with overflow checking.
977 auto do_mult
= [&](uintmax_t& out
, uintmax_t other
) {
978 out
= other
* m_svfs
.f_frsize
;
979 if (other
== 0 || out
/ other
!= m_svfs
.f_frsize
)
980 out
= static_cast<uintmax_t>(-1);
982 do_mult(si
.capacity
, m_svfs
.f_blocks
);
983 do_mult(si
.free
, m_svfs
.f_bfree
);
984 do_mult(si
.available
, m_svfs
.f_bavail
);
988 file_status
__status(const path
& p
, error_code
* ec
) { return detail::posix_stat(p
, ec
); }
990 file_status
__symlink_status(const path
& p
, error_code
* ec
) { return detail::posix_lstat(p
, ec
); }
992 path
__temp_directory_path(error_code
* ec
) {
993 ErrorHandler
<path
> err("temp_directory_path", ec
);
995 #if defined(_LIBCPP_WIN32API)
996 wchar_t buf
[MAX_PATH
];
997 DWORD retval
= GetTempPathW(MAX_PATH
, buf
);
999 return err
.report(detail::make_windows_error(GetLastError()));
1000 if (retval
> MAX_PATH
)
1001 return err
.report(errc::filename_too_long
);
1002 // GetTempPathW returns a path with a trailing slash, which we
1003 // shouldn't include for consistency.
1004 if (buf
[retval
- 1] == L
'\\')
1005 buf
[retval
- 1] = L
'\0';
1008 const char* env_paths
[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
1009 const char* ret
= nullptr;
1011 for (auto& ep
: env_paths
)
1012 if ((ret
= getenv(ep
)))
1014 if (ret
== nullptr) {
1015 # if defined(__ANDROID__)
1016 ret
= "/data/local/tmp";
1025 file_status st
= detail::posix_stat(p
, &m_ec
);
1026 if (!status_known(st
))
1027 return err
.report(m_ec
, "cannot access path " PATH_CSTR_FMT
, p
.c_str());
1029 if (!exists(st
) || !is_directory(st
))
1030 return err
.report(errc::not_a_directory
, "path " PATH_CSTR_FMT
" is not a directory", p
.c_str());
1035 path
__weakly_canonical(const path
& p
, error_code
* ec
) {
1036 ErrorHandler
<path
> err("weakly_canonical", ec
, &p
);
1039 return __canonical("", ec
);
1043 tmp
.__reserve(p
.native().size());
1044 auto PP
= PathParser::CreateEnd(p
.native());
1046 vector
<string_view_t
> DNEParts
;
1049 while (PP
.State_
!= PathParser::PS_BeforeBegin
) {
1050 tmp
.assign(createView(p
.native().data(), &PP
.RawEntry
.back()));
1051 file_status st
= __status(tmp
, &m_ec
);
1052 if (!status_known(st
)) {
1053 return err
.report(m_ec
);
1054 } else if (exists(st
)) {
1055 result
= __canonical(tmp
, &m_ec
);
1057 return err
.report(m_ec
);
1061 DNEParts
.push_back(*PP
);
1064 if (PP
.State_
== PathParser::PS_BeforeBegin
) {
1065 result
= __canonical("", &m_ec
);
1067 return err
.report(m_ec
);
1070 if (DNEParts
.empty())
1072 for (auto It
= DNEParts
.rbegin(); It
!= DNEParts
.rend(); ++It
)
1074 return result
.lexically_normal();
1077 _LIBCPP_END_NAMESPACE_FILESYSTEM