Skip to content

Commit

Permalink
Fix Windows developer mode symlinks
Browse files Browse the repository at this point in the history
The OS version check in bazelbuild#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 bazelbuild#13629.

PiperOrigin-RevId: 382734470
  • Loading branch information
Kevin Hogeland authored and Copybara-Service committed Jul 2, 2021
1 parent 080d4ad commit 33903d2
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 13 deletions.
23 changes: 13 additions & 10 deletions src/main/native/windows/file.cc
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions src/main/native/windows/file.h
Expand Up @@ -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 <typename char_type>
Expand Down
8 changes: 8 additions & 0 deletions src/main/tools/build-runfiles-windows.cc
Expand Up @@ -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"
Expand Down

0 comments on commit 33903d2

Please sign in to comment.