diff --git a/llvm/include/llvm/Support/Errc.h b/llvm/include/llvm/Support/Errc.h index 9df522cbe45c7..fcb69d303109a 100644 --- a/llvm/include/llvm/Support/Errc.h +++ b/llvm/include/llvm/Support/Errc.h @@ -38,6 +38,10 @@ enum class errc { bad_address = int(std::errc::bad_address), bad_file_descriptor = int(std::errc::bad_file_descriptor), broken_pipe = int(std::errc::broken_pipe), + // There is no delete_pending in std::errc; this error code is negative to + // avoid conflicts. This error roughly corresponds with Windows' + // STATUS_DELETE_PENDING 0xC0000056. + delete_pending = -56, device_or_resource_busy = int(std::errc::device_or_resource_busy), directory_not_empty = int(std::errc::directory_not_empty), executable_format_error = int(std::errc::executable_format_error), diff --git a/llvm/include/llvm/Support/WindowsError.h b/llvm/include/llvm/Support/WindowsError.h index 1954052241243..d11e9019f5815 100644 --- a/llvm/include/llvm/Support/WindowsError.h +++ b/llvm/include/llvm/Support/WindowsError.h @@ -12,6 +12,7 @@ #include namespace llvm { +std::error_code mapLastWindowsError(); std::error_code mapWindowsError(unsigned EV); } diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index fbbb27a7c1334..1420b431628f0 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -40,7 +40,8 @@ endif() if( MSVC OR MINGW ) # libuuid required for FOLDERID_Profile usage in lib/Support/Windows/Path.inc. # advapi32 required for CryptAcquireContextW in lib/Support/Windows/Path.inc. - set(system_libs ${system_libs} psapi shell32 ole32 uuid advapi32) + # ntdll required for RtlGetLastNtStatus in lib/Support/ErrorHandling.cpp. + set(system_libs ${system_libs} psapi shell32 ole32 uuid advapi32 ws2_32 ntdll) elseif( CMAKE_HOST_UNIX ) if( HAVE_LIBRT ) set(system_libs ${system_libs} rt) diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp index b8b3b7424ac6b..0fed01216657e 100644 --- a/llvm/lib/Support/ErrorHandling.cpp +++ b/llvm/lib/Support/ErrorHandling.cpp @@ -235,8 +235,43 @@ void LLVMResetFatalErrorHandler() { #ifdef _WIN32 +#define WIN32_NO_STATUS +#include "llvm/Support/Windows/WindowsSupport.h" +#undef WIN32_NO_STATUS +#include #include +// This is equivalent to NtCurrentTeb()->LastStatusValue, but the public +// _TEB definition does not expose the LastStatusValue field directly. +// Avoid offsetting into this structure by calling RtlGetLastNtStatus +// from ntdll.dll. +// +// The return of this function will roughly match that of +// GetLastError, but this lower level API disambiguates some cases +// that GetLastError does not. +// +// For more information, see: +// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm +// https://github.com/llvm/llvm-project/issues/89137 +extern "C" NTSYSAPI NTSTATUS NTAPI RtlGetLastNtStatus(); + +// This function obtains the last error code and maps it. It may call +// RtlGetLastNtStatus, which is a lower level API that can return a +// more specific error code than GetLastError. +std::error_code llvm::mapLastWindowsError() { + unsigned EV = ::GetLastError(); + // The mapping of NTSTATUS to Win32 error loses some information; special + // case the generic ERROR_ACCESS_DENIED code to check the underlying + // NTSTATUS and potentially return a more accurate error code. + if (EV == ERROR_ACCESS_DENIED) { + llvm::errc code = RtlGetLastNtStatus() == STATUS_DELETE_PENDING + ? errc::delete_pending + : errc::permission_denied; + return make_error_code(code); + } + return mapWindowsError(EV); +} + // I'd rather not double the line count of the following. #define MAP_ERR_TO_COND(x, y) \ case x: \ diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index 1b157fa52bad9..09f433f0c8357 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -745,14 +745,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { return std::error_code(); handle_status_error: - DWORD LastError = ::GetLastError(); - if (LastError == ERROR_FILE_NOT_FOUND || LastError == ERROR_PATH_NOT_FOUND) + std::error_code Err = mapLastWindowsError(); + if (Err == std::errc::no_such_file_or_directory) Result = file_status(file_type::file_not_found); - else if (LastError == ERROR_SHARING_VIOLATION) + else if (Err == std::errc::permission_denied) Result = file_status(file_type::type_unknown); else Result = file_status(file_type::status_error); - return mapWindowsError(LastError); + return Err; } std::error_code status(const Twine &path, file_status &result, bool Follow) {