platform_utils_win32.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. # Copyright (C) 2016 The Android Open Source Project
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import errno
  15. from ctypes import WinDLL, get_last_error, FormatError, WinError, addressof
  16. from ctypes import c_buffer, c_ubyte, Structure, Union, byref
  17. from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE
  18. from ctypes.wintypes import WCHAR, USHORT, LPVOID, ULONG, LPDWORD
  19. kernel32 = WinDLL('kernel32', use_last_error=True)
  20. UCHAR = c_ubyte
  21. # Win32 error codes
  22. ERROR_SUCCESS = 0
  23. ERROR_NOT_SUPPORTED = 50
  24. ERROR_PRIVILEGE_NOT_HELD = 1314
  25. # Win32 API entry points
  26. CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW
  27. CreateSymbolicLinkW.restype = BOOLEAN
  28. CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In
  29. LPCWSTR, # lpTargetFileName In
  30. DWORD) # dwFlags In
  31. # Symbolic link creation flags
  32. SYMBOLIC_LINK_FLAG_FILE = 0x00
  33. SYMBOLIC_LINK_FLAG_DIRECTORY = 0x01
  34. # symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972)
  35. SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x02
  36. GetFileAttributesW = kernel32.GetFileAttributesW
  37. GetFileAttributesW.restype = DWORD
  38. GetFileAttributesW.argtypes = (LPCWSTR,) # lpFileName In
  39. INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
  40. FILE_ATTRIBUTE_REPARSE_POINT = 0x00400
  41. CreateFileW = kernel32.CreateFileW
  42. CreateFileW.restype = HANDLE
  43. CreateFileW.argtypes = (LPCWSTR, # lpFileName In
  44. DWORD, # dwDesiredAccess In
  45. DWORD, # dwShareMode In
  46. LPVOID, # lpSecurityAttributes In_opt
  47. DWORD, # dwCreationDisposition In
  48. DWORD, # dwFlagsAndAttributes In
  49. HANDLE) # hTemplateFile In_opt
  50. CloseHandle = kernel32.CloseHandle
  51. CloseHandle.restype = BOOL
  52. CloseHandle.argtypes = (HANDLE,) # hObject In
  53. INVALID_HANDLE_VALUE = HANDLE(-1).value
  54. OPEN_EXISTING = 3
  55. FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
  56. FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
  57. DeviceIoControl = kernel32.DeviceIoControl
  58. DeviceIoControl.restype = BOOL
  59. DeviceIoControl.argtypes = (HANDLE, # hDevice In
  60. DWORD, # dwIoControlCode In
  61. LPVOID, # lpInBuffer In_opt
  62. DWORD, # nInBufferSize In
  63. LPVOID, # lpOutBuffer Out_opt
  64. DWORD, # nOutBufferSize In
  65. LPDWORD, # lpBytesReturned Out_opt
  66. LPVOID) # lpOverlapped Inout_opt
  67. # Device I/O control flags and options
  68. FSCTL_GET_REPARSE_POINT = 0x000900A8
  69. IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
  70. IO_REPARSE_TAG_SYMLINK = 0xA000000C
  71. MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000
  72. class GENERIC_REPARSE_BUFFER(Structure):
  73. _fields_ = (('DataBuffer', UCHAR * 1),)
  74. class SYMBOLIC_LINK_REPARSE_BUFFER(Structure):
  75. _fields_ = (('SubstituteNameOffset', USHORT),
  76. ('SubstituteNameLength', USHORT),
  77. ('PrintNameOffset', USHORT),
  78. ('PrintNameLength', USHORT),
  79. ('Flags', ULONG),
  80. ('PathBuffer', WCHAR * 1))
  81. @property
  82. def PrintName(self):
  83. arrayt = WCHAR * (self.PrintNameLength // 2)
  84. offset = type(self).PathBuffer.offset + self.PrintNameOffset
  85. return arrayt.from_address(addressof(self) + offset).value
  86. class MOUNT_POINT_REPARSE_BUFFER(Structure):
  87. _fields_ = (('SubstituteNameOffset', USHORT),
  88. ('SubstituteNameLength', USHORT),
  89. ('PrintNameOffset', USHORT),
  90. ('PrintNameLength', USHORT),
  91. ('PathBuffer', WCHAR * 1))
  92. @property
  93. def PrintName(self):
  94. arrayt = WCHAR * (self.PrintNameLength // 2)
  95. offset = type(self).PathBuffer.offset + self.PrintNameOffset
  96. return arrayt.from_address(addressof(self) + offset).value
  97. class REPARSE_DATA_BUFFER(Structure):
  98. class REPARSE_BUFFER(Union):
  99. _fields_ = (('SymbolicLinkReparseBuffer', SYMBOLIC_LINK_REPARSE_BUFFER),
  100. ('MountPointReparseBuffer', MOUNT_POINT_REPARSE_BUFFER),
  101. ('GenericReparseBuffer', GENERIC_REPARSE_BUFFER))
  102. _fields_ = (('ReparseTag', ULONG),
  103. ('ReparseDataLength', USHORT),
  104. ('Reserved', USHORT),
  105. ('ReparseBuffer', REPARSE_BUFFER))
  106. _anonymous_ = ('ReparseBuffer',)
  107. def create_filesymlink(source, link_name):
  108. """Creates a Windows file symbolic link source pointing to link_name."""
  109. _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_FILE)
  110. def create_dirsymlink(source, link_name):
  111. """Creates a Windows directory symbolic link source pointing to link_name.
  112. """
  113. _create_symlink(source, link_name, SYMBOLIC_LINK_FLAG_DIRECTORY)
  114. def _create_symlink(source, link_name, dwFlags):
  115. if not CreateSymbolicLinkW(link_name, source,
  116. dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE):
  117. # See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0
  118. # "the unprivileged create flag is unsupported below Windows 10 (1703, v10.0.14972).
  119. # retry without it."
  120. if not CreateSymbolicLinkW(link_name, source, dwFlags):
  121. code = get_last_error()
  122. error_desc = FormatError(code).strip()
  123. if code == ERROR_PRIVILEGE_NOT_HELD:
  124. raise OSError(errno.EPERM, error_desc, link_name)
  125. _raise_winerror(
  126. code,
  127. 'Error creating symbolic link \"%s\"'.format(link_name))
  128. def islink(path):
  129. result = GetFileAttributesW(path)
  130. if result == INVALID_FILE_ATTRIBUTES:
  131. return False
  132. return bool(result & FILE_ATTRIBUTE_REPARSE_POINT)
  133. def readlink(path):
  134. reparse_point_handle = CreateFileW(path,
  135. 0,
  136. 0,
  137. None,
  138. OPEN_EXISTING,
  139. FILE_FLAG_OPEN_REPARSE_POINT |
  140. FILE_FLAG_BACKUP_SEMANTICS,
  141. None)
  142. if reparse_point_handle == INVALID_HANDLE_VALUE:
  143. _raise_winerror(
  144. get_last_error(),
  145. 'Error opening symbolic link \"%s\"'.format(path))
  146. target_buffer = c_buffer(MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
  147. n_bytes_returned = DWORD()
  148. io_result = DeviceIoControl(reparse_point_handle,
  149. FSCTL_GET_REPARSE_POINT,
  150. None,
  151. 0,
  152. target_buffer,
  153. len(target_buffer),
  154. byref(n_bytes_returned),
  155. None)
  156. CloseHandle(reparse_point_handle)
  157. if not io_result:
  158. _raise_winerror(
  159. get_last_error(),
  160. 'Error reading symbolic link \"%s\"'.format(path))
  161. rdb = REPARSE_DATA_BUFFER.from_buffer(target_buffer)
  162. if rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK:
  163. return rdb.SymbolicLinkReparseBuffer.PrintName
  164. elif rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT:
  165. return rdb.MountPointReparseBuffer.PrintName
  166. # Unsupported reparse point type
  167. _raise_winerror(
  168. ERROR_NOT_SUPPORTED,
  169. 'Error reading symbolic link \"%s\"'.format(path))
  170. def _raise_winerror(code, error_desc):
  171. win_error_desc = FormatError(code).strip()
  172. error_desc = "%s: %s".format(error_desc, win_error_desc)
  173. raise WinError(code, error_desc)