Skip to content

Commit cb7690a

Browse files
authored
[Support] Handle delete_pending case for Windows fs::status (#90655)
If a delete is pending on the file queried for status, a misleading `permission_denied` error code will be returned (this is the correct mapping of the error set by GetFileAttributesW). By querying the underlying NTSTATUS code via ntdll's RtlGetLastNtStatus, this case can be disambiguated. If this underlying error code indicates a pending delete, fs::status will return a new `pending_delete` error code to be handled by callers Fixes #89137
1 parent 10ecdee commit cb7690a

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

llvm/include/llvm/Support/Errc.h

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ enum class errc {
3838
bad_address = int(std::errc::bad_address),
3939
bad_file_descriptor = int(std::errc::bad_file_descriptor),
4040
broken_pipe = int(std::errc::broken_pipe),
41+
// There is no delete_pending in std::errc; this error code is negative to
42+
// avoid conflicts. This error roughly corresponds with Windows'
43+
// STATUS_DELETE_PENDING 0xC0000056.
44+
delete_pending = -56,
4145
device_or_resource_busy = int(std::errc::device_or_resource_busy),
4246
directory_not_empty = int(std::errc::directory_not_empty),
4347
executable_format_error = int(std::errc::executable_format_error),

llvm/include/llvm/Support/WindowsError.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <system_error>
1313

1414
namespace llvm {
15+
std::error_code mapLastWindowsError();
1516
std::error_code mapWindowsError(unsigned EV);
1617
}
1718

llvm/lib/Support/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ endif()
4040
if( MSVC OR MINGW )
4141
# libuuid required for FOLDERID_Profile usage in lib/Support/Windows/Path.inc.
4242
# advapi32 required for CryptAcquireContextW in lib/Support/Windows/Path.inc.
43-
set(system_libs ${system_libs} psapi shell32 ole32 uuid advapi32 ws2_32)
43+
# ntdll required for RtlGetLastNtStatus in lib/Support/ErrorHandling.cpp.
44+
set(system_libs ${system_libs} psapi shell32 ole32 uuid advapi32 ws2_32 ntdll)
4445
elseif( CMAKE_HOST_UNIX )
4546
if( HAVE_LIBRT )
4647
set(system_libs ${system_libs} rt)

llvm/lib/Support/ErrorHandling.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,43 @@ void LLVMResetFatalErrorHandler() {
236236

237237
#ifdef _WIN32
238238

239+
#define WIN32_NO_STATUS
240+
#include "llvm/Support/Windows/WindowsSupport.h"
241+
#undef WIN32_NO_STATUS
242+
#include <ntstatus.h>
239243
#include <winerror.h>
240244

245+
// This is equivalent to NtCurrentTeb()->LastStatusValue, but the public
246+
// _TEB definition does not expose the LastStatusValue field directly.
247+
// Avoid offsetting into this structure by calling RtlGetLastNtStatus
248+
// from ntdll.dll.
249+
//
250+
// The return of this function will roughly match that of
251+
// GetLastError, but this lower level API disambiguates some cases
252+
// that GetLastError does not.
253+
//
254+
// For more information, see:
255+
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
256+
// https://github.com/llvm/llvm-project/issues/89137
257+
extern "C" NTSYSAPI NTSTATUS NTAPI RtlGetLastNtStatus();
258+
259+
// This function obtains the last error code and maps it. It may call
260+
// RtlGetLastNtStatus, which is a lower level API that can return a
261+
// more specific error code than GetLastError.
262+
std::error_code llvm::mapLastWindowsError() {
263+
unsigned EV = ::GetLastError();
264+
// The mapping of NTSTATUS to Win32 error loses some information; special
265+
// case the generic ERROR_ACCESS_DENIED code to check the underlying
266+
// NTSTATUS and potentially return a more accurate error code.
267+
if (EV == ERROR_ACCESS_DENIED) {
268+
llvm::errc code = RtlGetLastNtStatus() == STATUS_DELETE_PENDING
269+
? errc::delete_pending
270+
: errc::permission_denied;
271+
return make_error_code(code);
272+
}
273+
return mapWindowsError(EV);
274+
}
275+
241276
// I'd rather not double the line count of the following.
242277
#define MAP_ERR_TO_COND(x, y) \
243278
case x: \

llvm/lib/Support/Windows/Path.inc

+4-4
Original file line numberDiff line numberDiff line change
@@ -760,14 +760,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
760760
return std::error_code();
761761

762762
handle_status_error:
763-
DWORD LastError = ::GetLastError();
764-
if (LastError == ERROR_FILE_NOT_FOUND || LastError == ERROR_PATH_NOT_FOUND)
763+
std::error_code Err = mapLastWindowsError();
764+
if (Err == std::errc::no_such_file_or_directory)
765765
Result = file_status(file_type::file_not_found);
766-
else if (LastError == ERROR_SHARING_VIOLATION)
766+
else if (Err == std::errc::permission_denied)
767767
Result = file_status(file_type::type_unknown);
768768
else
769769
Result = file_status(file_type::status_error);
770-
return mapWindowsError(LastError);
770+
return Err;
771771
}
772772

773773
std::error_code status(const Twine &path, file_status &result, bool Follow) {

0 commit comments

Comments
 (0)