[libcxx-commits] [libcxx] [libcxx] Handle windows system error code mapping in std::error_code. (PR #93101)
James Y Knight via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Sep 13 15:08:46 PDT 2024
https://github.com/jyknight updated https://github.com/llvm/llvm-project/pull/93101
>From 2bfbd98dd041e132325f91e39f80ab05e9706948 Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight at google.com>
Date: Thu, 15 Jun 2023 11:39:46 -0400
Subject: [PATCH 1/5] [libcxx] Handle windows system error code mapping in
std::error_code.
The std::error_code/std::error_category functionality is designed to
support multiple error domains. On Unix, both system calls and libc
functions return the same error codes, and thus, libc++ today treats
generic_category() and system_category() as being equivalent.
However, on Windows, libc functions return `errno.h` error codes in
the `errno` global, but system calls return the very different
`winerror.h` error codes via `GetLastError()`.
As such, there is a need to map the winerror.h error codes into
generic errno codes. In libc++, however, the system_error facility
does not currently implement this mapping, and instead the mapping is
hidden inside libc++, used directly by the std::filesystem
implementation.
That has a few problems:
1. For std::filesystem APIs, the concrete windows error number is
lost, before users can see it. The intent of the distinction
between std::error_code and std::error_condition is that the
error_code return has the original (potentially more detailed)
error code.
2. User-written code which calls Windows system APIs requires this
same mapping, so it also can also return error_code objects that other
(cross-platform) code can understand.
After this commit, an `error_code` with `generic_category()` is used
to report an error from `errno`, and, on Windows only, an `error_code`
with `system_category()` is used to report an error from
`GetLastError()`. On Unix, system_category remains identity-mapped to
generic_category, but is never used by libc++ itself.
The windows error code mapping is moved into system_error, so that
conversion of an `error_code` to `error_condition` correctly
translates the `system_category()` code into a `generic_category()`
code, when appropriate.
This allows code like:
`error_code(GetLastError(), system_category()) == errc::invalid_argument`
to work as expected -- as it does with MSVC STL.
Differential Revision: https://reviews.llvm.org/D151493
---
libcxx/include/__filesystem/directory_entry.h | 11 +-
libcxx/include/__system_error/system_error.h | 2 +
libcxx/src/filesystem/directory_iterator.cpp | 11 +-
libcxx/src/filesystem/error.h | 75 ++---------
libcxx/src/filesystem/file_descriptor.h | 12 +-
libcxx/src/filesystem/operations.cpp | 49 ++++----
libcxx/src/filesystem/posix_compat.h | 73 +++++------
libcxx/src/system_error.cpp | 118 +++++++++++++++++-
.../eq_error_code_error_code.pass.cpp | 14 ++-
.../syserr.errcat.derived/message.pass.cpp | 7 +-
.../system_category.pass.cpp | 6 +
.../file_type_obs.pass.cpp | 8 +-
.../directory_entry.obs/status.pass.cpp | 2 +-
.../symlink_status.pass.cpp | 2 +-
libcxx/test/support/filesystem_test_helper.h | 4 +
15 files changed, 228 insertions(+), 166 deletions(-)
diff --git a/libcxx/include/__filesystem/directory_entry.h b/libcxx/include/__filesystem/directory_entry.h
index bb7a061db4ceae..340cdaa03af141 100644
--- a/libcxx/include/__filesystem/directory_entry.h
+++ b/libcxx/include/__filesystem/directory_entry.h
@@ -22,6 +22,7 @@
#include <__filesystem/path.h>
#include <__filesystem/perms.h>
#include <__system_error/errc.h>
+#include <__system_error/error_category.h>
#include <__system_error/error_code.h>
#include <__utility/move.h>
#include <__utility/unreachable.h>
@@ -325,15 +326,7 @@ class directory_entry {
_LIBCPP_INLINE_VISIBILITY
static bool __is_dne_error(error_code const& __ec) {
- if (!__ec)
- return true;
- switch (static_cast<errc>(__ec.value())) {
- case errc::no_such_file_or_directory:
- case errc::not_a_directory:
- return true;
- default:
- return false;
- }
+ return !__ec || __ec == errc::no_such_file_or_directory || __ec == errc::not_a_directory;
}
_LIBCPP_INLINE_VISIBILITY
diff --git a/libcxx/include/__system_error/system_error.h b/libcxx/include/__system_error/system_error.h
index bc829491a493f7..5553f05c8acc1d 100644
--- a/libcxx/include/__system_error/system_error.h
+++ b/libcxx/include/__system_error/system_error.h
@@ -41,6 +41,8 @@ class _LIBCPP_EXPORTED_FROM_ABI system_error : public runtime_error {
static string __init(const error_code&, string);
};
+// __ev is expected to be an error in the generic_category domain (e.g. from
+// errno, or std::errc::*), not system_category (e.g. from windows syscalls).
_LIBCPP_NORETURN _LIBCPP_EXPORTED_FROM_ABI void __throw_system_error(int __ev, const char* __what_arg);
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index 151fb2fb621af7..f5ea984e03d478 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -48,11 +48,10 @@ class __dir_stream {
}
__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;
}
@@ -95,7 +94,7 @@ class __dir_stream {
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;
}
@@ -125,7 +124,7 @@ class __dir_stream {
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;
}
@@ -326,7 +325,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 965e0eadcd0dcf..193fc85d1652c0 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -32,82 +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_UNCATEGORIZED(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 d3a668fa2e36c0..2be5db3b3d46b5 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -201,7 +201,7 @@ 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,
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 63a119aa983e31..5057c2fdc88508 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -87,7 +87,7 @@ path __canonical(path const& orig_p, error_code* ec) {
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)
@@ -97,7 +97,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
}
@@ -412,9 +412,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))
@@ -437,10 +437,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))
@@ -452,19 +452,19 @@ 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) {
@@ -493,7 +493,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()};
}
@@ -501,7 +501,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) {
@@ -590,10 +590,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;
@@ -653,7 +653,7 @@ void __permissions(const path& p, perms prms, perm_options opts,
#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)
@@ -679,14 +679,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());
_LIBCPP_ASSERT_UNCATEGORIZED(ret > 0, "TODO");
if (static_cast<size_t>(ret) >= size)
return err.report(errc::value_too_large);
@@ -697,8 +697,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;
@@ -853,13 +854,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) {
@@ -867,7 +868,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;
}
@@ -898,7 +899,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 f11f41552e3916..f59f2098b6a7ae 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) {
@@ -148,7 +144,7 @@ class WinHandle {
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);
@@ -164,19 +160,19 @@ inline int stat_handle(HANDLE h, StatT *buf) {
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);
@@ -186,7 +182,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;
}
@@ -205,7 +201,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;
}
@@ -220,10 +216,10 @@ inline int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname,
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) {
@@ -237,17 +233,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;
}
@@ -255,9 +251,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;
}
@@ -269,7 +265,7 @@ 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);
}
@@ -277,13 +273,13 @@ 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;
}
@@ -303,7 +299,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;
@@ -312,7 +308,7 @@ inline int statvfs(const wchar_t *p, StatVFS *buf) {
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;
@@ -334,7 +330,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();
@@ -347,7 +342,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;
@@ -362,7 +356,6 @@ inline wchar_t *realpath(const wchar_t *path, [[maybe_unused]] wchar_t *resolved
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
}
if (!retval) {
- set_errno();
return nullptr;
}
wchar_t *ptr = buff.get();
@@ -384,21 +377,21 @@ 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
@@ -407,7 +400,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
@@ -417,7 +410,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;
}
@@ -434,20 +427,20 @@ 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;
@@ -461,11 +454,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)],
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index 1502c7de6ba551..d06c745102b875 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -16,8 +16,9 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
-#include <string>
+#include <optional>
#include <string.h>
+#include <string>
#include <system_error>
#include "include/config_elast.h"
@@ -26,8 +27,80 @@
#include <android/api-level.h>
#endif
+#if defined(_LIBCPP_WIN32API)
+# include <windows.h>
+# include <winerror.h>
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_STD
+#if defined(_LIBCPP_WIN32API)
+
+namespace {
+std::optional<errc> __win_err_to_errc(int err) {
+ constexpr struct {
+ int 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 == err)
+ return pair.errc;
+ return {};
+}
+} // namespace
+#endif
+
// class error_category
#if defined(_LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS)
@@ -189,21 +262,54 @@ __system_error_category::name() const noexcept
string
__system_error_category::message(int ev) const
{
-#ifdef _LIBCPP_ELAST
+#ifdef _LIBCPP_WIN32API
+ std::string result;
+ char* str = nullptr;
+ unsigned long num_chars = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ ev,
+ 0,
+ reinterpret_cast<char*>(&str),
+ 0,
+ nullptr);
+ auto is_whitespace = [](char ch) { return ch == '\n' || ch == '\r' || ch == ' '; };
+ while (num_chars > 0 && is_whitespace(str[num_chars - 1]))
+ --num_chars;
+
+ if (num_chars)
+ result = std::string(str, num_chars);
+ else
+ result = "Unknown error";
+
+ LocalFree(str);
+ return result;
+#else
+# ifdef _LIBCPP_ELAST
if (ev > _LIBCPP_ELAST)
return string("unspecified system_category error");
-#endif // _LIBCPP_ELAST
+# endif // _LIBCPP_ELAST
return __do_message::message(ev);
+#endif
}
error_condition
__system_error_category::default_error_condition(int ev) const noexcept
{
-#ifdef _LIBCPP_ELAST
+#ifdef _LIBCPP_WIN32API
+ // Remap windows error codes to generic error codes if possible.
+ if (ev == 0)
+ return error_condition(0, generic_category());
+ if (auto maybe_errc = __win_err_to_errc(ev))
+ return error_condition(static_cast<int>(maybe_errc.value()), generic_category());
+ return error_condition(ev, system_category());
+#else
+# ifdef _LIBCPP_ELAST
if (ev > _LIBCPP_ELAST)
return error_condition(ev, system_category());
-#endif // _LIBCPP_ELAST
+# endif // _LIBCPP_ELAST
return error_condition(ev, generic_category());
+#endif
}
const error_category&
@@ -287,7 +393,7 @@ void
__throw_system_error(int ev, const char* what_arg)
{
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
- throw system_error(error_code(ev, system_category()), what_arg);
+ throw system_error(error_code(ev, generic_category()), what_arg);
#else
(void)ev;
(void)what_arg;
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.compare/eq_error_code_error_code.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.compare/eq_error_code_error_code.pass.cpp
index c63bfcd955a69c..6c8a5948b8db9f 100644
--- a/libcxx/test/std/diagnostics/syserr/syserr.compare/eq_error_code_error_code.pass.cpp
+++ b/libcxx/test/std/diagnostics/syserr/syserr.compare/eq_error_code_error_code.pass.cpp
@@ -22,6 +22,10 @@
#include "test_macros.h"
+#ifndef _WIN32
+# define TEST_SYSTEM_CATEGORY_IS_GENERIC_CATEGORY
+#endif
+
int main(int, char**)
{
std::error_code e_code1(5, std::generic_category());
@@ -46,7 +50,9 @@ int main(int, char**)
assert(e_code2 == e_code2);
assert(e_code2 != e_code3);
assert(e_code2 != e_code4);
- assert(e_code2 == e_condition1); // ?
+#ifdef TEST_SYSTEM_CATEGORY_IS_GENERIC_CATEGORY
+ assert(e_code2 == e_condition1);
+#endif
assert(e_code2 == e_condition2);
assert(e_code2 != e_condition3);
assert(e_code2 != e_condition4);
@@ -66,11 +72,15 @@ int main(int, char**)
assert(e_code4 == e_code4);
assert(e_code4 != e_condition1);
assert(e_code4 != e_condition2);
+#ifdef TEST_SYSTEM_CATEGORY_IS_GENERIC_CATEGORY
assert(e_code4 == e_condition3); // ?
+#endif
assert(e_code4 == e_condition4);
assert(e_condition1 == e_code1);
+#ifdef TEST_SYSTEM_CATEGORY_IS_GENERIC_CATEGORY
assert(e_condition1 == e_code2); // ?
+#endif
assert(e_condition1 != e_code3);
assert(e_condition1 != e_code4);
assert(e_condition1 == e_condition1);
@@ -90,7 +100,9 @@ int main(int, char**)
assert(e_condition3 != e_code1);
assert(e_condition3 != e_code2);
assert(e_condition3 == e_code3);
+#ifdef TEST_SYSTEM_CATEGORY_IS_GENERIC_CATEGORY
assert(e_condition3 == e_code4); // ?
+#endif
assert(e_condition3 != e_condition1);
assert(e_condition3 != e_condition2);
assert(e_condition3 == e_condition3);
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.derived/message.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.derived/message.pass.cpp
index a899638ce169ae..db54a0ae79e705 100644
--- a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.derived/message.pass.cpp
+++ b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.derived/message.pass.cpp
@@ -30,8 +30,11 @@ int main(int, char**)
assert(!m1.empty());
assert(!m2.empty());
assert(!m3.empty());
+#ifndef _WIN32
+ // On windows, system_category is distinct.
assert(m1 == m2);
- assert(m1 != m3);
+#endif
+ assert(m2 != m3);
- return 0;
+ return 0;
}
diff --git a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
index 7b15da3cd6d17b..2120f70b9a14ba 100644
--- a/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
+++ b/libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp
@@ -39,8 +39,14 @@ int main(int, char**)
{
const std::error_category& e_cat1 = std::system_category();
std::error_condition e_cond = e_cat1.default_error_condition(5);
+#ifdef _WIN32
+ // Windows error 5 is ERROR_ACCESS_DENIED, which maps to generic code permission_denied.
+ assert(e_cond.value() == static_cast<int>(std::errc::permission_denied));
+#else
assert(e_cond.value() == 5);
+#endif
assert(e_cond.category() == std::generic_category());
+
e_cond = e_cat1.default_error_condition(5000);
assert(e_cond.value() == 5000);
assert(e_cond.category() == std::system_category());
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 6591d5191d9d5b..6f521dd45dcaa2 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -167,8 +167,12 @@ static void test_with_ec_dne() {
file_status st = status(p, status_ec);
file_status sym_st = symlink_status(p, sym_status_ec);
std::error_code ec = GetTestEC(2);
- auto CheckEC = [&](std::error_code const& other_ec) {
- bool res = ec == other_ec;
+ auto CheckEC = [&](std::error_code const& other_ec) {
+ // We compare the canonicalized error_conditions for this case, because
+ // directory_entry may construct its own generic_category error when a
+ // file doesn't exist, instead of directly returning a system_category
+ // error.
+ bool res = ec.default_error_condition() == other_ec.default_error_condition();
ec = GetTestEC(2);
return res;
};
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp
index 7a45d608fb268a..90fd63c0d26cfc 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp
@@ -43,7 +43,7 @@ static void test_basic() {
file_status es = e.status(eec);
assert(ps.type() == es.type());
assert(ps.permissions() == es.permissions());
- assert(pec == eec);
+ assert(pec.default_error_condition() == eec.default_error_condition());
}
for (const auto& p : TestCases) {
const directory_entry e(p);
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp
index 7fc5d3cdbee3b9..b664d58d8347e5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp
@@ -43,7 +43,7 @@ static void test_signature() {
file_status es = e.symlink_status(eec);
assert(ps.type() == es.type());
assert(ps.permissions() == es.permissions());
- assert(pec == eec);
+ assert(pec.default_error_condition() == eec.default_error_condition());
}
for (const auto& p : TestCases) {
const directory_entry e(p);
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index d63b1e61b5f9b5..3d7b4a09c6be12 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -562,7 +562,11 @@ struct ExceptionChecker {
assert(ErrorIsImp(Err.code(), {expected_err}));
assert(Err.path1() == expected_path1);
assert(Err.path2() == expected_path2);
+#ifndef _WIN32
+ // On Windows, the error strings are windows error code strings, and don't
+ // match textually with the strings generated for generic std::errc::*.
LIBCPP_ONLY(check_libcxx_string(Err));
+#endif
}
void check_libcxx_string(fs::filesystem_error const& Err) {
>From 0ff332a8d977c076bec4dde5e021f36dd3fec5b6 Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight at google.com>
Date: Thu, 23 May 2024 12:46:45 -0400
Subject: [PATCH 2/5] Fix semantic merge conflict.
---
libcxx/src/print.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
index 8fa59fdd097bcd..26ac8e0466d5b9 100644
--- a/libcxx/src/print.cpp
+++ b/libcxx/src/print.cpp
@@ -51,7 +51,7 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst
__view.size(),
nullptr,
nullptr) == 0) {
- __throw_system_error(filesystem::detail::make_windows_error(GetLastError()), "failed to write formatted output");
+ __throw_system_error(filesystem::detail::get_last_error(), "failed to write formatted output");
}
}
# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
>From 8e3979ea7e37d15e2d4d20b4f8299c567472947d Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight at google.com>
Date: Tue, 28 May 2024 10:24:52 -0400
Subject: [PATCH 3/5] Add missing include (should fix "generic-no-localization"
builder)
---
libcxx/include/__filesystem/directory_entry.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__filesystem/directory_entry.h b/libcxx/include/__filesystem/directory_entry.h
index ef10acfa2dbabe..299dc0d2f7ec60 100644
--- a/libcxx/include/__filesystem/directory_entry.h
+++ b/libcxx/include/__filesystem/directory_entry.h
@@ -24,6 +24,7 @@
#include <__system_error/errc.h>
#include <__system_error/error_category.h>
#include <__system_error/error_code.h>
+#include <__system_error/error_condition.h>
#include <__utility/move.h>
#include <__utility/unreachable.h>
#include <cstdint>
>From 1533c1018f19f5078d8ba567c75b1c682fdadd28 Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight at google.com>
Date: Fri, 13 Sep 2024 17:59:22 -0400
Subject: [PATCH 4/5] Address review comments.
---
libcxx/include/__filesystem/directory_entry.h | 2 -
libcxx/src/system_error.cpp | 115 ++++++++----------
.../system_error_win_codes.pass.cpp | 24 ++++
.../file_type_obs.pass.cpp | 9 +-
4 files changed, 83 insertions(+), 67 deletions(-)
create mode 100644 libcxx/test/libcxx/diagnostics/system_error_win_codes.pass.cpp
diff --git a/libcxx/include/__filesystem/directory_entry.h b/libcxx/include/__filesystem/directory_entry.h
index 23ec470b796bd8..3273509ec501b1 100644
--- a/libcxx/include/__filesystem/directory_entry.h
+++ b/libcxx/include/__filesystem/directory_entry.h
@@ -22,9 +22,7 @@
#include <__filesystem/perms.h>
#include <__fwd/ostream.h>
#include <__system_error/errc.h>
-#include <__system_error/error_category.h>
#include <__system_error/error_code.h>
-#include <__system_error/error_condition.h>
#include <__utility/move.h>
#include <__utility/unreachable.h>
#include <cstdint>
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index fe5b7a6a3118db..967826d7ef979f 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -35,65 +35,58 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace {
std::optional<errc> __win_err_to_errc(int err) {
- constexpr struct {
- int 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 == err)
- return pair.errc;
- return {};
+ switch (err) {
+ case ERROR_ACCESS_DENIED: return errc::permission_denied;
+ case ERROR_ALREADY_EXISTS: return errc::file_exists;
+ case ERROR_BAD_NETPATH: return errc::no_such_file_or_directory;
+ case ERROR_BAD_PATHNAME: return errc::no_such_file_or_directory;
+ case ERROR_BAD_UNIT: return errc::no_such_device;
+ case ERROR_BROKEN_PIPE: return errc::broken_pipe;
+ case ERROR_BUFFER_OVERFLOW: return errc::filename_too_long;
+ case ERROR_BUSY: return errc::device_or_resource_busy;
+ case ERROR_BUSY_DRIVE: return errc::device_or_resource_busy;
+ case ERROR_CANNOT_MAKE: return errc::permission_denied;
+ case ERROR_CANTOPEN: return errc::io_error;
+ case ERROR_CANTREAD: return errc::io_error;
+ case ERROR_CANTWRITE: return errc::io_error;
+ case ERROR_CURRENT_DIRECTORY: return errc::permission_denied;
+ case ERROR_DEV_NOT_EXIST: return errc::no_such_device;
+ case ERROR_DEVICE_IN_USE: return errc::device_or_resource_busy;
+ case ERROR_DIR_NOT_EMPTY: return errc::directory_not_empty;
+ case ERROR_DIRECTORY: return errc::invalid_argument;
+ case ERROR_DISK_FULL: return errc::no_space_on_device;
+ case ERROR_FILE_EXISTS: return errc::file_exists;
+ case ERROR_FILE_NOT_FOUND: return errc::no_such_file_or_directory;
+ case ERROR_HANDLE_DISK_FULL: return errc::no_space_on_device;
+ case ERROR_INVALID_ACCESS: return errc::permission_denied;
+ case ERROR_INVALID_DRIVE: return errc::no_such_device;
+ case ERROR_INVALID_FUNCTION: return errc::function_not_supported;
+ case ERROR_INVALID_HANDLE: return errc::invalid_argument;
+ case ERROR_INVALID_NAME: return errc::no_such_file_or_directory;
+ case ERROR_INVALID_PARAMETER: return errc::invalid_argument;
+ case ERROR_LOCK_VIOLATION: return errc::no_lock_available;
+ case ERROR_LOCKED: return errc::no_lock_available;
+ case ERROR_NEGATIVE_SEEK: return errc::invalid_argument;
+ case ERROR_NOACCESS: return errc::permission_denied;
+ case ERROR_NOT_ENOUGH_MEMORY: return errc::not_enough_memory;
+ case ERROR_NOT_READY: return errc::resource_unavailable_try_again;
+ case ERROR_NOT_SAME_DEVICE: return errc::cross_device_link;
+ case ERROR_NOT_SUPPORTED: return errc::not_supported;
+ case ERROR_OPEN_FAILED: return errc::io_error;
+ case ERROR_OPEN_FILES: return errc::device_or_resource_busy;
+ case ERROR_OPERATION_ABORTED: return errc::operation_canceled;
+ case ERROR_OUTOFMEMORY: return errc::not_enough_memory;
+ case ERROR_PATH_NOT_FOUND: return errc::no_such_file_or_directory;
+ case ERROR_READ_FAULT: return errc::io_error;
+ case ERROR_REPARSE_TAG_INVALID: return errc::invalid_argument;
+ case ERROR_RETRY: return errc::resource_unavailable_try_again;
+ case ERROR_SEEK: return errc::io_error;
+ case ERROR_SHARING_VIOLATION: return errc::permission_denied;
+ case ERROR_TOO_MANY_OPEN_FILES: return errc::too_many_files_open;
+ case ERROR_WRITE_FAULT: return errc::io_error;
+ case ERROR_WRITE_PROTECT: return errc::permission_denied;
+ default: return {};
+ }
}
} // namespace
#endif
@@ -232,7 +225,7 @@ string __system_error_category::message(int ev) const {
#ifdef _LIBCPP_WIN32API
std::string result;
char* str = nullptr;
- unsigned long num_chars = FormatMessageA(
+ unsigned long num_chars = ::FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
ev,
@@ -266,7 +259,7 @@ error_condition __system_error_category::default_error_condition(int ev) const n
if (ev == 0)
return error_condition(0, generic_category());
if (auto maybe_errc = __win_err_to_errc(ev))
- return error_condition(static_cast<int>(maybe_errc.value()), generic_category());
+ return error_condition(static_cast<int>(*maybe_errc), generic_category());
return error_condition(ev, system_category());
#else
# ifdef _LIBCPP_ELAST
diff --git a/libcxx/test/libcxx/diagnostics/system_error_win_codes.pass.cpp b/libcxx/test/libcxx/diagnostics/system_error_win_codes.pass.cpp
new file mode 100644
index 00000000000000..89fe9a3232fb0a
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/system_error_win_codes.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: windows
+
+// Validate that system_error on windows accepts Windows' System Error Codes (as
+// used by win32 APIs and reported by GetLastError), and that they are properly
+// translated to generic conditions.
+
+#include <windows.h>
+#include <system_error>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ LIBCPP_ASSERT(std::error_code(ERROR_ACCESS_DENIED, std::system_category()) == std::errc::permission_denied);
+ LIBCPP_ASSERT(std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()) == std::errc::no_such_file_or_directory);
+}
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index c7459a7830df86..071ee7f6c891fa 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -173,10 +173,11 @@ static void test_with_ec_dne() {
file_status sym_st = symlink_status(p, sym_status_ec);
std::error_code ec = GetTestEC(2);
auto CheckEC = [&](std::error_code const& other_ec) {
- // We compare the canonicalized error_conditions for this case, because
- // directory_entry may construct its own generic_category error when a
- // file doesn't exist, instead of directly returning a system_category
- // error.
+ // Note: we're comparing equality of the _canonicalized_ error_condition
+ // here (unlike in other tests where we expect exactly the same
+ // error_code). This is because directory_entry can construct its own
+ // generic_category error when a file doesn't exist, instead of passing
+ // through an underlying system_category error.
bool res = ec.default_error_condition() == other_ec.default_error_condition();
ec = GetTestEC(2);
return res;
>From cf0fbb2297bd08c9a2912f85b29716c436d3d47e Mon Sep 17 00:00:00 2001
From: James Y Knight <jyknight at google.com>
Date: Fri, 13 Sep 2024 18:08:32 -0400
Subject: [PATCH 5/5] Clang-format the switch statement.
---
libcxx/src/system_error.cpp | 150 ++++++++++++++++++++++++------------
1 file changed, 100 insertions(+), 50 deletions(-)
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index 967826d7ef979f..cd8526a1aa680f 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -36,56 +36,106 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace {
std::optional<errc> __win_err_to_errc(int err) {
switch (err) {
- case ERROR_ACCESS_DENIED: return errc::permission_denied;
- case ERROR_ALREADY_EXISTS: return errc::file_exists;
- case ERROR_BAD_NETPATH: return errc::no_such_file_or_directory;
- case ERROR_BAD_PATHNAME: return errc::no_such_file_or_directory;
- case ERROR_BAD_UNIT: return errc::no_such_device;
- case ERROR_BROKEN_PIPE: return errc::broken_pipe;
- case ERROR_BUFFER_OVERFLOW: return errc::filename_too_long;
- case ERROR_BUSY: return errc::device_or_resource_busy;
- case ERROR_BUSY_DRIVE: return errc::device_or_resource_busy;
- case ERROR_CANNOT_MAKE: return errc::permission_denied;
- case ERROR_CANTOPEN: return errc::io_error;
- case ERROR_CANTREAD: return errc::io_error;
- case ERROR_CANTWRITE: return errc::io_error;
- case ERROR_CURRENT_DIRECTORY: return errc::permission_denied;
- case ERROR_DEV_NOT_EXIST: return errc::no_such_device;
- case ERROR_DEVICE_IN_USE: return errc::device_or_resource_busy;
- case ERROR_DIR_NOT_EMPTY: return errc::directory_not_empty;
- case ERROR_DIRECTORY: return errc::invalid_argument;
- case ERROR_DISK_FULL: return errc::no_space_on_device;
- case ERROR_FILE_EXISTS: return errc::file_exists;
- case ERROR_FILE_NOT_FOUND: return errc::no_such_file_or_directory;
- case ERROR_HANDLE_DISK_FULL: return errc::no_space_on_device;
- case ERROR_INVALID_ACCESS: return errc::permission_denied;
- case ERROR_INVALID_DRIVE: return errc::no_such_device;
- case ERROR_INVALID_FUNCTION: return errc::function_not_supported;
- case ERROR_INVALID_HANDLE: return errc::invalid_argument;
- case ERROR_INVALID_NAME: return errc::no_such_file_or_directory;
- case ERROR_INVALID_PARAMETER: return errc::invalid_argument;
- case ERROR_LOCK_VIOLATION: return errc::no_lock_available;
- case ERROR_LOCKED: return errc::no_lock_available;
- case ERROR_NEGATIVE_SEEK: return errc::invalid_argument;
- case ERROR_NOACCESS: return errc::permission_denied;
- case ERROR_NOT_ENOUGH_MEMORY: return errc::not_enough_memory;
- case ERROR_NOT_READY: return errc::resource_unavailable_try_again;
- case ERROR_NOT_SAME_DEVICE: return errc::cross_device_link;
- case ERROR_NOT_SUPPORTED: return errc::not_supported;
- case ERROR_OPEN_FAILED: return errc::io_error;
- case ERROR_OPEN_FILES: return errc::device_or_resource_busy;
- case ERROR_OPERATION_ABORTED: return errc::operation_canceled;
- case ERROR_OUTOFMEMORY: return errc::not_enough_memory;
- case ERROR_PATH_NOT_FOUND: return errc::no_such_file_or_directory;
- case ERROR_READ_FAULT: return errc::io_error;
- case ERROR_REPARSE_TAG_INVALID: return errc::invalid_argument;
- case ERROR_RETRY: return errc::resource_unavailable_try_again;
- case ERROR_SEEK: return errc::io_error;
- case ERROR_SHARING_VIOLATION: return errc::permission_denied;
- case ERROR_TOO_MANY_OPEN_FILES: return errc::too_many_files_open;
- case ERROR_WRITE_FAULT: return errc::io_error;
- case ERROR_WRITE_PROTECT: return errc::permission_denied;
- default: return {};
+ case ERROR_ACCESS_DENIED:
+ return errc::permission_denied;
+ case ERROR_ALREADY_EXISTS:
+ return errc::file_exists;
+ case ERROR_BAD_NETPATH:
+ return errc::no_such_file_or_directory;
+ case ERROR_BAD_PATHNAME:
+ return errc::no_such_file_or_directory;
+ case ERROR_BAD_UNIT:
+ return errc::no_such_device;
+ case ERROR_BROKEN_PIPE:
+ return errc::broken_pipe;
+ case ERROR_BUFFER_OVERFLOW:
+ return errc::filename_too_long;
+ case ERROR_BUSY:
+ return errc::device_or_resource_busy;
+ case ERROR_BUSY_DRIVE:
+ return errc::device_or_resource_busy;
+ case ERROR_CANNOT_MAKE:
+ return errc::permission_denied;
+ case ERROR_CANTOPEN:
+ return errc::io_error;
+ case ERROR_CANTREAD:
+ return errc::io_error;
+ case ERROR_CANTWRITE:
+ return errc::io_error;
+ case ERROR_CURRENT_DIRECTORY:
+ return errc::permission_denied;
+ case ERROR_DEV_NOT_EXIST:
+ return errc::no_such_device;
+ case ERROR_DEVICE_IN_USE:
+ return errc::device_or_resource_busy;
+ case ERROR_DIR_NOT_EMPTY:
+ return errc::directory_not_empty;
+ case ERROR_DIRECTORY:
+ return errc::invalid_argument;
+ case ERROR_DISK_FULL:
+ return errc::no_space_on_device;
+ case ERROR_FILE_EXISTS:
+ return errc::file_exists;
+ case ERROR_FILE_NOT_FOUND:
+ return errc::no_such_file_or_directory;
+ case ERROR_HANDLE_DISK_FULL:
+ return errc::no_space_on_device;
+ case ERROR_INVALID_ACCESS:
+ return errc::permission_denied;
+ case ERROR_INVALID_DRIVE:
+ return errc::no_such_device;
+ case ERROR_INVALID_FUNCTION:
+ return errc::function_not_supported;
+ case ERROR_INVALID_HANDLE:
+ return errc::invalid_argument;
+ case ERROR_INVALID_NAME:
+ return errc::no_such_file_or_directory;
+ case ERROR_INVALID_PARAMETER:
+ return errc::invalid_argument;
+ case ERROR_LOCK_VIOLATION:
+ return errc::no_lock_available;
+ case ERROR_LOCKED:
+ return errc::no_lock_available;
+ case ERROR_NEGATIVE_SEEK:
+ return errc::invalid_argument;
+ case ERROR_NOACCESS:
+ return errc::permission_denied;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return errc::not_enough_memory;
+ case ERROR_NOT_READY:
+ return errc::resource_unavailable_try_again;
+ case ERROR_NOT_SAME_DEVICE:
+ return errc::cross_device_link;
+ case ERROR_NOT_SUPPORTED:
+ return errc::not_supported;
+ case ERROR_OPEN_FAILED:
+ return errc::io_error;
+ case ERROR_OPEN_FILES:
+ return errc::device_or_resource_busy;
+ case ERROR_OPERATION_ABORTED:
+ return errc::operation_canceled;
+ case ERROR_OUTOFMEMORY:
+ return errc::not_enough_memory;
+ case ERROR_PATH_NOT_FOUND:
+ return errc::no_such_file_or_directory;
+ case ERROR_READ_FAULT:
+ return errc::io_error;
+ case ERROR_REPARSE_TAG_INVALID:
+ return errc::invalid_argument;
+ case ERROR_RETRY:
+ return errc::resource_unavailable_try_again;
+ case ERROR_SEEK:
+ return errc::io_error;
+ case ERROR_SHARING_VIOLATION:
+ return errc::permission_denied;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return errc::too_many_files_open;
+ case ERROR_WRITE_FAULT:
+ return errc::io_error;
+ case ERROR_WRITE_PROTECT:
+ return errc::permission_denied;
+ default:
+ return {};
}
}
} // namespace
More information about the libcxx-commits
mailing list