Fix races in CanAccessDirectory
If several bazel processes are starting concurrently some of them can fail to start with error
```Output base directory ... must be readable and writable.```
It happens because CanAccessDirectory doesn't handle `NTSTATUS` `STATUS_DELETE_PENDING` for file.
`STATUS_DELETE_PENDING` occured if `DeleteFile` finished successfully but there are open handles for the file.
`STATUS_DELETE_PENDING` is converted to `ERROR_ACCESS_DENIED` on Win32 level so client code can't distinguish pending deletion from real denied access.
To workaround this problem we can create check-file with unique name, for example with current thread id.
P.S.
This problem is reproducible on Windows versions prior to Windows 10 19H1.
Closes #10649.
PiperOrigin-RevId: 292529503
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 06ad4b8..5dc1c5a 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -21,6 +21,7 @@
#include <memory> // unique_ptr
#include <sstream>
+#include <string>
#include <vector>
#include "src/main/cpp/util/errors.h"
@@ -534,7 +535,11 @@
// The only easy way to know if a directory is writable is by attempting to
// open a file for writing in it.
- Path dummy_path = path.GetRelative("bazel_directory_access_test");
+ // File name with Thread ID avoids races among concurrent Bazel processes.
+ std::string dummy_name = "bazel_directory_access_test_";
+ dummy_name += std::to_string(::GetCurrentThreadId());
+
+ Path dummy_path = path.GetRelative(dummy_name);
// Attempt to open the dummy file for read/write access.
// If the file happens to exist, no big deal, we won't overwrite it thanks to
@@ -548,9 +553,9 @@
/* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile */ NULL);
DWORD err = GetLastError();
- if (handle == INVALID_HANDLE_VALUE && err != ERROR_ALREADY_EXISTS) {
+ if (handle == INVALID_HANDLE_VALUE) {
// We couldn't open the file, and not because the dummy file already exists.
- // Consequently it is because `wpath` doesn't exist.
+ // Consequently it is because `path` doesn't exist.
return false;
}
// The fact that we could open the file, regardless of it existing beforehand