summaryrefslogtreecommitdiffstats
path: root/platform_utils_win32.py
diff options
context:
space:
mode:
Diffstat (limited to 'platform_utils_win32.py')
-rw-r--r--platform_utils_win32.py245
1 files changed, 133 insertions, 112 deletions
diff --git a/platform_utils_win32.py b/platform_utils_win32.py
index bf916d47..e9b15f46 100644
--- a/platform_utils_win32.py
+++ b/platform_utils_win32.py
@@ -19,7 +19,7 @@ from ctypes import c_buffer, c_ubyte, Structure, Union, byref
19from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE 19from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE
20from ctypes.wintypes import WCHAR, USHORT, LPVOID, ULONG, LPDWORD 20from ctypes.wintypes import WCHAR, USHORT, LPVOID, ULONG, LPDWORD
21 21
22kernel32 = WinDLL('kernel32', use_last_error=True) 22kernel32 = WinDLL("kernel32", use_last_error=True)
23 23
24UCHAR = c_ubyte 24UCHAR = c_ubyte
25 25
@@ -31,14 +31,17 @@ ERROR_PRIVILEGE_NOT_HELD = 1314
31# Win32 API entry points 31# Win32 API entry points
32CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW 32CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW
33CreateSymbolicLinkW.restype = BOOLEAN 33CreateSymbolicLinkW.restype = BOOLEAN
34CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In 34CreateSymbolicLinkW.argtypes = (
35 LPCWSTR, # lpTargetFileName In 35 LPCWSTR, # lpSymlinkFileName In
36 DWORD) # dwFlags In 36 LPCWSTR, # lpTargetFileName In
37 DWORD, # dwFlags In
38)
37 39
38# Symbolic link creation flags 40# Symbolic link creation flags
39SYMBOLIC_LINK_FLAG_FILE = 0x00 41SYMBOLIC_LINK_FLAG_FILE = 0x00
40SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01 42SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01
41# symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) 43# symlink support for CreateSymbolicLink() starting with Windows 10 (1703,
44# v10.0.14972)
42SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02 45SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02
43 46
44GetFileAttributesW = kernel32.GetFileAttributesW 47GetFileAttributesW = kernel32.GetFileAttributesW
@@ -50,13 +53,15 @@ FILE_ATTRIBUTE_REPARSE_POINT = 0x00400
50 53
51CreateFileW = kernel32.CreateFileW 54CreateFileW = kernel32.CreateFileW
52CreateFileW.restype = HANDLE 55CreateFileW.restype = HANDLE
53CreateFileW.argtypes = (LPCWSTR, # lpFileName In 56CreateFileW.argtypes = (
54 DWORD, # dwDesiredAccess In 57 LPCWSTR, # lpFileName In
55 DWORD, # dwShareMode In 58 DWORD, # dwDesiredAccess In
56 LPVOID, # lpSecurityAttributes In_opt 59 DWORD, # dwShareMode In
57 DWORD, # dwCreationDisposition In 60 LPVOID, # lpSecurityAttributes In_opt
58 DWORD, # dwFlagsAndAttributes In 61 DWORD, # dwCreationDisposition In
59 HANDLE) # hTemplateFile In_opt 62 DWORD, # dwFlagsAndAttributes In
63 HANDLE, # hTemplateFile In_opt
64)
60 65
61CloseHandle = kernel32.CloseHandle 66CloseHandle = kernel32.CloseHandle
62CloseHandle.restype = BOOL 67CloseHandle.restype = BOOL
@@ -69,14 +74,16 @@ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
69 74
70DeviceIoControl = kernel32.DeviceIoControl 75DeviceIoControl = kernel32.DeviceIoControl
71DeviceIoControl.restype = BOOL 76DeviceIoControl.restype = BOOL
72DeviceIoControl.argtypes = (HANDLE, # hDevice In 77DeviceIoControl.argtypes = (
73 DWORD, # dwIoControlCode In 78 HANDLE, # hDevice In
74 LPVOID, # lpInBuffer In_opt 79 DWORD, # dwIoControlCode In
75 DWORD, # nInBufferSize In 80 LPVOID, # lpInBuffer In_opt
76 LPVOID, # lpOutBuffer Out_opt 81 DWORD, # nInBufferSize In
77 DWORD, # nOutBufferSize In 82 LPVOID, # lpOutBuffer Out_opt
78 LPDWORD, # lpBytesReturned Out_opt 83 DWORD, # nOutBufferSize In
79 LPVOID) # lpOverlapped Inout_opt 84 LPDWORD, # lpBytesReturned Out_opt
85 LPVOID, # lpOverlapped Inout_opt
86)
80 87
81# Device I/O control flags and options 88# Device I/O control flags and options
82FSCTL_GET_REPARSE_POINT = 0x000900A8 89FSCTL_GET_REPARSE_POINT = 0x000900A8
@@ -86,124 +93,138 @@ MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000
86 93
87 94
88class GENERIC_REPARSE_BUFFER(Structure): 95class GENERIC_REPARSE_BUFFER(Structure):
89 _fields_ = (('DataBuffer', UCHAR * 1),) 96 _fields_ = (("DataBuffer", UCHAR * 1),)
90 97
91 98
92class SYMBOLIC_LINK_REPARSE_BUFFER(Structure): 99class SYMBOLIC_LINK_REPARSE_BUFFER(Structure):
93 _fields_ = (('SubstituteNameOffset', USHORT), 100 _fields_ = (
94 ('SubstituteNameLength', USHORT), 101 ("SubstituteNameOffset", USHORT),
95 ('PrintNameOffset', USHORT), 102 ("SubstituteNameLength", USHORT),
96 ('PrintNameLength', USHORT), 103 ("PrintNameOffset", USHORT),
97 ('Flags', ULONG), 104 ("PrintNameLength", USHORT),
98 ('PathBuffer', WCHAR * 1)) 105 ("Flags", ULONG),
99 106 ("PathBuffer", WCHAR * 1),
100 @property 107 )
101 def PrintName(self): 108
102 arrayt = WCHAR * (self.PrintNameLength // 2) 109 @property
103 offset = type(self).PathBuffer.offset + self.PrintNameOffset 110 def PrintName(self):
104 return arrayt.from_address(addressof(self) + offset).value 111 arrayt = WCHAR * (self.PrintNameLength // 2)
112 offset = type(self).PathBuffer.offset + self.PrintNameOffset
113 return arrayt.from_address(addressof(self) + offset).value
105 114
106 115
107class MOUNT_POINT_REPARSE_BUFFER(Structure): 116class MOUNT_POINT_REPARSE_BUFFER(Structure):
108 _fields_ = (('SubstituteNameOffset', USHORT), 117 _fields_ = (
109 ('SubstituteNameLength', USHORT), 118 ("SubstituteNameOffset", USHORT),
110 ('PrintNameOffset', USHORT), 119 ("SubstituteNameLength", USHORT),
111 ('PrintNameLength', USHORT), 120 ("PrintNameOffset", USHORT),
112 ('PathBuffer', WCHAR * 1)) 121 ("PrintNameLength", USHORT),
122 ("PathBuffer", WCHAR * 1),
123 )
113 124
114 @property 125 @property
115 def PrintName(self): 126 def PrintName(self):
116 arrayt = WCHAR * (self.PrintNameLength // 2) 127 arrayt = WCHAR * (self.PrintNameLength // 2)
117 offset = type(self).PathBuffer.offset + self.PrintNameOffset 128 offset = type(self).PathBuffer.offset + self.PrintNameOffset
118 return arrayt.from_address(addressof(self) + offset).value 129 return arrayt.from_address(addressof(self) + offset).value
119 130
120 131
121class REPARSE_DATA_BUFFER(Structure): 132class REPARSE_DATA_BUFFER(Structure):
122 class REPARSE_BUFFER(Union): 133 class REPARSE_BUFFER(Union):
123 _fields_ = (('SymbolicLinkReparseBuffer', SYMBOLIC_LINK_REPARSE_BUFFER), 134 _fields_ = (
124 ('MountPointReparseBuffer', MOUNT_POINT_REPARSE_BUFFER), 135 ("SymbolicLinkReparseBuffer", SYMBOLIC_LINK_REPARSE_BUFFER),
125 ('GenericReparseBuffer', GENERIC_REPARSE_BUFFER)) 136 ("MountPointReparseBuffer", MOUNT_POINT_REPARSE_BUFFER),
126 _fields_ = (('ReparseTag', ULONG), 137 ("GenericReparseBuffer", GENERIC_REPARSE_BUFFER),
127 ('ReparseDataLength', USHORT), 138 )
128 ('Reserved', USHORT), 139
129 ('ReparseBuffer', REPARSE_BUFFER)) 140 _fields_ = (
130 _anonymous_ = ('ReparseBuffer',) 141 ("ReparseTag", ULONG),
142 ("ReparseDataLength", USHORT),
143 ("Reserved", USHORT),
144 ("ReparseBuffer", REPARSE_BUFFER),
145 )
146 _anonymous_ = ("ReparseBuffer",)
131 147
132 148
133def create_filesymlink(source, link_name): 149def create_filesymlink(source, link_name):
134 """Creates a Windows file symbolic link source pointing to link_name.""" 150 """Creates a Windows file symbolic link source pointing to link_name."""
135 _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_FILE) 151 _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_FILE)
136 152
137 153
138def create_dirsymlink(source, link_name): 154def create_dirsymlink(source, link_name):
139 """Creates a Windows directory symbolic link source pointing to link_name. 155 """Creates a Windows directory symbolic link source pointing to link_name.""" # noqa: E501
140 """ 156 _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_DIRECTORY)
141 _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_DIRECTORY)
142 157
143 158
144def _create_symlink(source, link_name, dwFlags): 159def _create_symlink(source, link_name, dwFlags):
145 if not CreateSymbolicLinkW(link_name, source, 160 if not CreateSymbolicLinkW(
146 dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE): 161 link_name,
147 # See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0 162 source,
148 # "the unprivileged create flag is unsupported below Windows 10 (1703, v10.0.14972). 163 dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
149 # retry without it." 164 ):
150 if not CreateSymbolicLinkW(link_name, source, dwFlags): 165 # See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0 # noqa: E501
151 code = get_last_error() 166 # "the unprivileged create flag is unsupported below Windows 10 (1703,
152 error_desc = FormatError(code).strip() 167 # v10.0.14972). retry without it."
153 if code == ERROR_PRIVILEGE_NOT_HELD: 168 if not CreateSymbolicLinkW(link_name, source, dwFlags):
154 raise OSError(errno.EPERM, error_desc, link_name) 169 code = get_last_error()
155 _raise_winerror( 170 error_desc = FormatError(code).strip()
156 code, 171 if code == ERROR_PRIVILEGE_NOT_HELD:
157 'Error creating symbolic link \"%s\"'.format(link_name)) 172 raise OSError(errno.EPERM, error_desc, link_name)
173 _raise_winerror(
174 code, 'Error creating symbolic link "{}"'.format(link_name)
175 )
158 176
159 177
160def islink(path): 178def islink(path):
161 result = GetFileAttributesW(path) 179 result = GetFileAttributesW(path)
162 if result == INVALID_FILE_ATTRIBUTES: 180 if result == INVALID_FILE_ATTRIBUTES:
163 return False 181 return False
164 return bool(result & FILE_ATTRIBUTE_REPARSE_POINT) 182 return bool(result & FILE_ATTRIBUTE_REPARSE_POINT)
165 183
166 184
167def readlink(path): 185def readlink(path):
168 reparse_point_handle = CreateFileW(path, 186 reparse_point_handle = CreateFileW(
169 0, 187 path,
170 0, 188 0,
171 None, 189 0,
172 OPEN_EXISTING, 190 None,
173 FILE_FLAG_OPEN_REPARSE_POINT | 191 OPEN_EXISTING,
174 FILE_FLAG_BACKUP_SEMANTICS, 192 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
175 None) 193 None,
176 if reparse_point_handle == INVALID_HANDLE_VALUE: 194 )
177 _raise_winerror( 195 if reparse_point_handle == INVALID_HANDLE_VALUE:
178 get_last_error(), 196 _raise_winerror(
179 'Error opening symbolic link \"%s\"'.format(path)) 197 get_last_error(), 'Error opening symbolic link "{}"'.format(path)
180 target_buffer = c_buffer(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) 198 )
181 n_bytes_returned = DWORD() 199 target_buffer = c_buffer(MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
182 io_result = DeviceIoControl(reparse_point_handle, 200 n_bytes_returned = DWORD()
183 FSCTL_GET_REPARSE_POINT, 201 io_result = DeviceIoControl(
184 None, 202 reparse_point_handle,
185 0, 203 FSCTL_GET_REPARSE_POINT,
186 target_buffer, 204 None,
187 len(target_buffer), 205 0,
188 byref(n_bytes_returned), 206 target_buffer,
189 None) 207 len(target_buffer),
190 CloseHandle(reparse_point_handle) 208 byref(n_bytes_returned),
191 if not io_result: 209 None,
210 )
211 CloseHandle(reparse_point_handle)
212 if not io_result:
213 _raise_winerror(
214 get_last_error(), 'Error reading symbolic link "{}"'.format(path)
215 )
216 rdb = REPARSE_DATA_BUFFER.from_buffer(target_buffer)
217 if rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK:
218 return rdb.SymbolicLinkReparseBuffer.PrintName
219 elif rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT:
220 return rdb.MountPointReparseBuffer.PrintName
221 # Unsupported reparse point type.
192 _raise_winerror( 222 _raise_winerror(
193 get_last_error(), 223 ERROR_NOT_SUPPORTED, 'Error reading symbolic link "{}"'.format(path)
194 'Error reading symbolic link \"%s\"'.format(path)) 224 )
195 rdb = REPARSE_DATA_BUFFER.from_buffer(target_buffer)
196 if rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK:
197 return rdb.SymbolicLinkReparseBuffer.PrintName
198 elif rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT:
199 return rdb.MountPointReparseBuffer.PrintName
200 # Unsupported reparse point type
201 _raise_winerror(
202 ERROR_NOT_SUPPORTED,
203 'Error reading symbolic link \"%s\"'.format(path))
204 225
205 226
206def _raise_winerror(code, error_desc): 227def _raise_winerror(code, error_desc):
207 win_error_desc = FormatError(code).strip() 228 win_error_desc = FormatError(code).strip()
208 error_desc = "%s: %s".format(error_desc, win_error_desc) 229 error_desc = "{0}: {1}".format(error_desc, win_error_desc)
209 raise WinError(code, error_desc) 230 raise WinError(code, error_desc)