[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