]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Stat: treat pending deletes as missing files on Windows 832/head
authorNicholas Hutchinson <nshutchinson@gmail.com>
Sun, 4 Apr 2021 15:44:43 +0000 (16:44 +0100)
committerNicholas Hutchinson <nshutchinson@gmail.com>
Sun, 4 Apr 2021 16:54:27 +0000 (17:54 +0100)
Win32 functions like `CreateFile` return `ERROR_ACCESS_DENIED` when a
file is in the process of being deleted, which gets mapped to an errno
of `EACCES`. For the purposes of `Stat::stat` and `Stat::lstat`, it's
more useful to treat this as a missing file and mapping this to an errno
of `ENOENT`.

src/Stat.cpp
src/Win32Util.cpp
src/Win32Util.hpp
src/system.hpp
unittest/test_Stat.cpp

index d491682937112bd0e703da72e7566e5cefd3579f..244fd6f5a911f9c33308fecd63c357846b958fd7 100644 (file)
@@ -19,6 +19,8 @@
 #include "Stat.hpp"
 
 #ifdef _WIN32
+#  include "Win32Util.hpp"
+
 #  include "third_party/win32/winerror_to_errno.h"
 #endif
 
@@ -124,6 +126,11 @@ win32_stat_impl(const char* path, bool traverse_links, Stat::stat_t* st)
   }
 
   if (handle == INVALID_HANDLE_VALUE) {
+    if (GetLastError() == ERROR_ACCESS_DENIED
+        && Win32Util::get_last_ntstatus() == STATUS_DELETE_PENDING) {
+      // Treat a 'pending delete' as a nonexistent file.
+      SetLastError(ERROR_FILE_NOT_FOUND);
+    }
     return false;
   }
 
index b259ed0e98d159215fa61c0e9e1118df80863a89..1a9de6a0b71ca88f066db8069c6e0cc8a9fb5417 100644 (file)
 #include <chrono>
 #include <thread>
 
+namespace {
+
+template<typename Proc>
+Proc*
+get_proc_address(HMODULE module, const char* proc_name)
+{
+#if defined __GNUC__
+#  pragma GCC diagnostic push
+#  if __GNUC__ >= 8
+#    pragma GCC diagnostic ignored "-Wcast-function-type"
+#  endif
+#endif
+  return reinterpret_cast<Proc*>(GetProcAddress(module, proc_name));
+#if defined __GNUC__
+#  pragma GCC diagnostic pop
+#endif
+}
+
+} // namespace
+
 namespace Win32Util {
 
 std::string
@@ -97,6 +117,14 @@ argv_to_string(const char* const* argv,
   return result;
 }
 
+NTSTATUS
+get_last_ntstatus()
+{
+  static auto* get_last_ntstatus_fn = get_proc_address<NTSTATUS NTAPI()>(
+    GetModuleHandleA("ntdll.dll"), "RtlGetLastNtStatus");
+  return get_last_ntstatus_fn();
+}
+
 } // namespace Win32Util
 
 // From: https://stackoverflow.com/a/58162122/262458
index d6fc57595a10c8839f13c5971a8ef768d01694a1..0a2f1a133c30ed470d4e23abd7613d6858e7ab57 100644 (file)
@@ -39,4 +39,8 @@ std::string argv_to_string(const char* const* argv,
 // Return the error message corresponding to `error_code`.
 std::string error_message(DWORD error_code);
 
+// Returns the last NTSTATUS code. (These can be more specific than the
+// corresponding Win32 error code.)
+NTSTATUS get_last_ntstatus();
+
 } // namespace Win32Util
index 6115c5ba5a214776c70de308ce5307342c3e6c0d..fa4bbf3e37d97ffd5ba39ab4afcd27d168c6c82e 100644 (file)
@@ -158,7 +158,10 @@ const mode_t S_IWUSR = mode_t(_S_IWRITE);
 #  include <io.h>
 #  include <process.h>
 #  define NOMINMAX 1
+#  define WIN32_NO_STATUS
 #  include <windows.h>
+#  undef WIN32_NO_STATUS
+#  include <ntstatus.h>
 #  define mkdir(a, b) _mkdir(a)
 #  define execv(a, b)                                                          \
     do_not_call_execv_on_windows // to protect against incidental use of MinGW
index 6f8baa6495537253af292a742ae92aa14cf7da86..e5b6eeb8385fd7fb2e7fa0f7510afb627dd2c66a 100644 (file)
@@ -579,7 +579,7 @@ TEST_CASE("Win32 Pending Delete" * doctest::skip(running_under_wine()))
   // will persist until the handle is closed. Until the file is closed, new
   // handles cannot be created to the file; attempts to do so fail with
   // ERROR_ACCESS_DENIED/STATUS_DELETE_PENDING. Our stat implementation maps
-  // these to EACCES. (Should this be ENOENT?)
+  // these to ENOENT.
   FILE_DISPOSITION_INFO info{};
   info.DeleteFile = TRUE;
   REQUIRE_MESSAGE(SetFileInformationByHandle(
@@ -590,14 +590,14 @@ TEST_CASE("Win32 Pending Delete" * doctest::skip(running_under_wine()))
   {
     auto st = Stat::stat("file");
     CHECK(!st);
-    CHECK(st.error_number() == EACCES);
+    CHECK(st.error_number() == ENOENT);
   }
 
   SUBCASE("lstat file pending delete")
   {
     auto st = Stat::lstat("file");
     CHECK(!st);
-    CHECK(st.error_number() == EACCES);
+    CHECK(st.error_number() == ENOENT);
   }
 }