From 33903d28bcea0005adf9b2a8cc4659c5e2999bbe Mon Sep 17 00:00:00 2001 From: Kevin Hogeland Date: Fri, 2 Jul 2021 07:20:48 -0700 Subject: [PATCH] Fix Windows developer mode symlinks The OS version check in https://github.com/bazelbuild/bazel/pull/13488 breaks the developer mode symlink behavior. `IsWindowsVersionOrGreater` does not work as advertised, and returns false on Windows 10 if not called from an executable with an associated application manifest declaring its compatibility for Windows 10. (Very cool, Microsoft.) The other methods of checking OS version are far more verbose and complicated, which doesn't seem warranted here. As an alternative workaround, this PR replaces the ahead-of-time version check with a retry without the flag if the function reports an invalid argument exception. @meteorcloudy Closes #13629. PiperOrigin-RevId: 382734470 --- src/main/native/windows/file.cc | 23 +++++++++++++---------- src/main/native/windows/file.h | 5 ++--- src/main/tools/build-runfiles-windows.cc | 8 ++++++++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc index 35521a501cbe25..72d3e4a2263fc9 100644 --- a/src/main/native/windows/file.cc +++ b/src/main/native/windows/file.cc @@ -43,16 +43,12 @@ using std::wstring; DWORD DetermineSymlinkPrivilegeFlag() { DWORD val = 0; DWORD valSize = sizeof(val); - if ( // The unprivileged create flag was introduced in Windows 10 build - // 14972: - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - !IsWindowsVersionOrGreater(10, 0, 14972) - // Check if developer mode is disabled: - || RegGetValueW( - HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", - L"AllowDevelopmentWithoutDevLicense", RRF_RT_DWORD, nullptr, &val, - &valSize) != ERROR_SUCCESS || + // Check if developer mode is disabled + if (RegGetValueW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", + L"AllowDevelopmentWithoutDevLicense", RRF_RT_DWORD, nullptr, &val, + &valSize) != ERROR_SUCCESS || val == 0) { return 0; } @@ -467,6 +463,13 @@ int CreateSymlink(const wstring& symlink_name, const wstring& symlink_target, if (!CreateSymbolicLinkW(name.c_str(), target.c_str(), symlinkPrivilegeFlag)) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // We are on a version of Windows that does not support this flag. + // Retry without the flag and return to error handling if necessary. + if (CreateSymbolicLinkW(name.c_str(), target.c_str(), 0)) { + return CreateSymlinkResult::kSuccess; + } + } *error = MakeErrorMessage( WSTR(__FILE__), __LINE__, L"CreateSymlink", symlink_target, GetLastError() == ERROR_PRIVILEGE_NOT_HELD diff --git a/src/main/native/windows/file.h b/src/main/native/windows/file.h index 421d97112e586f..590137fc842107 100644 --- a/src/main/native/windows/file.h +++ b/src/main/native/windows/file.h @@ -38,9 +38,8 @@ bool IsDeveloperModeEnabled(); DWORD DetermineSymlinkPrivilegeFlag(); // The flag SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE requires -// developer mode to be enabled. If it is not enabled, or the current -// version of Windows does not support it, do not use the flag. -// The process will need to be run with elevated privileges. +// developer mode to be enabled. If it is not enabled, do not use the +// flag. The process will need to be run with elevated privileges. const DWORD symlinkPrivilegeFlag = DetermineSymlinkPrivilegeFlag(); template diff --git a/src/main/tools/build-runfiles-windows.cc b/src/main/tools/build-runfiles-windows.cc index e0a00af387ce0f..0a0b0ab6f5d706 100644 --- a/src/main/tools/build-runfiles-windows.cc +++ b/src/main/tools/build-runfiles-windows.cc @@ -336,6 +336,14 @@ class RunfilesCreator { if (!CreateSymbolicLinkW( it.first.c_str(), it.second.c_str(), bazel::windows::symlinkPrivilegeFlag | create_dir)) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // We are on a version of Windows that does not support this flag. + // Retry without the flag and return to error handling if necessary. + if (CreateSymbolicLinkW(it.first.c_str(), it.second.c_str(), + create_dir)) { + return; + } + } if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD) { die(L"CreateSymbolicLinkW failed:\n%hs\n", "Bazel needs to create symlinks to build the runfiles tree.\n"