-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Support] Handle delete_pending case for Windows fs::status #90655
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
@llvm/pr-subscribers-platform-windows @llvm/pr-subscribers-llvm-support Author: Jeremy Day (z2oh) ChangesIf a delete is pending on the file queried for status, a misleading Fixes #89137 Full diff: https://github.com/llvm/llvm-project/pull/90655.diff 3 Files Affected:
diff --git a/llvm/include/llvm/Support/Errc.h b/llvm/include/llvm/Support/Errc.h
index 9df522cbe45c76..fcb69d303109a3 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/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 03e888958a0711..36a208f0919d36 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 ws2_32)
+ # ntdll required for RtlGetLastNtStatus in lib/Support/Windows/Path.inc.
+ 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/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index 4f0336a85daaa5..96940adb3c4682 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -22,12 +22,22 @@
#include <sys/stat.h>
#include <sys/types.h>
-// These two headers must be included last, and make sure shlobj is required
+// These headers must be included last, and make sure shlobj is required
// after Windows.h to make sure it picks up our definition of _WIN32_WINNT
+#define WIN32_NO_STATUS
#include "llvm/Support/Windows/WindowsSupport.h"
+#undef WIN32_NO_STATUS
+
+#include <ntstatus.h>
#include <shellapi.h>
#include <shlobj.h>
+// 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.
+extern "C" NTSYSAPI NTSTATUS NTAPI RtlGetLastNtStatus();
+
#undef max
// MinGW doesn't define this.
@@ -786,8 +796,28 @@ std::error_code status(const Twine &path, file_status &result, bool Follow) {
DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS;
if (!Follow) {
DWORD attr = ::GetFileAttributesW(path_utf16.begin());
- if (attr == INVALID_FILE_ATTRIBUTES)
- return getStatus(INVALID_HANDLE_VALUE, result);
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ // If the file is just pending deletion, try querying file attributes
+ // again in a loop to avoid returning a misleading permission denied
+ // error.
+ if (RtlGetLastNtStatus() == STATUS_DELETE_PENDING) {
+ for (unsigned Retry = 0; Retry != 200; ++Retry) {
+ ::Sleep(10);
+ attr = ::GetFileAttributesW(path_utf16.begin());
+ if (attr != INVALID_FILE_ATTRIBUTES ||
+ RtlGetLastNtStatus() != STATUS_DELETE_PENDING)
+ break;
+ }
+
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ return RtlGetLastNtStatus() == STATUS_DELETE_PENDING
+ ? llvm::errc::delete_pending
+ : getStatus(INVALID_HANDLE_VALUE, result);
+ }
+ } else {
+ return getStatus(INVALID_HANDLE_VALUE, result);
+ }
+ }
// Handle reparse points.
if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes! LGTM.
I've added more reviewers, please give it a few days before committing in case they have anything to add/change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for all the changes! LGTM.
In the latest revision, I've expanded the loop to include the call to |
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
Thanks for your review @efriedma-quic! In the most recent update, I've removed the loop and return |
@aganea mind taking a final look at this PR? There hasn't been any additional feedback for a few days so I think it should be ready to go. |
All this sounds good to me. Thanks again for working on this. |
Perfect, thank you! I'll need someone to merge on my behalf as I lack write access. cc @compnerd |
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)
This seems to be causing an issue when building with MinGW, are you able to take a look? https://discourse.llvm.org/t/failing-to-compile-llvm-on-windows-with-clang/79422 |
Looking into this; they are using clang to compile (instead of clang-cl) and so CMake's MSVC var is false, failing this condition, and not adding ntdll as a system lib. I'm trying to figure out now where else this lib can be specified to support building under this configuration. |
It's looking like my build will succeed with
added to |
…delete [🍒 stable/20230725] [Support] Handle delete_pending case for Windows fs::status (llvm#90655)
Any reason why we can't just change the |
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 newpending_delete
error code to be handled by callersFixes #89137