<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/89137>89137</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            sys::fs::status can fail on Windows with unexpected `permission_denied` error code
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          z2oh
      </td>
    </tr>
</table>

<pre>
    This is another instance of the same root cause of https://github.com/llvm/llvm-project/issues/83046

On Windows, if a file is queried with `GetFileAttributesW` after it has been marked for deletion (but not yet deleted), the query will fail with the error code being set to `ERROR_ACCESS_DENIED`.

Apple's symbol index generation code writes index unit files out with temporary names, and then calls `rename` to place the file in the final location. Multiple clang processes may produce the same index unit file, and they race to write the file out to the final destination (overwriting is okay, because the unit file is the same). But before generating index information, the final destination file is first checked to see if it's up-to-date which would avoid doing work to generate duplicate information. This check is done by calling `fs::status` on the final destination file, and if this occurs while another clang process has just called to rename its temporary index file to the final destination file, there is a small window where the destination file is marked for deletion and `fs::status` returns an unexpected `permission_denied` error.

Ideally, I think the `status` function should detect this case on Windows and return `file_not_found` instead of `permission_denied`, but I initially had trouble finding a way to do this. I filed https://github.com/apple/llvm-project/pull/8577 to treat `permission_denied` as `file_not_found` in this specific case (which fixes the build failures), but this solution is not very satisfying in that it leaks Windows-specific idiosyncrasies.

