Windows: Add support for creating symlinks as an unprivileged user

See https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/
for announcement of new flag.

This change follow the same pattern as what was done in "go":
https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0

Change-Id: If1e99fefdd3f787598e695731019c34b9bfcd1c2
This commit is contained in:
Renaud Paquay 2018-10-01 14:59:48 -07:00
parent e469a0c741
commit 2b42d288c0

View File

@ -41,6 +41,8 @@ CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In
# Symbolic link creation flags # Symbolic link creation flags
SYMBOLIC_LINK_FLAG_FILE = 0x00 SYMBOLIC_LINK_FLAG_FILE = 0x00
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01
# symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972)
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02
GetFileAttributesW = kernel32.GetFileAttributesW GetFileAttributesW = kernel32.GetFileAttributesW
GetFileAttributesW.restype = DWORD GetFileAttributesW.restype = DWORD
@ -147,15 +149,21 @@ def _create_symlink(source, link_name, dwFlags):
# On success, the function returns "1". # On success, the function returns "1".
# On error, the function returns some random value (e.g. 1280). # On error, the function returns some random value (e.g. 1280).
# The best bet seems to use "GetLastError" and check for error/success. # The best bet seems to use "GetLastError" and check for error/success.
CreateSymbolicLinkW(link_name, source, dwFlags) CreateSymbolicLinkW(link_name, source, dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
code = get_last_error() code = get_last_error()
if code != ERROR_SUCCESS: if code != ERROR_SUCCESS:
error_desc = FormatError(code).strip() # See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0
if code == ERROR_PRIVILEGE_NOT_HELD: # "the unprivileged create flag is unsupported below Windows 10 (1703, v10.0.14972).
raise OSError(errno.EPERM, error_desc, link_name) # retry without it."
_raise_winerror( CreateSymbolicLinkW(link_name, source, dwFlags)
code, code = get_last_error()
'Error creating symbolic link \"%s\"'.format(link_name)) if code != ERROR_SUCCESS:
error_desc = FormatError(code).strip()
if code == ERROR_PRIVILEGE_NOT_HELD:
raise OSError(errno.EPERM, error_desc, link_name)
_raise_winerror(
code,
'Error creating symbolic link \"%s\"'.format(link_name))
def islink(path): def islink(path):