blaze_util_windows: use some widechar Win32 API
In this change:
- implement PrintErrorW (copy of PrintError but
uses FormatMessageW)
- use GetModuleFileNameW in GetSelfPath and do not
worry about UNC paths, we'll automatically handle
them in the future when we use widechar
functions everywhere. Until then, if a path
happens to have the "\\?\" prefix and that causes
an error, we won't be worse off than today; today
we just call `pdie`
- use GetTempPathW in GetOutputRoot; in theory
this might also return an UNC path but again that
shouldn't be a problem
- add comments for the arguments of CreateFileA
and CreateProcessA calls, to make the code a bit
more readable
See https://github.com/bazelbuild/bazel/issues/2181
--
PiperOrigin-RevId: 142118854
MOS_MIGRATED_REVID=142118854
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 3e2ae3b..22cc19f 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -29,9 +29,10 @@
#include <windows.h>
-#include <cstdlib>
#include <cstdio>
+#include <cstdlib>
#include <thread> // NOLINT (to slience Google-internal linter)
+#include <type_traits> // static_assert
#include "src/main/cpp/blaze_util.h"
#include "src/main/cpp/blaze_util_platform.h"
@@ -47,10 +48,20 @@
namespace blaze {
+// Ensure we can safely cast (const) wchar_t* to LP(C)WSTR.
+// This is true with MSVC but usually not with GCC.
+static_assert(sizeof(wchar_t) == sizeof(WCHAR),
+ "wchar_t and WCHAR should be the same size");
+
+// When using widechar Win32 API functions the maximum path length is 32K.
+// Add 4 characters for potential UNC prefix and a couple more for safety.
+static const size_t kWindowsPathBufferSize = 0x8010;
+
using blaze_util::die;
using blaze_util::pdie;
using std::string;
using std::vector;
+using std::wstring;
SignalHandler SignalHandler::INSTANCE;
@@ -220,6 +231,28 @@
LocalFree(message_buffer);
}
+static void PrintErrorW(const wstring& op) {
+ DWORD last_error = ::GetLastError();
+ if (last_error == 0) {
+ return;
+ }
+
+ WCHAR* message_buffer;
+ FormatMessageW(
+ /* dwFlags */ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ /* lpSource */ nullptr,
+ /* dwMessageId */ last_error,
+ /* dwLanguageId */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ /* lpBuffer */ message_buffer,
+ /* nSize */ 0,
+ /* Arguments */ nullptr);
+
+ fwprintf(stderr, L"ERROR: %s: %s (%d)\n", op.c_str(), message_buffer,
+ last_error);
+ LocalFree(message_buffer);
+}
+
void WarnFilesystemType(const string& output_base) {
}
@@ -228,37 +261,27 @@
}
string GetSelfPath() {
-#ifdef COMPILER_MSVC
- const size_t PATH_MAX = 4096;
-#endif // COMPILER_MSVC
- char buffer[PATH_MAX] = {};
- if (!GetModuleFileNameA(0, buffer, sizeof(buffer))) {
+ WCHAR buffer[kWindowsPathBufferSize] = {0};
+ if (!GetModuleFileNameW(0, buffer, kWindowsPathBufferSize)) {
pdie(255, "Error %u getting executable file name\n", GetLastError());
}
-
- // TODO(bazel-team): Implement proper handling for UNC paths
- // (e.g. "\\?\C:\foo\bar") instead of erroring out when we see them.
- if (strlen(buffer) == 0 || buffer[0] == '\\') {
- PrintError("GetModuleFileName");
- buffer[PATH_MAX - 1] = '\0';
- pdie(255, "Error in GetSelfPath, buffer=(%s)", buffer);
- }
- return string(buffer);
+ return string(blaze_util::WstringToCstring(buffer).get());
}
string GetOutputRoot() {
#ifdef COMPILER_MSVC
- // GetTempPathA and GetEnvironmentVariableA only work properly when Bazel
+ // GetTempPathW and GetEnvironmentVariableW only work properly when Bazel
// runs under cmd.exe, not when it's run from msys.
- // We don't know the reason for this; what's sure is GetEnvironmentVariableA
- // returns nothing for TEMP under msys, though it can retrieve WINDIR.
+ // The reason is that MSYS consumes all environment variables and sets its own
+ // ones. The symptom of this is that GetEnvironmentVariableW returns nothing
+ // for TEMP under MSYS, though it can retrieve WINDIR.
- char buf[MAX_PATH + 1];
- if (!GetTempPathA(sizeof(buf), buf)) {
- PrintError("GetTempPath");
+ WCHAR buffer[kWindowsPathBufferSize] = {0};
+ if (!GetTempPathW(kWindowsPathBufferSize, buffer)) {
+ PrintErrorW(L"GetTempPathW");
pdie(255, "Could not retrieve the temp directory path");
}
- return buf;
+ return string(blaze_util::WstringToCstring(buffer).get());
#else // not COMPILER_MSVC
for (const char* i : {"TMPDIR", "TEMPDIR", "TMP", "TEMP"}) {
char* tmpdir = getenv(i);
@@ -429,18 +452,17 @@
CmdLine cmdline;
CreateCommandLine(&cmdline, exe, args_vector);
- bool ok = CreateProcessA(
- NULL, // _In_opt_ LPCTSTR lpApplicationName,
- // _Inout_opt_ LPTSTR lpCommandLine,
- cmdline.cmdline,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
- true, // _In_ BOOL bInheritHandles,
- 0, // _In_ DWORD dwCreationFlags,
- NULL, // _In_opt_ LPVOID lpEnvironment,
- NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
- &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
- &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+ BOOL ok = CreateProcessA(
+ /* lpApplicationName */ NULL,
+ /* lpCommandLine */ cmdline.cmdline,
+ /* lpProcessAttributes */ NULL,
+ /* lpThreadAttributes */ NULL,
+ /* bInheritHandles */ TRUE,
+ /* dwCreationFlags */ 0,
+ /* lpEnvironment */ NULL,
+ /* lpCurrentDirectory */ NULL,
+ /* lpStartupInfo */ &startupInfo,
+ /* lpProcessInformation */ &processInfo);
if (!ok) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
@@ -527,14 +549,14 @@
sa.lpSecurityDescriptor = NULL;
HANDLE output_file = CreateFileA(
- ConvertPath(daemon_output).c_str(), // lpFileName
- GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
+ /* lpFileName */ ConvertPath(daemon_output).c_str(),
+ /* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE,
// So that the file can be read while the server is running
- FILE_SHARE_READ, // dwShareMode
- &sa, // lpSecurityAttributes
- CREATE_ALWAYS, // dwCreationDisposition
- FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
- NULL); // hTemplateFile
+ /* dwShareMode */ FILE_SHARE_READ,
+ /* lpSecurityAttributes */ &sa,
+ /* dwCreationDisposition */ CREATE_ALWAYS,
+ /* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
+ /* hTemplateFile */ NULL);
if (output_file == INVALID_HANDLE_VALUE) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreateFile");
@@ -564,19 +586,17 @@
// environment variables.
SetEnvironmentVariableA("BAZEL_SH", getenv("BAZEL_SH"));
- bool ok = CreateProcessA(
- NULL, // _In_opt_ LPCTSTR lpApplicationName,
- // _Inout_opt_ LPTSTR lpCommandLine,
- cmdline.cmdline,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
- TRUE, // _In_ BOOL bInheritHandles,
- // _In_ DWORD dwCreationFlags,
- DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
- NULL, // _In_opt_ LPVOID lpEnvironment,
- NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
- &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
- &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+ BOOL ok = CreateProcessA(
+ /* lpApplicationName */ NULL,
+ /* lpCommandLine */ cmdline.cmdline,
+ /* lpProcessAttributes */ NULL,
+ /* lpThreadAttributes */ NULL,
+ /* bInheritHandles */ TRUE,
+ /* dwCreationFlags */ DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
+ /* lpEnvironment */ NULL,
+ /* lpCurrentDirectory */ NULL,
+ /* lpStartupInfo */ &startupInfo,
+ /* lpProcessInformation */ &processInfo);
if (!ok) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
@@ -679,21 +699,20 @@
pdie(255, "Error %u while setting up job\n", GetLastError());
}
- bool success = CreateProcessA(
- NULL, // _In_opt_ LPCTSTR lpApplicationName,
- // _Inout_opt_ LPTSTR lpCommandLine,
- cmdline.cmdline,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
- NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
- true, // _In_ BOOL bInheritHandles,
- // _In_ DWORD dwCreationFlags,
- CREATE_NEW_PROCESS_GROUP // So that Ctrl-Break does not affect it
+ BOOL success = CreateProcessA(
+ /* lpApplicationName */ NULL,
+ /* lpCommandLine */ cmdline.cmdline,
+ /* lpProcessAttributes */ NULL,
+ /* lpThreadAttributes */ NULL,
+ /* bInheritHandles */ TRUE,
+ /* dwCreationFlags */
+ CREATE_NEW_PROCESS_GROUP // So that Ctrl-Break does not affect it
| CREATE_BREAKAWAY_FROM_JOB // We'll put it in a new job
| CREATE_SUSPENDED, // So that it doesn't start a new job itself
- NULL, // _In_opt_ LPVOID lpEnvironment,
- NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
- &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
- &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+ /* lpEnvironment */ NULL,
+ /* lpCurrentDirectory */ NULL,
+ /* lpStartupInfo */ &startupInfo,
+ /* lpProcessInformation */ &processInfo);
if (!success) {
pdie(255, "ExecuteProgram/CreateProcess: error %u executing: %s\n",
@@ -801,13 +820,15 @@
HANDLE OpenDirectory(const string& path, bool readWrite) {
HANDLE result = ::CreateFileA(
- path.c_str(),
- readWrite ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
+ /* lpFileName */ path.c_str(),
+ /* dwDesiredAccess */ readWrite ? (GENERIC_READ | GENERIC_WRITE)
+ : GENERIC_READ,
+ /* dwShareMode */ 0,
+ /* lpSecurityAttributes */ NULL,
+ /* dwCreationDisposition */ OPEN_EXISTING,
+ /* dwFlagsAndAttributes */ FILE_FLAG_OPEN_REPARSE_POINT |
+ FILE_FLAG_BACKUP_SEMANTICS,
+ /* hTemplateFile */ NULL);
if (result == INVALID_HANDLE_VALUE) {
PrintError("CreateFile(" + path + ")");
}