platform_utils_win32.py 7.8 KB

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