diff --git a/libcxx/include/__filesystem/directory_entry.h b/libcxx/include/__filesystem/directory_entry.h --- 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> @@ -327,15 +328,7 @@ _LIBCPP_INLINE_VISIBILITY static bool __is_dne_error(error_code const& __ec) { - if (!__ec) - return true; - switch (static_cast(__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 --- a/libcxx/include/__system_error/system_error.h +++ b/libcxx/include/__system_error/system_error.h @@ -41,6 +41,8 @@ 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_FUNC_VIS 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 --- a/libcxx/src/filesystem/directory_iterator.cpp +++ b/libcxx/src/filesystem/directory_iterator.cpp @@ -38,11 +38,10 @@ } __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(errc::permission_denied)) + if (ignore_permission_denied && ec == errc::permission_denied) ec.clear(); return; } @@ -85,7 +84,7 @@ 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; } @@ -115,7 +114,7 @@ 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; } @@ -316,7 +315,7 @@ 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/filesystem_common.h b/libcxx/src/filesystem/filesystem_common.h --- a/libcxx/src/filesystem/filesystem_common.h +++ b/libcxx/src/filesystem/filesystem_common.h @@ -58,11 +58,6 @@ namespace detail { -#if defined(_LIBCPP_WIN32API) -// Non anonymous, to allow access from two translation units. -errc __win_err_to_errc(int err); -#endif - namespace { static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 0) string @@ -108,16 +103,21 @@ return ret; } +// On windows, libc functions use errno, but system functions use GetLastError. +// So, callers need to be careful which of these next functions they call! + error_code capture_errno() { _LIBCPP_ASSERT(errno != 0, "Expected errno to be non-zero"); return error_code(errno, generic_category()); } +error_code get_last_error() { #if defined(_LIBCPP_WIN32API) -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 T error_value(); diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp --- a/libcxx/src/filesystem/operations.cpp +++ b/libcxx/src/filesystem/operations.cpp @@ -404,74 +404,6 @@ // POSIX HELPERS -#if defined(_LIBCPP_WIN32API) -namespace detail { - -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(err)) - return pair.errc; - return errc::invalid_argument; -} - -} // namespace detail -#endif - namespace detail { namespace { @@ -566,7 +498,7 @@ 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 err("posix_stat", ec, &p); @@ -601,7 +533,7 @@ 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); } @@ -613,7 +545,7 @@ 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); } @@ -625,7 +557,7 @@ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html 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 = detail::get_last_error(); return true; } ec.clear(); @@ -634,7 +566,7 @@ 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 = detail::get_last_error(); return true; } ec.clear(); @@ -651,14 +583,13 @@ m_stat = {}; error_code m_ec; if (detail::fstat(fd, &m_stat) == -1) - m_ec = capture_errno(); + m_ec = detail::get_last_error(); m_status = create_file_status(m_ec, name, m_stat, &ec); return m_status; } } // namespace } // end namespace detail -using detail::capture_errno; using detail::ErrorHandler; using detail::StatT; using detail::TimeSpec; @@ -736,7 +667,7 @@ std::unique_ptr 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) @@ -746,7 +677,7 @@ #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 } @@ -1061,9 +992,9 @@ if (detail::mkdir(p.c_str(), static_cast(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)) @@ -1086,10 +1017,10 @@ 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)) @@ -1101,19 +1032,19 @@ error_code* ec) { ErrorHandler 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 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 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) { @@ -1142,7 +1073,7 @@ unique_ptr 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()}; } @@ -1150,7 +1081,7 @@ void __current_path(const path& p, error_code* ec) { ErrorHandler 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) { @@ -1251,10 +1182,10 @@ 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 tbuf; @@ -1314,7 +1245,7 @@ #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) @@ -1340,14 +1271,14 @@ #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(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(ret > 0, "TODO"); if (static_cast(ret) >= size) return err.report(errc::value_too_large); @@ -1358,8 +1289,9 @@ bool __remove(const path& p, error_code* ec) { ErrorHandler 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; @@ -1514,13 +1446,13 @@ void __rename(const path& from, const path& to, error_code* ec) { ErrorHandler 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 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) { @@ -1528,7 +1460,7 @@ 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(-1); return si; } @@ -1559,7 +1491,7 @@ 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 --- 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 @@ -147,11 +148,6 @@ return ft; } -int set_errno(int e = GetLastError()) { - errno = static_cast(__win_err_to_errc(e)); - return -1; -} - class WinHandle { public: WinHandle(const wchar_t *p, DWORD access, DWORD flags) { @@ -173,7 +169,7 @@ 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); @@ -189,19 +185,19 @@ 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); @@ -211,7 +207,7 @@ 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; } @@ -230,7 +226,7 @@ int mkdir(const wchar_t *path, int permissions) { (void)permissions; if (!CreateDirectoryW(path, nullptr)) - return set_errno(); + return -1; return 0; } @@ -245,10 +241,10 @@ 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; } int symlink_file(const wchar_t *oldname, const wchar_t *newname) { @@ -262,17 +258,17 @@ int link(const wchar_t *oldname, const wchar_t *newname) { if (CreateHardLinkW(newname, oldname, nullptr)) return 0; - return set_errno(); + return -1; } 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; } @@ -280,9 +276,9 @@ 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; } @@ -294,7 +290,7 @@ 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); } @@ -302,13 +298,13 @@ if (!(MoveFileExW(from, to, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))) - return set_errno(); + return -1; return 0; } int chdir(const wchar_t* path) { if (!SetCurrentDirectoryW(path)) - return set_errno(); + return -1; return 0; } @@ -328,7 +324,7 @@ break; path parent = dir.parent_path(); if (parent == dir) { - errno = ENOENT; + SetLastError(ERROR_PATH_NOT_FOUND); return -1; } dir = parent; @@ -337,7 +333,7 @@ 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; @@ -359,7 +355,6 @@ retval = GetCurrentDirectoryW(buff_size, buff.get()); } if (!retval) { - set_errno(); return nullptr; } return buff.release(); @@ -372,7 +367,6 @@ WinHandle h(path, FILE_READ_ATTRIBUTES, 0); if (!h) { - set_errno(); return nullptr; } size_t buff_size = MAX_PATH + 10; @@ -387,7 +381,6 @@ FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); } if (!retval) { - set_errno(); return nullptr; } wchar_t *ptr = buff.get(); @@ -409,21 +402,21 @@ 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; } 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 @@ -432,7 +425,7 @@ // 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 @@ -442,7 +435,7 @@ if ((perms & 0222) == 0) attributes |= FILE_ATTRIBUTE_READONLY; if (attributes != orig_attributes && !SetFileAttributesW(path, attributes)) - return set_errno(); + return -1; } return 0; } @@ -459,20 +452,20 @@ 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(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; @@ -486,11 +479,11 @@ } // 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 --- a/libcxx/src/system_error.cpp +++ b/libcxx/src/system_error.cpp @@ -16,8 +16,9 @@ #include #include #include -#include +#include #include +#include #include #include "include/config_elast.h" @@ -26,8 +27,80 @@ #include #endif +#if defined(_LIBCPP_WIN32API) +# include +# include +#endif + _LIBCPP_BEGIN_NAMESPACE_STD +#if defined(_LIBCPP_WIN32API) + +namespace { +std::optional __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 @@ 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(&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(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 @@ __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 --- 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 @@ 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 @@ 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 @@ 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 --- 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 @@ 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 --- 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 @@ { 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(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 --- 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 @@ 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 --- 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 @@ 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 --- 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 @@ 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 --- a/libcxx/test/support/filesystem_test_helper.h +++ b/libcxx/test/support/filesystem_test_helper.h @@ -677,7 +677,11 @@ 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) {