diff options
Diffstat (limited to 'platform_utils_win32.py')
-rw-r--r-- | platform_utils_win32.py | 245 |
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 | |||
19 | from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE | 19 | from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE |
20 | from ctypes.wintypes import WCHAR, USHORT, LPVOID, ULONG, LPDWORD | 20 | from ctypes.wintypes import WCHAR, USHORT, LPVOID, ULONG, LPDWORD |
21 | 21 | ||
22 | kernel32 = WinDLL('kernel32', use_last_error=True) | 22 | kernel32 = WinDLL("kernel32", use_last_error=True) |
23 | 23 | ||
24 | UCHAR = c_ubyte | 24 | UCHAR = c_ubyte |
25 | 25 | ||
@@ -31,14 +31,17 @@ ERROR_PRIVILEGE_NOT_HELD = 1314 | |||
31 | # Win32 API entry points | 31 | # Win32 API entry points |
32 | CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW | 32 | CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW |
33 | CreateSymbolicLinkW.restype = BOOLEAN | 33 | CreateSymbolicLinkW.restype = BOOLEAN |
34 | CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In | 34 | CreateSymbolicLinkW.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 |
39 | SYMBOLIC_LINK_FLAG_FILE = 0x00 | 41 | SYMBOLIC_LINK_FLAG_FILE = 0x00 |
40 | SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01 | 42 | SYMBOLIC_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) | ||
42 | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02 | 45 | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02 |
43 | 46 | ||
44 | GetFileAttributesW = kernel32.GetFileAttributesW | 47 | GetFileAttributesW = kernel32.GetFileAttributesW |
@@ -50,13 +53,15 @@ FILE_ATTRIBUTE_REPARSE_POINT = 0x00400 | |||
50 | 53 | ||
51 | CreateFileW = kernel32.CreateFileW | 54 | CreateFileW = kernel32.CreateFileW |
52 | CreateFileW.restype = HANDLE | 55 | CreateFileW.restype = HANDLE |
53 | CreateFileW.argtypes = (LPCWSTR, # lpFileName In | 56 | CreateFileW.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 | ||
61 | CloseHandle = kernel32.CloseHandle | 66 | CloseHandle = kernel32.CloseHandle |
62 | CloseHandle.restype = BOOL | 67 | CloseHandle.restype = BOOL |
@@ -69,14 +74,16 @@ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 | |||
69 | 74 | ||
70 | DeviceIoControl = kernel32.DeviceIoControl | 75 | DeviceIoControl = kernel32.DeviceIoControl |
71 | DeviceIoControl.restype = BOOL | 76 | DeviceIoControl.restype = BOOL |
72 | DeviceIoControl.argtypes = (HANDLE, # hDevice In | 77 | DeviceIoControl.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 |
82 | FSCTL_GET_REPARSE_POINT = 0x000900A8 | 89 | FSCTL_GET_REPARSE_POINT = 0x000900A8 |
@@ -86,124 +93,138 @@ MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000 | |||
86 | 93 | ||
87 | 94 | ||
88 | class GENERIC_REPARSE_BUFFER(Structure): | 95 | class GENERIC_REPARSE_BUFFER(Structure): |
89 | _fields_ = (('DataBuffer', UCHAR * 1),) | 96 | _fields_ = (("DataBuffer", UCHAR * 1),) |
90 | 97 | ||
91 | 98 | ||
92 | class SYMBOLIC_LINK_REPARSE_BUFFER(Structure): | 99 | class 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 | ||
107 | class MOUNT_POINT_REPARSE_BUFFER(Structure): | 116 | class 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 | ||
121 | class REPARSE_DATA_BUFFER(Structure): | 132 | class 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 | ||
133 | def create_filesymlink(source, link_name): | 149 | def 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 | ||
138 | def create_dirsymlink(source, link_name): | 154 | def 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 | ||
144 | def _create_symlink(source, link_name, dwFlags): | 159 | def _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 | ||
160 | def islink(path): | 178 | def 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 | ||
167 | def readlink(path): | 185 | def 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 | ||
206 | def _raise_winerror(code, error_desc): | 227 | def _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) |