From: Nicholas Hutchinson Date: Sun, 4 Apr 2021 15:44:43 +0000 (+0100) Subject: Stat: treat pending deletes as missing files on Windows X-Git-Tag: v4.3~22^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F832%2Fhead;p=thirdparty%2Fccache.git Stat: treat pending deletes as missing files on Windows 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`. --- diff --git a/src/Stat.cpp b/src/Stat.cpp index d49168293..244fd6f5a 100644 --- a/src/Stat.cpp +++ b/src/Stat.cpp @@ -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; } diff --git a/src/Win32Util.cpp b/src/Win32Util.cpp index b259ed0e9..1a9de6a0b 100644 --- a/src/Win32Util.cpp +++ b/src/Win32Util.cpp @@ -23,6 +23,26 @@ #include #include +namespace { + +template +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(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( + GetModuleHandleA("ntdll.dll"), "RtlGetLastNtStatus"); + return get_last_ntstatus_fn(); +} + } // namespace Win32Util // From: https://stackoverflow.com/a/58162122/262458 diff --git a/src/Win32Util.hpp b/src/Win32Util.hpp index d6fc57595..0a2f1a133 100644 --- a/src/Win32Util.hpp +++ b/src/Win32Util.hpp @@ -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 diff --git a/src/system.hpp b/src/system.hpp index 6115c5ba5..fa4bbf3e3 100644 --- a/src/system.hpp +++ b/src/system.hpp @@ -158,7 +158,10 @@ const mode_t S_IWUSR = mode_t(_S_IWRITE); # include # include # define NOMINMAX 1 +# define WIN32_NO_STATUS # include +# undef WIN32_NO_STATUS +# include # define mkdir(a, b) _mkdir(a) # define execv(a, b) \ do_not_call_execv_on_windows // to protect against incidental use of MinGW diff --git a/unittest/test_Stat.cpp b/unittest/test_Stat.cpp index 6f8baa649..e5b6eeb83 100644 --- a/unittest/test_Stat.cpp +++ b/unittest/test_Stat.cpp @@ -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); } }