[llvm] [Support] Handle delete_pending case for Windows fs::status (PR #90655)

Jeremy Day via llvm-commits llvm-commits at lists.llvm.org
Tue May 14 12:16:50 PDT 2024


================
@@ -785,9 +785,20 @@ 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);
+    DWORD attr;
+
+    // If getting file attributes fails due to a pending deletion, try
+    // again in a loop to avoid returning a misleading permission denied
+    // error.
+    for (int Retry = 200; Retry >= 0; --Retry) {
+      attr = ::GetFileAttributesW(path_utf16.begin());
+      if (attr != INVALID_FILE_ATTRIBUTES)
+        break;
+      std::error_code code = getStatus(INVALID_HANDLE_VALUE, result);
+      if (code != llvm::errc::delete_pending || !Retry)
+        return code;
+      ::Sleep(15);
----------------
z2oh wrote:

Yes, I suspect it's the `FILE_FLAG_DELETE_ON_CLOSE` flag passed here. Consider this test program which vaguely emulates two processes racing to `CreateFileW` at a common destination, and a third process trying to query this file:
```
#include <Windows.h>
#include <thread>
#include <cassert>

int main() {
    static LPCWSTR filePath = L"%TEMP%";
    std::thread createFileThread([]()
    {
        while (true)
        {
            HANDLE h = CreateFileW(filePath, GENERIC_READ | DELETE, 
                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                   NULL, OPEN_EXISTING, 
                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
            assert(h != NULL);
            Sleep(100);
            assert(CloseHandle(h));
            Sleep(100);
        }
    });

    std::thread createFileThread2([]()
    {
        while (true)
        {
            HANDLE h = CreateFileW(filePath, GENERIC_READ | DELETE, 
                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                   NULL, OPEN_ALWAYS, 
                   FILE_ATTRIBUTE_NORMAL, NULL);
            assert(h != NULL);
            Sleep(100);
            assert(CloseHandle(h));
            Sleep(100);
        }
    });

    std::thread getFileAttributesThread([]()
    {
        while (true)
        {
            auto attributes = GetFileAttributesW(filePath);
            if (attributes == INVALID_FILE_ATTRIBUTES)
            {
                auto lastError = GetLastError();
                assert(lastError != ERROR_ACCESS_DENIED);
            }
        }
    });

    getFileAttributesThread.join();
}
```
After a few iterations of the loop, the assertion in the `getFileAttributesThread` fails.

There may be a way to change the rename logic to avoid this, but it still seems valuable to me for `fs::status` to be able to handle the case where a deletion is pending on the queried file.

https://github.com/llvm/llvm-project/pull/90655


More information about the llvm-commits mailing list