aboutsummaryrefslogtreecommitdiff
path: root/libcxx/src/filesystem
diff options
context:
space:
mode:
authorNAKAMURA Takumi <geek4civic@gmail.com>2025-01-09 18:49:54 +0900
committerNAKAMURA Takumi <geek4civic@gmail.com>2025-01-09 18:49:54 +0900
commite2810c9a248f4c7fbfae84bb32b6f7e01027458b (patch)
treeae0b02a8491b969a1cee94ea16ffe42c559143c5 /libcxx/src/filesystem
parentfa04eb4af95c1ca7377279728cb004bcd2324d01 (diff)
parentbdcf47e4bcb92889665825654bb80a8bbe30379e (diff)
downloadllvm-users/chapuni/cov/single/switch.zip
llvm-users/chapuni/cov/single/switch.tar.gz
llvm-users/chapuni/cov/single/switch.tar.bz2
Merge branch 'users/chapuni/cov/single/base' into users/chapuni/cov/single/switchusers/chapuni/cov/single/switch
Diffstat (limited to 'libcxx/src/filesystem')
-rw-r--r--libcxx/src/filesystem/directory_iterator.cpp10
-rw-r--r--libcxx/src/filesystem/error.h73
-rw-r--r--libcxx/src/filesystem/file_descriptor.h12
-rw-r--r--libcxx/src/filesystem/operations.cpp235
-rw-r--r--libcxx/src/filesystem/posix_compat.h73
5 files changed, 229 insertions, 174 deletions
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index d7ed9a3..7e8e40d 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -47,9 +47,9 @@ public:
}
__stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
if (__stream_ == INVALID_HANDLE_VALUE) {
- ec = detail::make_windows_error(GetLastError());
+ ec = detail::get_last_error();
const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied);
- if (ignore_permission_denied && ec.value() == static_cast<int>(errc::permission_denied))
+ if (ignore_permission_denied && ec == errc::permission_denied)
ec.clear();
return;
}
@@ -91,7 +91,7 @@ private:
error_code close() noexcept {
error_code ec;
if (!::FindClose(__stream_))
- ec = detail::make_windows_error(GetLastError());
+ ec = detail::get_last_error();
__stream_ = INVALID_HANDLE_VALUE;
return ec;
}
@@ -118,7 +118,7 @@ public:
if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
ec = detail::capture_errno();
const bool allow_eacces = bool(opts & directory_options::skip_permission_denied);
- if (allow_eacces && ec.value() == EACCES)
+ if (allow_eacces && ec == errc::permission_denied)
ec.clear();
return;
}
@@ -307,7 +307,7 @@ bool recursive_directory_iterator::__try_recursion(error_code* ec) {
}
if (m_ec) {
const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied);
- if (m_ec.value() == EACCES && allow_eacess) {
+ if (m_ec == errc::permission_denied && allow_eacess) {
if (ec)
ec->clear();
} else {
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
index 07ba7fc..c021391 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -32,80 +32,21 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
namespace detail {
-#if defined(_LIBCPP_WIN32API)
-
-inline errc __win_err_to_errc(int err) {
- constexpr struct {
- DWORD win;
- errc errc;
- } win_error_mapping[] = {
- {ERROR_ACCESS_DENIED, errc::permission_denied},
- {ERROR_ALREADY_EXISTS, errc::file_exists},
- {ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
- {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory},
- {ERROR_BAD_UNIT, errc::no_such_device},
- {ERROR_BROKEN_PIPE, errc::broken_pipe},
- {ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
- {ERROR_BUSY, errc::device_or_resource_busy},
- {ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
- {ERROR_CANNOT_MAKE, errc::permission_denied},
- {ERROR_CANTOPEN, errc::io_error},
- {ERROR_CANTREAD, errc::io_error},
- {ERROR_CANTWRITE, errc::io_error},
- {ERROR_CURRENT_DIRECTORY, errc::permission_denied},
- {ERROR_DEV_NOT_EXIST, errc::no_such_device},
- {ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
- {ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
- {ERROR_DIRECTORY, errc::invalid_argument},
- {ERROR_DISK_FULL, errc::no_space_on_device},
- {ERROR_FILE_EXISTS, errc::file_exists},
- {ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
- {ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
- {ERROR_INVALID_ACCESS, errc::permission_denied},
- {ERROR_INVALID_DRIVE, errc::no_such_device},
- {ERROR_INVALID_FUNCTION, errc::function_not_supported},
- {ERROR_INVALID_HANDLE, errc::invalid_argument},
- {ERROR_INVALID_NAME, errc::no_such_file_or_directory},
- {ERROR_INVALID_PARAMETER, errc::invalid_argument},
- {ERROR_LOCK_VIOLATION, errc::no_lock_available},
- {ERROR_LOCKED, errc::no_lock_available},
- {ERROR_NEGATIVE_SEEK, errc::invalid_argument},
- {ERROR_NOACCESS, errc::permission_denied},
- {ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
- {ERROR_NOT_READY, errc::resource_unavailable_try_again},
- {ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
- {ERROR_NOT_SUPPORTED, errc::not_supported},
- {ERROR_OPEN_FAILED, errc::io_error},
- {ERROR_OPEN_FILES, errc::device_or_resource_busy},
- {ERROR_OPERATION_ABORTED, errc::operation_canceled},
- {ERROR_OUTOFMEMORY, errc::not_enough_memory},
- {ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
- {ERROR_READ_FAULT, errc::io_error},
- {ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
- {ERROR_RETRY, errc::resource_unavailable_try_again},
- {ERROR_SEEK, errc::io_error},
- {ERROR_SHARING_VIOLATION, errc::permission_denied},
- {ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
- {ERROR_WRITE_FAULT, errc::io_error},
- {ERROR_WRITE_PROTECT, errc::permission_denied},
- };
-
- for (const auto& pair : win_error_mapping)
- if (pair.win == static_cast<DWORD>(err))
- return pair.errc;
- return errc::invalid_argument;
-}
-
-#endif // _LIBCPP_WIN32API
+// On windows, libc functions use errno, but system functions use GetLastError.
+// So, callers need to be careful which of these next functions they call!
inline error_code capture_errno() {
_LIBCPP_ASSERT_INTERNAL(errno != 0, "Expected errno to be non-zero");
return error_code(errno, generic_category());
}
+inline error_code get_last_error() {
#if defined(_LIBCPP_WIN32API)
-inline error_code make_windows_error(int err) { return make_error_code(__win_err_to_errc(err)); }
+ return std::error_code(GetLastError(), std::system_category());
+#else
+ return capture_errno();
#endif
+}
template <class T>
T error_value();
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
index db66ad5..9c279c4 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -201,7 +201,7 @@ inline perms posix_get_perms(const StatT& st) noexcept { return static_cast<perm
inline file_status create_file_status(error_code& m_ec, path const& p, const StatT& path_stat, error_code* ec) {
if (ec)
*ec = m_ec;
- if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
+ if (m_ec && (m_ec == errc::no_such_file_or_directory || m_ec == errc::not_a_directory)) {
return file_status(file_type::not_found);
} else if (m_ec) {
ErrorHandler<void> err("posix_stat", ec, &p);
@@ -236,7 +236,7 @@ inline file_status create_file_status(error_code& m_ec, path const& p, const Sta
inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
error_code m_ec;
if (detail::stat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
+ m_ec = detail::get_last_error();
return create_file_status(m_ec, p, path_stat, ec);
}
@@ -248,7 +248,7 @@ inline file_status posix_stat(path const& p, error_code* ec) {
inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
error_code m_ec;
if (detail::lstat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
+ m_ec = detail::get_last_error();
return create_file_status(m_ec, p, path_stat, ec);
}
@@ -260,7 +260,7 @@ inline file_status posix_lstat(path const& p, error_code* ec) {
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {
if (detail::ftruncate(fd.fd, to_size) == -1) {
- ec = capture_errno();
+ ec = get_last_error();
return true;
}
ec.clear();
@@ -269,7 +269,7 @@ inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code&
inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {
if (detail::fchmod(fd.fd, st.st_mode) == -1) {
- ec = capture_errno();
+ ec = get_last_error();
return true;
}
ec.clear();
@@ -286,7 +286,7 @@ inline file_status FileDescriptor::refresh_status(error_code& ec) {
m_stat = {};
error_code m_ec;
if (detail::fstat(fd, &m_stat) == -1)
- m_ec = capture_errno();
+ m_ec = get_last_error();
m_status = create_file_status(m_ec, name, m_stat, &ec);
return m_status;
}
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index d771f20..23c1c28 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -15,6 +15,7 @@
#include <filesystem>
#include <iterator>
#include <string_view>
+#include <system_error>
#include <type_traits>
#include <vector>
@@ -32,11 +33,24 @@
# include <dirent.h>
# include <sys/stat.h>
# include <sys/statvfs.h>
+# include <sys/types.h>
# include <unistd.h>
#endif
#include <fcntl.h> /* values for fchmodat */
#include <time.h>
+// since Linux 4.5 and FreeBSD 13, but the Linux libc wrapper is only provided by glibc >= 2.27 and musl
+#if defined(__linux__)
+# if defined(_LIBCPP_GLIBC_PREREQ)
+# if _LIBCPP_GLIBC_PREREQ(2, 27)
+# define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
+# endif
+# elif _LIBCPP_HAS_MUSL_LIBC
+# define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
+# endif
+#elif defined(__FreeBSD__)
+# define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
+#endif
#if __has_include(<sys/sendfile.h>)
# include <sys/sendfile.h>
# define _LIBCPP_FILESYSTEM_USE_SENDFILE
@@ -44,10 +58,18 @@
# include <copyfile.h>
# define _LIBCPP_FILESYSTEM_USE_COPYFILE
#else
-# include <fstream>
# define _LIBCPP_FILESYSTEM_USE_FSTREAM
#endif
+// sendfile and copy_file_range need to fall back
+// to the fstream implementation for special files
+#if (defined(_LIBCPP_FILESYSTEM_USE_SENDFILE) || defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE) || \
+ defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)) && \
+ _LIBCPP_HAS_LOCALIZATION
+# include <fstream>
+# define _LIBCPP_FILESYSTEM_NEED_FSTREAM
+#endif
+
#if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB)
# pragma comment(lib, "rt")
#endif
@@ -86,7 +108,7 @@ path __canonical(path const& orig_p, error_code* ec) {
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
if (hold.get() == nullptr)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
return {hold.get()};
#else
# if defined(__MVS__) && !defined(PATH_MAX)
@@ -96,7 +118,7 @@ path __canonical(path const& orig_p, error_code* ec) {
# endif
path::value_type* ret;
if ((ret = detail::realpath(p.c_str(), buff)) == nullptr)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
return {ret};
#endif
}
@@ -178,9 +200,89 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
namespace detail {
namespace {
+#if defined(_LIBCPP_FILESYSTEM_NEED_FSTREAM)
+bool copy_file_impl_fstream(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
+ ifstream in;
+ in.__open(read_fd.fd, ios::binary);
+ if (!in.is_open()) {
+ // This assumes that __open didn't reset the error code.
+ ec = capture_errno();
+ return false;
+ }
+ read_fd.fd = -1;
+ ofstream out;
+ out.__open(write_fd.fd, ios::binary);
+ if (!out.is_open()) {
+ ec = capture_errno();
+ return false;
+ }
+ write_fd.fd = -1;
+
+ if (in.good() && out.good()) {
+ using InIt = istreambuf_iterator<char>;
+ using OutIt = ostreambuf_iterator<char>;
+ InIt bin(in);
+ InIt ein;
+ OutIt bout(out);
+ copy(bin, ein, bout);
+ }
+ if (out.fail() || in.fail()) {
+ ec = make_error_code(errc::io_error);
+ return false;
+ }
+
+ ec.clear();
+ return true;
+}
+#endif
+
+#if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
+bool copy_file_impl_copy_file_range(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
+ size_t count = read_fd.get_stat().st_size;
+ // a zero-length file is either empty, or not copyable by this syscall
+ // return early to avoid the syscall cost
+ if (count == 0) {
+ ec = {EINVAL, generic_category()};
+ return false;
+ }
+ // do not modify the fd positions as copy_file_impl_sendfile may be called after a partial copy
+# if defined(__linux__)
+ loff_t off_in = 0;
+ loff_t off_out = 0;
+# else
+ off_t off_in = 0;
+ off_t off_out = 0;
+# endif
+
+ do {
+ ssize_t res;
+
+ if ((res = ::copy_file_range(read_fd.fd, &off_in, write_fd.fd, &off_out, count, 0)) == -1) {
+ ec = capture_errno();
+ return false;
+ }
+ count -= res;
+ } while (count > 0);
+
+ ec.clear();
+
+ return true;
+}
+#endif
+
#if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
-bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
+bool copy_file_impl_sendfile(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
size_t count = read_fd.get_stat().st_size;
+ // a zero-length file is either empty, or not copyable by this syscall
+ // return early to avoid the syscall cost
+ // however, we can't afford this luxury in the no-locale build,
+ // as we can't utilize the fstream impl to copy empty files
+# if _LIBCPP_HAS_LOCALIZATION
+ if (count == 0) {
+ ec = {EINVAL, generic_category()};
+ return false;
+ }
+# endif
do {
ssize_t res;
if ((res = ::sendfile(write_fd.fd, read_fd.fd, nullptr, count)) == -1) {
@@ -194,6 +296,54 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
return true;
}
+#endif
+
+#if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE) || defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
+// If we have copy_file_range or sendfile, try both in succession (if available).
+// If both fail, fall back to using fstream.
+bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
+# if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
+ if (copy_file_impl_copy_file_range(read_fd, write_fd, ec)) {
+ return true;
+ }
+ // EINVAL: src and dst are the same file (this is not cheaply
+ // detectable from userspace)
+ // EINVAL: copy_file_range is unsupported for this file type by the
+ // underlying filesystem
+ // ENOTSUP: undocumented, can arise with old kernels and NFS
+ // EOPNOTSUPP: filesystem does not implement copy_file_range
+ // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
+ // with normal copying)
+ // EXDEV: src and dst are on different filesystems that do not support
+ // cross-fs copy_file_range
+ // ENOENT: undocumented, can arise with CIFS
+ // ENOSYS: unsupported by kernel or blocked by seccomp
+ if (ec.value() != EINVAL && ec.value() != ENOTSUP && ec.value() != EOPNOTSUPP && ec.value() != ETXTBSY &&
+ ec.value() != EXDEV && ec.value() != ENOENT && ec.value() != ENOSYS) {
+ return false;
+ }
+ ec.clear();
+# endif
+
+# if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
+ if (copy_file_impl_sendfile(read_fd, write_fd, ec)) {
+ return true;
+ }
+ // EINVAL: unsupported file type
+ if (ec.value() != EINVAL) {
+ return false;
+ }
+ ec.clear();
+# endif
+
+# if defined(_LIBCPP_FILESYSTEM_NEED_FSTREAM)
+ return copy_file_impl_fstream(read_fd, write_fd, ec);
+# else
+ // since iostreams are unavailable in the no-locale build, just fail after a failed sendfile
+ ec.assign(EINVAL, std::system_category());
+ return false;
+# endif
+}
#elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
struct CopyFileState {
@@ -217,37 +367,7 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
}
#elif defined(_LIBCPP_FILESYSTEM_USE_FSTREAM)
bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
- ifstream in;
- in.__open(read_fd.fd, ios::binary);
- if (!in.is_open()) {
- // This assumes that __open didn't reset the error code.
- ec = capture_errno();
- return false;
- }
- read_fd.fd = -1;
- ofstream out;
- out.__open(write_fd.fd, ios::binary);
- if (!out.is_open()) {
- ec = capture_errno();
- return false;
- }
- write_fd.fd = -1;
-
- if (in.good() && out.good()) {
- using InIt = istreambuf_iterator<char>;
- using OutIt = ostreambuf_iterator<char>;
- InIt bin(in);
- InIt ein;
- OutIt bout(out);
- copy(bin, ein, bout);
- }
- if (out.fail() || in.fail()) {
- ec = make_error_code(errc::io_error);
- return false;
- }
-
- ec.clear();
- return true;
+ return copy_file_impl_fstream(read_fd, write_fd, ec);
}
#else
# error "Unknown implementation for copy_file_impl"
@@ -393,9 +513,9 @@ bool __create_directory(const path& p, error_code* ec) {
if (detail::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
return true;
- if (errno != EEXIST)
- return err.report(capture_errno());
- error_code mec = capture_errno();
+ error_code mec = detail::get_last_error();
+ if (mec != errc::file_exists)
+ return err.report(mec);
error_code ignored_ec;
const file_status st = status(p, ignored_ec);
if (!is_directory(st))
@@ -417,10 +537,10 @@ bool __create_directory(path const& p, path const& attributes, error_code* ec) {
if (detail::mkdir(p.c_str(), attr_stat.st_mode) == 0)
return true;
- if (errno != EEXIST)
- return err.report(capture_errno());
+ mec = detail::get_last_error();
+ if (mec != errc::file_exists)
+ return err.report(mec);
- mec = capture_errno();
error_code ignored_ec;
st = status(p, ignored_ec);
if (!is_directory(st))
@@ -431,19 +551,19 @@ bool __create_directory(path const& p, path const& attributes, error_code* ec) {
void __create_directory_symlink(path const& from, path const& to, error_code* ec) {
ErrorHandler<void> err("create_directory_symlink", ec, &from, &to);
if (detail::symlink_dir(from.c_str(), to.c_str()) == -1)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
void __create_hard_link(const path& from, const path& to, error_code* ec) {
ErrorHandler<void> err("create_hard_link", ec, &from, &to);
if (detail::link(from.c_str(), to.c_str()) == -1)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
void __create_symlink(path const& from, path const& to, error_code* ec) {
ErrorHandler<void> err("create_symlink", ec, &from, &to);
if (detail::symlink_file(from.c_str(), to.c_str()) == -1)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
path __current_path(error_code* ec) {
@@ -486,7 +606,7 @@ path __current_path(error_code* ec) {
unique_ptr<path::value_type, Deleter> hold(detail::getcwd(ptr, size), deleter);
if (hold.get() == nullptr)
- return err.report(capture_errno(), "call to getcwd failed");
+ return err.report(detail::get_last_error(), "call to getcwd failed");
return {hold.get()};
}
@@ -494,7 +614,7 @@ path __current_path(error_code* ec) {
void __current_path(const path& p, error_code* ec) {
ErrorHandler<void> err("current_path", ec, &p);
if (detail::chdir(p.c_str()) == -1)
- err.report(capture_errno());
+ err.report(detail::get_last_error());
}
bool __equivalent(const path& p1, const path& p2, error_code* ec) {
@@ -582,10 +702,10 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
return err.report(errc::value_too_large);
detail::WinHandle h(p.c_str(), FILE_WRITE_ATTRIBUTES, 0);
if (!h)
- return err.report(detail::make_windows_error(GetLastError()));
+ return err.report(detail::get_last_error());
FILETIME last_write = timespec_to_filetime(ts);
if (!SetFileTime(h, nullptr, nullptr, &last_write))
- return err.report(detail::make_windows_error(GetLastError()));
+ return err.report(detail::get_last_error());
#else
error_code m_ec;
array<TimeSpec, 2> tbuf;
@@ -643,7 +763,7 @@ void __permissions(const path& p, perms prms, perm_options opts, error_code* ec)
#if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
if (detail::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
#else
if (set_sym_perms)
@@ -671,14 +791,14 @@ path __read_symlink(const path& p, error_code* ec) {
#else
StatT sb;
if (detail::lstat(p.c_str(), &sb) == -1) {
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
const size_t size = sb.st_size + 1;
auto buff = unique_ptr<path::value_type[]>(new path::value_type[size]);
#endif
detail::SSizeT ret;
if ((ret = detail::readlink(p.c_str(), buff.get(), size)) == -1)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
// Note that `ret` returning `0` would work, resulting in a valid empty string being returned.
if (static_cast<size_t>(ret) >= size)
return err.report(errc::value_too_large);
@@ -689,8 +809,9 @@ path __read_symlink(const path& p, error_code* ec) {
bool __remove(const path& p, error_code* ec) {
ErrorHandler<bool> err("remove", ec, &p);
if (detail::remove(p.c_str()) == -1) {
- if (errno != ENOENT)
- err.report(capture_errno());
+ error_code mec = detail::get_last_error();
+ if (mec != errc::no_such_file_or_directory)
+ err.report(mec);
return false;
}
return true;
@@ -843,13 +964,13 @@ uintmax_t __remove_all(const path& p, error_code* ec) {
void __rename(const path& from, const path& to, error_code* ec) {
ErrorHandler<void> err("rename", ec, &from, &to);
if (detail::rename(from.c_str(), to.c_str()) == -1)
- err.report(capture_errno());
+ err.report(detail::get_last_error());
}
void __resize_file(const path& p, uintmax_t size, error_code* ec) {
ErrorHandler<void> err("resize_file", ec, &p);
if (detail::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1)
- return err.report(capture_errno());
+ return err.report(detail::get_last_error());
}
space_info __space(const path& p, error_code* ec) {
@@ -857,7 +978,7 @@ space_info __space(const path& p, error_code* ec) {
space_info si;
detail::StatVFS m_svfs = {};
if (detail::statvfs(p.c_str(), &m_svfs) == -1) {
- err.report(capture_errno());
+ err.report(detail::get_last_error());
si.capacity = si.free = si.available = static_cast<uintmax_t>(-1);
return si;
}
@@ -884,7 +1005,7 @@ path __temp_directory_path(error_code* ec) {
wchar_t buf[MAX_PATH];
DWORD retval = GetTempPathW(MAX_PATH, buf);
if (!retval)
- return err.report(detail::make_windows_error(GetLastError()));
+ return err.report(detail::get_last_error());
if (retval > MAX_PATH)
return err.report(errc::filename_too_long);
// GetTempPathW returns a path with a trailing slash, which we
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index b41c004..ddd99d8 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -11,9 +11,10 @@
//
// These generally behave like the proper posix functions, with these
// exceptions:
-// On Windows, they take paths in wchar_t* form, instead of char* form.
-// The symlink() function is split into two frontends, symlink_file()
-// and symlink_dir().
+// - On Windows, they take paths in wchar_t* form, instead of char* form.
+// - The symlink() function is split into two frontends, symlink_file()
+// and symlink_dir().
+// - Errors should be retrieved with get_last_error, not errno.
//
// These are provided within an anonymous namespace within the detail
// namespace - callers need to include this header and call them as
@@ -122,11 +123,6 @@ namespace detail {
# define O_NONBLOCK 0
-inline int set_errno(int e = GetLastError()) {
- errno = static_cast<int>(__win_err_to_errc(e));
- return -1;
-}
-
class WinHandle {
public:
WinHandle(const wchar_t* p, DWORD access, DWORD flags) {
@@ -153,7 +149,7 @@ private:
inline int stat_handle(HANDLE h, StatT* buf) {
FILE_BASIC_INFO basic;
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic)))
- return set_errno();
+ return -1;
memset(buf, 0, sizeof(*buf));
buf->st_mtim = filetime_to_timespec(basic.LastWriteTime);
buf->st_atim = filetime_to_timespec(basic.LastAccessTime);
@@ -168,18 +164,18 @@ inline int stat_handle(HANDLE h, StatT* buf) {
if (basic.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
FILE_ATTRIBUTE_TAG_INFO tag;
if (!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tag, sizeof(tag)))
- return set_errno();
+ return -1;
if (tag.ReparseTag == IO_REPARSE_TAG_SYMLINK)
buf->st_mode = (buf->st_mode & ~_S_IFMT) | _S_IFLNK;
}
FILE_STANDARD_INFO standard;
if (!GetFileInformationByHandleEx(h, FileStandardInfo, &standard, sizeof(standard)))
- return set_errno();
+ return -1;
buf->st_nlink = standard.NumberOfLinks;
buf->st_size = standard.EndOfFile.QuadPart;
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(h, &info))
- return set_errno();
+ return -1;
buf->st_dev = info.dwVolumeSerialNumber;
memcpy(&buf->st_ino.id[0], &info.nFileIndexHigh, 4);
memcpy(&buf->st_ino.id[4], &info.nFileIndexLow, 4);
@@ -189,7 +185,7 @@ inline int stat_handle(HANDLE h, StatT* buf) {
inline int stat_file(const wchar_t* path, StatT* buf, DWORD flags) {
WinHandle h(path, FILE_READ_ATTRIBUTES, flags);
if (!h)
- return set_errno();
+ return -1;
int ret = stat_handle(h, buf);
return ret;
}
@@ -206,7 +202,7 @@ inline int fstat(int fd, StatT* buf) {
inline int mkdir(const wchar_t* path, int permissions) {
(void)permissions;
if (!CreateDirectoryW(path, nullptr))
- return set_errno();
+ return -1;
return 0;
}
@@ -219,10 +215,10 @@ inline int symlink_file_dir(const wchar_t* oldname, const wchar_t* newname, bool
return 0;
int e = GetLastError();
if (e != ERROR_INVALID_PARAMETER)
- return set_errno(e);
+ return -1;
if (CreateSymbolicLinkW(newname, oldname, flags))
return 0;
- return set_errno();
+ return -1;
}
inline int symlink_file(const wchar_t* oldname, const wchar_t* newname) {
@@ -236,17 +232,17 @@ inline int symlink_dir(const wchar_t* oldname, const wchar_t* newname) {
inline int link(const wchar_t* oldname, const wchar_t* newname) {
if (CreateHardLinkW(newname, oldname, nullptr))
return 0;
- return set_errno();
+ return -1;
}
inline int remove(const wchar_t* path) {
detail::WinHandle h(path, DELETE, FILE_FLAG_OPEN_REPARSE_POINT);
if (!h)
- return set_errno();
+ return -1;
FILE_DISPOSITION_INFO info;
info.DeleteFile = TRUE;
if (!SetFileInformationByHandle(h, FileDispositionInfo, &info, sizeof(info)))
- return set_errno();
+ return -1;
return 0;
}
@@ -254,9 +250,9 @@ inline int truncate_handle(HANDLE h, off_t length) {
LARGE_INTEGER size_param;
size_param.QuadPart = length;
if (!SetFilePointerEx(h, size_param, 0, FILE_BEGIN))
- return set_errno();
+ return -1;
if (!SetEndOfFile(h))
- return set_errno();
+ return -1;
return 0;
}
@@ -268,19 +264,19 @@ inline int ftruncate(int fd, off_t length) {
inline int truncate(const wchar_t* path, off_t length) {
detail::WinHandle h(path, GENERIC_WRITE, 0);
if (!h)
- return set_errno();
+ return -1;
return truncate_handle(h, length);
}
inline int rename(const wchar_t* from, const wchar_t* to) {
if (!(MoveFileExW(from, to, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)))
- return set_errno();
+ return -1;
return 0;
}
inline int chdir(const wchar_t* path) {
if (!SetCurrentDirectoryW(path))
- return set_errno();
+ return -1;
return 0;
}
@@ -300,7 +296,7 @@ inline int statvfs(const wchar_t* p, StatVFS* buf) {
break;
path parent = dir.parent_path();
if (parent == dir) {
- errno = ENOENT;
+ SetLastError(ERROR_PATH_NOT_FOUND);
return -1;
}
dir = parent;
@@ -308,7 +304,7 @@ inline int statvfs(const wchar_t* p, StatVFS* buf) {
ULARGE_INTEGER free_bytes_available_to_caller, total_number_of_bytes, total_number_of_free_bytes;
if (!GetDiskFreeSpaceExW(
dir.c_str(), &free_bytes_available_to_caller, &total_number_of_bytes, &total_number_of_free_bytes))
- return set_errno();
+ return -1;
buf->f_frsize = 1;
buf->f_blocks = total_number_of_bytes.QuadPart;
buf->f_bfree = total_number_of_free_bytes.QuadPart;
@@ -330,7 +326,6 @@ inline wchar_t* getcwd([[maybe_unused]] wchar_t* in_buf, [[maybe_unused]] size_t
retval = GetCurrentDirectoryW(buff_size, buff.get());
}
if (!retval) {
- set_errno();
return nullptr;
}
return buff.release();
@@ -342,7 +337,6 @@ inline wchar_t* realpath(const wchar_t* path, [[maybe_unused]] wchar_t* resolved
WinHandle h(path, FILE_READ_ATTRIBUTES, 0);
if (!h) {
- set_errno();
return nullptr;
}
size_t buff_size = MAX_PATH + 10;
@@ -354,7 +348,6 @@ inline wchar_t* realpath(const wchar_t* path, [[maybe_unused]] wchar_t* resolved
retval = GetFinalPathNameByHandleW(h, buff.get(), buff_size, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
}
if (!retval) {
- set_errno();
return nullptr;
}
wchar_t* ptr = buff.get();
@@ -376,20 +369,20 @@ using ModeT = int;
inline int fchmod_handle(HANDLE h, int perms) {
FILE_BASIC_INFO basic;
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic)))
- return set_errno();
+ return -1;
DWORD orig_attributes = basic.FileAttributes;
basic.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
if ((perms & 0222) == 0)
basic.FileAttributes |= FILE_ATTRIBUTE_READONLY;
if (basic.FileAttributes != orig_attributes && !SetFileInformationByHandle(h, FileBasicInfo, &basic, sizeof(basic)))
- return set_errno();
+ return -1;
return 0;
}
inline int fchmodat(int /*fd*/, const wchar_t* path, int perms, int flag) {
DWORD attributes = GetFileAttributesW(path);
if (attributes == INVALID_FILE_ATTRIBUTES)
- return set_errno();
+ return -1;
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT && !(flag & AT_SYMLINK_NOFOLLOW)) {
// If the file is a symlink, and we are supposed to operate on the target
// of the symlink, we need to open a handle to it, without the
@@ -397,7 +390,7 @@ inline int fchmodat(int /*fd*/, const wchar_t* path, int perms, int flag) {
// symlink, and operate on it via the handle.
detail::WinHandle h(path, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 0);
if (!h)
- return set_errno();
+ return -1;
return fchmod_handle(h, perms);
} else {
// For a non-symlink, or if operating on the symlink itself instead of
@@ -407,7 +400,7 @@ inline int fchmodat(int /*fd*/, const wchar_t* path, int perms, int flag) {
if ((perms & 0222) == 0)
attributes |= FILE_ATTRIBUTE_READONLY;
if (attributes != orig_attributes && !SetFileAttributesW(path, attributes))
- return set_errno();
+ return -1;
}
return 0;
}
@@ -424,18 +417,18 @@ inline SSizeT readlink(const wchar_t* path, wchar_t* ret_buf, size_t bufsize) {
uint8_t buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
detail::WinHandle h(path, FILE_READ_ATTRIBUTES, FILE_FLAG_OPEN_REPARSE_POINT);
if (!h)
- return set_errno();
+ return -1;
DWORD out;
if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf, sizeof(buf), &out, 0))
- return set_errno();
+ return -1;
const auto* reparse = reinterpret_cast<LIBCPP_REPARSE_DATA_BUFFER*>(buf);
size_t path_buf_offset = offsetof(LIBCPP_REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]);
if (out < path_buf_offset) {
- errno = EINVAL;
+ SetLastError(ERROR_REPARSE_TAG_INVALID);
return -1;
}
if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
- errno = EINVAL;
+ SetLastError(ERROR_REPARSE_TAG_INVALID);
return -1;
}
const auto& symlink = reparse->SymbolicLinkReparseBuffer;
@@ -449,11 +442,11 @@ inline SSizeT readlink(const wchar_t* path, wchar_t* ret_buf, size_t bufsize) {
}
// name_offset/length are expressed in bytes, not in wchar_t
if (path_buf_offset + name_offset + name_length > out) {
- errno = EINVAL;
+ SetLastError(ERROR_REPARSE_TAG_INVALID);
return -1;
}
if (name_length / sizeof(wchar_t) > bufsize) {
- errno = ENOMEM;
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return -1;
}
memcpy(ret_buf, &symlink.PathBuffer[name_offset / sizeof(wchar_t)], name_length);