Skip to content

Commit 3a9af81

Browse files
committed
[Support] Handle delete_pending case for Windows fs::status (llvm#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 llvm#89137 (cherry picked from commit cb7690a)
1 parent c7c87ee commit 3a9af81

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

llvm/include/llvm/Support/Errc.h

Lines changed: 4 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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)
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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,43 @@ void LLVMResetFatalErrorHandler() {
235235

236236
#ifdef _WIN32
237237

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

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

llvm/lib/Support/Windows/Path.inc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -745,14 +745,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
745745
return std::error_code();
746746

747747
handle_status_error:
748-
DWORD LastError = ::GetLastError();
749-
if (LastError == ERROR_FILE_NOT_FOUND || LastError == ERROR_PATH_NOT_FOUND)
748+
std::error_code Err = mapLastWindowsError();
749+
if (Err == std::errc::no_such_file_or_directory)
750750
Result = file_status(file_type::file_not_found);
751-
else if (LastError == ERROR_SHARING_VIOLATION)
751+
else if (Err == std::errc::permission_denied)
752752
Result = file_status(file_type::type_unknown);
753753
else
754754
Result = file_status(file_type::status_error);
755-
return mapWindowsError(LastError);
755+
return Err;
756756
}
757757

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

0 commit comments

Comments
 (0)