#include "Stat.hpp"
#ifdef _WIN32
+# include "Win32Util.hpp"
+
# include "third_party/win32/winerror_to_errno.h"
#endif
}
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;
}
#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
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
// 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
# 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
// 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(
{
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);
}
}