However, a colleague pointed out that the underlying `NTSTATUS` code actually does disambiguate this case with [`0xC0000056 STATUS_DELETE_PENDING`](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55) (which is mapped to Win32 `ERROR_ACCESS_DENIED`). Querying this error code is not super straightforward (at least I couldn't find a straightforward way), but I was able to identify and predicate on this case in Windows-specific code with the following patch:
```
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index fbbb27a7c133..54d6d288e017 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -40,7 +40,7 @@ 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)
+  set(system_libs ${system_libs} psapi shell32 ole32 uuid advapi32 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 1b157fa52bad..10af4905e26c 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -24,10 +24,18 @@
 
 // These two 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 <winternl.h>
+#include <ntstatus.h>
 #include <shellapi.h>
 #include <shlobj.h>
 
+extern "C" NTSYSAPI NTSTATUS NTAPI RtlGetLastNtStatus();
+
 #undef max
 
 // MinGW doesn't define this.
@@ -769,8 +777,13 @@ std::error_code status(const Twine &path, file_status &result, bool Follow) {
     return ec;
 
 DWORD attr = ::GetFileAttributesW(path_utf16.begin());
-  if (attr == INVALID_FILE_ATTRIBUTES)
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+ NTSTATUS last_nt_status = RtlGetLastNtStatus();
+    if (last_nt_status == STATUS_DELETE_PENDING)
+      return mapWindowsError(ERROR_FILE_NOT_FOUND);
     return getStatus(INVALID_HANDLE_VALUE, result);
+  }
 
   DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS;
   // Handle reparse points.
```

I prefer this solution to the alternative because the additional complexity is nicely encapsulated, but it is not without caveats, namely having to link `ntdll` and a lack of confidence in compatibility w.r.t `RtlGetLastNtStatus` (I couldn't find any Microsoft documentation on this function).

Maybe I'm missing something and there's an easy way to inspect this error code? Is the cost of this solution as it stands too high?

cc @compnerd 
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWF1z4jrS_jXOTReUkQHDRS5IgBnqJGTehJm8e0XJVhvrREg-khzC_vqtlu0EMsnUTO2mUgnYkvrpp7_FnZM7jXgZja6i0fyC17409vLfzJQXmRHHy00pHUgHXBtfogWpnec6RzAF-BLB8T2CNcZDzmsXHpfeVy5KZhFbRmy5k76ss35u9hFbKvXc_etV1vyNuY_YUjpXo4vYcpLEw3EUz6N41vy90_AotTAHF7FrkAVwKKRCAvRPjVaigIP0JUTj-Av6pVQ4897KrPboHqNxDLzwBNpDyR1kiBr23D6hgMJYEKjQS6MhYpOs9qCNhyP65jmKiE1JKmlJwo5wkEpBwaVqhNILtNZYyI1AyFDqHTj04A0BWtzf391vZ9fXi4eH7XyxXi3m0Tjun6o3qyqFEUsduOM-MwqkFvgCO9RoeUAWTj5Y6dG1L2stfSDBgal9iwT3lbHcHkHzPQauuBYEUEPOlXKExyK9JFK8gUrxHIMGDZ-6_ay5AmXyILwPt7XyslIIueJ6B5U1OTqHDvb8SN9E3R4SvOAdvhMUR7BBnGlUeZNLGnhzIlug81LzzirmGS1tIWalA_PEj3Rsho2z0b5XebSgwxKxaR-uag8ZFsbiK6N0TEApdWHsPsjpbPwzgO7UQlrnIS8xJ8_xBhwiOaP0wXZ11fOmJ7hHOJQyL-FgaiWAPxspQBgSejD2iTa2OBBEXSmZ06cTJH0I0RYEkVxhNEJ2DCakU6JxXITASmbOc187MqbRv0Df2UBSsBKBeV5bRzAVvob0mXFDoPxdk75cqUbdxnNAenfiaQ2PgaJPLdhBIDGBSQ5uzxXFDwU1HMJz2vsR7R9FKinzIQ0WfW015SmoNb5UmHsMSyu0e-mcNHorUEsUtDqE7VkorgRypYJ3rYgr_RRwReP4TUZR6zygcGWwsECPuW-YzTklv9d0FYA2mAJeqXCrjd8WptYBAeVR5ILy5Scgg6PXHlYgtfSS0EHJBXhr6kwFugV5BYcDP5INhAlQ-rAKFIpfJWLeZJ73mbiqlaI8PErTYFWL3H9KInefqdZQ4irMZSHzhpuITZroKOQLNpGa1VKJkFBrS1lr2qncbDeqDmxLFzLzM6Vgx710xbEJZPAl95TcFfIn11Hfe5UrhTTuqHPLnUR3Zu6v5oDPaEN8QG6UQr6rESojNflNyEt0eJNhBFp1bCNwvXnYzDbfH0jTkJ157utgHGHQgZCO7zO5q3nIc51nNFVqdBWN4_jlOqaf0Riak7bzxc1is9h-W6znq_UXMv1oHrHJufkUcqv7e5lb40zhWzui7tVUOU2FmvSmz01wuW1ljTe5UfRs73poLRYRW46mYz6I00kPJ5OkN5ymrDfNsrw3nOI4zjDL-WgUsembwUIoVlWTCx6lTtjn5S0k3v-jakl0Bf1PSmRrSVdXaMF5y-Wu9IWxB24FyePBlI58PqcI0xFLfXB0yhzv1h-oFkzfouTAHfCsSUdSoPayOIYwrCyKJtcafWISqX_2mKbcdsW9MEqZAylScZ-XZIrGhcZx-xu-ClkU0OvtpAd-0uPILGLLh7qqjKXQur7lT3gjnXd9_-Ih-92VjYw222ZZxlKe5oMk6fdHQzEWbDLBeJDCII7Hw2GzuNfr_T6SViV21fz-KbBoGEfDGHpDylcpROyq-9S8QC1kEbEJmSpsgPAVbh9-XMPdPdyu1l8e4e0tQMQSUDKra0kZ9J9a2rYILO9u5ov71Xz7zZpQI2rHd8GO74G-9ozLb9yXfanz_tnxXDzzSibs_Pxre6z8LA9Pro32-OIf_-j0HlD7F7GJOzqP-62SmYOIDaP06uRJlM6hcryS4EpUKmFgFCYMgsIdsldCyCT_s1NBe0EJviMblcPGHNe3s78W2693D5vt9_Xq_88M0qz4Ovux2N6sru43Zy9_GxxRN_2TgPmZ5l_55s-rTwNnkA1GacFHLOOi3x_EvBhO4xGycf4ngfOZjN8Knk83twHEhhG7HsQUQc3HSRtCLdXdv1AMYFMi9b8HAyVygdbBnnq2jMIhV7VAAYo73_V_e_6E4GqL4Eplsr8pE3e-f35wMzC1YPslJdO3zdJDJfMnannB1NSWFaE3MZr6mO3jap0w-rvenBCThFUIzdv13batn53YpEUMEWMtdx-x1n5qX_XLiLFTIVSki49l0IKTla_ikusDVXurVb-MksUna7RvGsCTNXC-IgQcr-SvVhDpZ-87YfhCCEj364gxWG8e_vUw-7aCrs2A9Ya-3nv1Bf0Nd37tH5qGtMmpydU7JeGVjD1_-dB3bqX-8hi6laa-tvYJveO5T6Zjqq8T8sk0Tckpky6vOy-aFjyU920onK4DlhvtPGwOdGzExhX3JXliaBWbRfTYoqtVcNHMGAXLUG1D55FeneSXtovG_FXXTp_54939HLj3FqJkDg2eD-4B2IQQbGtfDMb9DHdSt-Sd8NejPNf0IM1xdOJq_WN2s5pvl6ubxXa22dyvrr5vFg_nyflPtr2pRltfbUyhutX-lZtk_lsGB-ik_3wAnfFxe3kG_oTgPa_aGFuQSSM2aVq8oMX6brNd3n1fz08hnO7e4RvMTv-vs_X8ZrH9Mbv5viAzdwZ_p0SUzs_tCq1ll4rvGjYChuXN7Mv2anb91_dv24fF7Wy9WV0_nIJp_fsr10IhWKy4dW1P_-rZ561bO_tRi1igfTd3tGMtVxSh3MtnPLt44EKE1McV5GZfKXyR_hhaXJmjOgLqnFeuVjxcJTVdqvRdD0xNJk0ZOX9G7sOVDQ3ZYcZ7Dr2zAUVTaDSOm6pNE1dohBXPnyjf5kYX1OjmoQciDNzLTCqCcejbfpjcPvCjMRWZyQcttj7CbTdfgDB5vUftm5m865y7EZj6_FMKb_kxQ1hFLN1DmBT1DpzZI03Su-4ayDZ3XVwDcnfsxlYZBhf_fliIkiWsmjkxN843942n5uGO6HSea-HAGwOl3JVRsjyFleeUr4gajVbAhbhMxDSZ8gu8HKSDJJmMRmxwUV6yjInxWIzyAY5wKsZJOpyM6H02SovxKLuQlyxmw3g4SFnMRmzQn6TZaDBN0rgYj6aYimgY455L1acS1jd2dxFuNS8n00GSXiieoXLhkpUxjQcIL6mEjeYX9jJM4Vm9c9EwVqG_fj3FS6_w0h3bC4_ziw_IuW4uJE9uHsL48rt3IIHqi9qqy__i1pZU_E8AAAD__yNvMho">