win32.py
773 lines
| 22.4 KiB
| text/x-python
|
PythonLexer
/ mercurial / win32.py
Martin Geisler
|
r8226 | # win32.py - utility functions that use win32 API | ||
# | ||||
Raphaël Gomès
|
r47575 | # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others | ||
Martin Geisler
|
r8226 | # | ||
# This software may be used and distributed according to the terms of the | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Martin Geisler
|
r8227 | |||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r25994 | |||
import ctypes | ||||
Yuya Nishihara
|
r35563 | import ctypes.wintypes as wintypes | ||
Gregory Szorc
|
r25994 | import errno | ||
import msvcrt | ||||
import os | ||||
import random | ||||
import subprocess | ||||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r50705 | from typing import ( | ||
List, | ||||
NoReturn, | ||||
Optional, | ||||
Tuple, | ||||
) | ||||
Pulkit Goyal
|
r30667 | from . import ( | ||
encoding, | ||||
pycompat, | ||||
) | ||||
Pulkit Goyal
|
r30637 | |||
Matt Harbison
|
r47542 | # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r13375 | _kernel32 = ctypes.windll.kernel32 | ||
Adrian Buehlmann
|
r14345 | _advapi32 = ctypes.windll.advapi32 | ||
_user32 = ctypes.windll.user32 | ||||
Matt Harbison
|
r33492 | _crypt32 = ctypes.windll.crypt32 | ||
Matt Harbison
|
r47542 | # pytype: enable=module-attr | ||
Adrian Buehlmann
|
r13375 | |||
_BOOL = ctypes.c_long | ||||
_WORD = ctypes.c_ushort | ||||
_DWORD = ctypes.c_ulong | ||||
Adrian Buehlmann
|
r14345 | _UINT = ctypes.c_uint | ||
_LONG = ctypes.c_long | ||||
Adrian Buehlmann
|
r13375 | _LPCSTR = _LPSTR = ctypes.c_char_p | ||
_HANDLE = ctypes.c_void_p | ||||
_HWND = _HANDLE | ||||
Matt Harbison
|
r33492 | _PCCERT_CONTEXT = ctypes.c_void_p | ||
Yuya Nishihara
|
r35563 | _MAX_PATH = wintypes.MAX_PATH | ||
Adrian Buehlmann
|
r13375 | |||
Adrian Buehlmann
|
r14345 | _INVALID_HANDLE_VALUE = _HANDLE(-1).value | ||
Adrian Buehlmann
|
r13375 | |||
# GetLastError | ||||
_ERROR_SUCCESS = 0 | ||||
Yuya Nishihara
|
r21193 | _ERROR_NO_MORE_FILES = 18 | ||
Adrian Buehlmann
|
r13375 | _ERROR_INVALID_PARAMETER = 87 | ||
Matt Harbison
|
r24652 | _ERROR_BROKEN_PIPE = 109 | ||
Adrian Buehlmann
|
r13375 | _ERROR_INSUFFICIENT_BUFFER = 122 | ||
Sune Foldager
|
r38575 | _ERROR_NO_DATA = 232 | ||
Adrian Buehlmann
|
r13375 | |||
# WPARAM is defined as UINT_PTR (unsigned type) | ||||
# LPARAM is defined as LONG_PTR (signed type) | ||||
if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p): | ||||
_WPARAM = ctypes.c_ulong | ||||
_LPARAM = ctypes.c_long | ||||
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p): | ||||
_WPARAM = ctypes.c_ulonglong | ||||
_LPARAM = ctypes.c_longlong | ||||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r13375 | class _FILETIME(ctypes.Structure): | ||
Augie Fackler
|
r43906 | _fields_ = [('dwLowDateTime', _DWORD), ('dwHighDateTime', _DWORD)] | ||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r13375 | |||
class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure): | ||||
Augie Fackler
|
r43346 | _fields_ = [ | ||
Augie Fackler
|
r43906 | ('dwFileAttributes', _DWORD), | ||
('ftCreationTime', _FILETIME), | ||||
('ftLastAccessTime', _FILETIME), | ||||
('ftLastWriteTime', _FILETIME), | ||||
('dwVolumeSerialNumber', _DWORD), | ||||
('nFileSizeHigh', _DWORD), | ||||
('nFileSizeLow', _DWORD), | ||||
('nNumberOfLinks', _DWORD), | ||||
('nFileIndexHigh', _DWORD), | ||||
('nFileIndexLow', _DWORD), | ||||
Augie Fackler
|
r43346 | ] | ||
Adrian Buehlmann
|
r13375 | |||
Mads Kiilerich
|
r18959 | # CreateFile | ||
Adrian Buehlmann
|
r13375 | _FILE_SHARE_READ = 0x00000001 | ||
_FILE_SHARE_WRITE = 0x00000002 | ||||
_FILE_SHARE_DELETE = 0x00000004 | ||||
_OPEN_EXISTING = 3 | ||||
Adrian Buehlmann
|
r17006 | _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 | ||
Adrian Buehlmann
|
r13776 | # SetFileAttributes | ||
_FILE_ATTRIBUTE_NORMAL = 0x80 | ||||
Adrian Buehlmann
|
r13795 | _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000 | ||
Adrian Buehlmann
|
r13776 | |||
Adrian Buehlmann
|
r13375 | # Process Security and Access Rights | ||
_PROCESS_QUERY_INFORMATION = 0x0400 | ||||
# GetExitCodeProcess | ||||
_STILL_ACTIVE = 259 | ||||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r13375 | class _STARTUPINFO(ctypes.Structure): | ||
Augie Fackler
|
r43346 | _fields_ = [ | ||
Augie Fackler
|
r43906 | ('cb', _DWORD), | ||
('lpReserved', _LPSTR), | ||||
('lpDesktop', _LPSTR), | ||||
('lpTitle', _LPSTR), | ||||
('dwX', _DWORD), | ||||
('dwY', _DWORD), | ||||
('dwXSize', _DWORD), | ||||
('dwYSize', _DWORD), | ||||
('dwXCountChars', _DWORD), | ||||
('dwYCountChars', _DWORD), | ||||
('dwFillAttribute', _DWORD), | ||||
('dwFlags', _DWORD), | ||||
('wShowWindow', _WORD), | ||||
('cbReserved2', _WORD), | ||||
('lpReserved2', ctypes.c_char_p), | ||||
('hStdInput', _HANDLE), | ||||
('hStdOutput', _HANDLE), | ||||
('hStdError', _HANDLE), | ||||
Augie Fackler
|
r43346 | ] | ||
Adrian Buehlmann
|
r13375 | |||
class _PROCESS_INFORMATION(ctypes.Structure): | ||||
Augie Fackler
|
r43346 | _fields_ = [ | ||
Augie Fackler
|
r43906 | ('hProcess', _HANDLE), | ||
('hThread', _HANDLE), | ||||
('dwProcessId', _DWORD), | ||||
('dwThreadId', _DWORD), | ||||
Augie Fackler
|
r43346 | ] | ||
Adrian Buehlmann
|
r13375 | |||
Adrian Buehlmann
|
r17050 | _CREATE_NO_WINDOW = 0x08000000 | ||
Adrian Buehlmann
|
r13375 | _SW_HIDE = 0 | ||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r13375 | class _COORD(ctypes.Structure): | ||
Augie Fackler
|
r43906 | _fields_ = [('X', ctypes.c_short), ('Y', ctypes.c_short)] | ||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r13375 | |||
class _SMALL_RECT(ctypes.Structure): | ||||
Augie Fackler
|
r43346 | _fields_ = [ | ||
Augie Fackler
|
r43906 | ('Left', ctypes.c_short), | ||
('Top', ctypes.c_short), | ||||
('Right', ctypes.c_short), | ||||
('Bottom', ctypes.c_short), | ||||
Augie Fackler
|
r43346 | ] | ||
Adrian Buehlmann
|
r13375 | |||
class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): | ||||
Augie Fackler
|
r43346 | _fields_ = [ | ||
Augie Fackler
|
r43906 | ('dwSize', _COORD), | ||
('dwCursorPosition', _COORD), | ||||
('wAttributes', _WORD), | ||||
('srWindow', _SMALL_RECT), | ||||
('dwMaximumWindowSize', _COORD), | ||||
Augie Fackler
|
r43346 | ] | ||
Matt Mackall
|
r7890 | |||
Matt Harbison
|
r32664 | _STD_OUTPUT_HANDLE = _DWORD(-11).value | ||
Adrian Buehlmann
|
r14344 | _STD_ERROR_HANDLE = _DWORD(-12).value | ||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r33492 | # CERT_TRUST_STATUS dwErrorStatus | ||
CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000 | ||||
# CertCreateCertificateContext encodings | ||||
X509_ASN_ENCODING = 0x00000001 | ||||
PKCS_7_ASN_ENCODING = 0x00010000 | ||||
Raphaël Gomès
|
r52596 | |||
Matt Harbison
|
r33492 | # These structs are only complete enough to achieve what we need. | ||
class CERT_CHAIN_CONTEXT(ctypes.Structure): | ||||
_fields_ = ( | ||||
Augie Fackler
|
r43809 | ("cbSize", _DWORD), | ||
Matt Harbison
|
r33492 | # CERT_TRUST_STATUS struct | ||
Augie Fackler
|
r43809 | ("dwErrorStatus", _DWORD), | ||
("dwInfoStatus", _DWORD), | ||||
("cChain", _DWORD), | ||||
("rgpChain", ctypes.c_void_p), | ||||
("cLowerQualityChainContext", _DWORD), | ||||
("rgpLowerQualityChainContext", ctypes.c_void_p), | ||||
("fHasRevocationFreshnessTime", _BOOL), | ||||
("dwRevocationFreshnessTime", _DWORD), | ||||
Matt Harbison
|
r33492 | ) | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r33492 | class CERT_USAGE_MATCH(ctypes.Structure): | ||
_fields_ = ( | ||||
Augie Fackler
|
r43809 | ("dwType", _DWORD), | ||
Augie Fackler
|
r43346 | # CERT_ENHKEY_USAGE struct | ||
Augie Fackler
|
r43809 | ("cUsageIdentifier", _DWORD), | ||
("rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR * | ||||
Matt Harbison
|
r33492 | ) | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r33492 | class CERT_CHAIN_PARA(ctypes.Structure): | ||
_fields_ = ( | ||||
Augie Fackler
|
r43809 | ("cbSize", _DWORD), | ||
("RequestedUsage", CERT_USAGE_MATCH), | ||||
("RequestedIssuancePolicy", CERT_USAGE_MATCH), | ||||
("dwUrlRetrievalTimeout", _DWORD), | ||||
("fCheckRevocationFreshnessTime", _BOOL), | ||||
("dwRevocationFreshnessTime", _DWORD), | ||||
("pftCacheResync", ctypes.c_void_p), # LPFILETIME | ||||
("pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA | ||||
("dwStrongSignFlags", _DWORD), | ||||
Matt Harbison
|
r33492 | ) | ||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r14345 | # types of parameters of C functions used (required by pypy) | ||
Augie Fackler
|
r43346 | _crypt32.CertCreateCertificateContext.argtypes = [ | ||
_DWORD, # cert encoding | ||||
ctypes.c_char_p, # cert | ||||
_DWORD, | ||||
] # cert size | ||||
Matt Harbison
|
r33492 | _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT | ||
_crypt32.CertGetCertificateChain.argtypes = [ | ||||
Augie Fackler
|
r43346 | ctypes.c_void_p, # HCERTCHAINENGINE | ||
_PCCERT_CONTEXT, | ||||
ctypes.c_void_p, # LPFILETIME | ||||
ctypes.c_void_p, # HCERTSTORE | ||||
ctypes.c_void_p, # PCERT_CHAIN_PARA | ||||
_DWORD, | ||||
ctypes.c_void_p, # LPVOID | ||||
ctypes.c_void_p, # PCCERT_CHAIN_CONTEXT * | ||||
] | ||||
Matt Harbison
|
r33492 | _crypt32.CertGetCertificateChain.restype = _BOOL | ||
_crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT] | ||||
_crypt32.CertFreeCertificateContext.restype = _BOOL | ||||
Augie Fackler
|
r43346 | _kernel32.CreateFileA.argtypes = [ | ||
_LPCSTR, | ||||
_DWORD, | ||||
_DWORD, | ||||
ctypes.c_void_p, | ||||
_DWORD, | ||||
_DWORD, | ||||
_HANDLE, | ||||
] | ||||
Adrian Buehlmann
|
r14345 | _kernel32.CreateFileA.restype = _HANDLE | ||
_kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p] | ||||
_kernel32.GetFileInformationByHandle.restype = _BOOL | ||||
_kernel32.CloseHandle.argtypes = [_HANDLE] | ||||
_kernel32.CloseHandle.restype = _BOOL | ||||
Matt Mackall
|
r15095 | try: | ||
_kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p] | ||||
_kernel32.CreateHardLinkA.restype = _BOOL | ||||
except AttributeError: | ||||
pass | ||||
Adrian Buehlmann
|
r14345 | |||
_kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD] | ||||
_kernel32.SetFileAttributesA.restype = _BOOL | ||||
Matt Harbison
|
r35528 | _DRIVE_UNKNOWN = 0 | ||
_DRIVE_NO_ROOT_DIR = 1 | ||||
_DRIVE_REMOVABLE = 2 | ||||
_DRIVE_FIXED = 3 | ||||
_DRIVE_REMOTE = 4 | ||||
_DRIVE_CDROM = 5 | ||||
_DRIVE_RAMDISK = 6 | ||||
_kernel32.GetDriveTypeA.argtypes = [_LPCSTR] | ||||
_kernel32.GetDriveTypeA.restype = _UINT | ||||
Augie Fackler
|
r43346 | _kernel32.GetVolumeInformationA.argtypes = [ | ||
_LPCSTR, | ||||
ctypes.c_void_p, | ||||
_DWORD, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
_DWORD, | ||||
] | ||||
Matt Harbison
|
r35528 | _kernel32.GetVolumeInformationA.restype = _BOOL | ||
_kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD] | ||||
_kernel32.GetVolumePathNameA.restype = _BOOL | ||||
Adrian Buehlmann
|
r14345 | _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD] | ||
_kernel32.OpenProcess.restype = _HANDLE | ||||
_kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p] | ||||
_kernel32.GetExitCodeProcess.restype = _BOOL | ||||
_kernel32.GetLastError.argtypes = [] | ||||
_kernel32.GetLastError.restype = _DWORD | ||||
_kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD] | ||||
_kernel32.GetModuleFileNameA.restype = _DWORD | ||||
Augie Fackler
|
r43346 | _kernel32.CreateProcessA.argtypes = [ | ||
_LPCSTR, | ||||
_LPCSTR, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
_BOOL, | ||||
_DWORD, | ||||
ctypes.c_void_p, | ||||
_LPCSTR, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
] | ||||
Adrian Buehlmann
|
r14345 | _kernel32.CreateProcessA.restype = _BOOL | ||
_kernel32.ExitProcess.argtypes = [_UINT] | ||||
_kernel32.ExitProcess.restype = None | ||||
_kernel32.GetCurrentProcessId.argtypes = [] | ||||
_kernel32.GetCurrentProcessId.restype = _DWORD | ||||
Matt Harbison
|
r47542 | # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r14345 | _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD) | ||
Matt Harbison
|
r47542 | # pytype: enable=module-attr | ||
Adrian Buehlmann
|
r14345 | _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL] | ||
_kernel32.SetConsoleCtrlHandler.restype = _BOOL | ||||
Matt Harbison
|
r32664 | _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD] | ||
_kernel32.SetConsoleMode.restype = _BOOL | ||||
_kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p] | ||||
_kernel32.GetConsoleMode.restype = _BOOL | ||||
Adrian Buehlmann
|
r14345 | _kernel32.GetStdHandle.argtypes = [_DWORD] | ||
_kernel32.GetStdHandle.restype = _HANDLE | ||||
_kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p] | ||||
_kernel32.GetConsoleScreenBufferInfo.restype = _BOOL | ||||
_advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p] | ||||
_advapi32.GetUserNameA.restype = _BOOL | ||||
_user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p] | ||||
_user32.GetWindowThreadProcessId.restype = _DWORD | ||||
_user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int] | ||||
_user32.ShowWindow.restype = _BOOL | ||||
Matt Harbison
|
r47542 | # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r14345 | _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM) | ||
Matt Harbison
|
r47542 | # pytype: enable=module-attr | ||
Adrian Buehlmann
|
r14345 | _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM] | ||
_user32.EnumWindows.restype = _BOOL | ||||
Augie Fackler
|
r43346 | _kernel32.PeekNamedPipe.argtypes = [ | ||
_HANDLE, | ||||
ctypes.c_void_p, | ||||
_DWORD, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
ctypes.c_void_p, | ||||
] | ||||
Matt Harbison
|
r24652 | _kernel32.PeekNamedPipe.restype = _BOOL | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def _raiseoserror(name: bytes) -> NoReturn: | ||
Matt Harbison
|
r33419 | # Force the code to a signed int to avoid an 'int too large' error. | ||
# See https://bugs.python.org/issue28474 | ||||
code = _kernel32.GetLastError() | ||||
Augie Fackler
|
r43346 | if code > 0x7FFFFFFF: | ||
Raphaël Gomès
|
r52596 | code -= 2**32 | ||
Matt Harbison
|
r47542 | err = ctypes.WinError(code=code) # pytype: disable=module-attr | ||
Augie Fackler
|
r43346 | raise OSError( | ||
Augie Fackler
|
r43906 | err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror) | ||
Augie Fackler
|
r43346 | ) | ||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r50705 | def _getfileinfo(name: bytes) -> _BY_HANDLE_FILE_INFORMATION: | ||
Augie Fackler
|
r43346 | fh = _kernel32.CreateFileA( | ||
name, | ||||
0, | ||||
_FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE, | ||||
None, | ||||
_OPEN_EXISTING, | ||||
_FILE_FLAG_BACKUP_SEMANTICS, | ||||
None, | ||||
) | ||||
Adrian Buehlmann
|
r13375 | if fh == _INVALID_HANDLE_VALUE: | ||
_raiseoserror(name) | ||||
try: | ||||
fi = _BY_HANDLE_FILE_INFORMATION() | ||||
if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)): | ||||
_raiseoserror(name) | ||||
return fi | ||||
finally: | ||||
_kernel32.CloseHandle(fh) | ||||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def checkcertificatechain(cert: bytes, build: bool = True) -> bool: | ||
Augie Fackler
|
r46554 | """Tests the given certificate to see if there is a complete chain to a | ||
trusted root certificate. As a side effect, missing certificates are | ||||
downloaded and installed unless ``build=False``. True is returned if a | ||||
chain to a trusted root exists (even if built on the fly), otherwise | ||||
False. NB: A chain to a trusted root does NOT imply that the certificate | ||||
is valid. | ||||
""" | ||||
Matt Harbison
|
r33492 | |||
chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT) | ||||
pchainctx = chainctxptr() | ||||
Augie Fackler
|
r43346 | chainpara = CERT_CHAIN_PARA( | ||
cbSize=ctypes.sizeof(CERT_CHAIN_PARA), RequestedUsage=CERT_USAGE_MATCH() | ||||
) | ||||
Matt Harbison
|
r33492 | |||
Augie Fackler
|
r43346 | certctx = _crypt32.CertCreateCertificateContext( | ||
X509_ASN_ENCODING, cert, len(cert) | ||||
) | ||||
Matt Harbison
|
r33492 | if certctx is None: | ||
Augie Fackler
|
r43347 | _raiseoserror(b'CertCreateCertificateContext') | ||
Matt Harbison
|
r33492 | |||
flags = 0 | ||||
if not build: | ||||
flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE | ||||
try: | ||||
# Building the certificate chain will update root certs as necessary. | ||||
Augie Fackler
|
r43346 | if not _crypt32.CertGetCertificateChain( | ||
None, # hChainEngine | ||||
certctx, # pCertContext | ||||
None, # pTime | ||||
None, # hAdditionalStore | ||||
ctypes.byref(chainpara), | ||||
flags, | ||||
None, # pvReserved | ||||
ctypes.byref(pchainctx), | ||||
): | ||||
Augie Fackler
|
r43347 | _raiseoserror(b'CertGetCertificateChain') | ||
Matt Harbison
|
r33492 | |||
chainctx = pchainctx.contents | ||||
return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0 | ||||
finally: | ||||
if pchainctx: | ||||
_crypt32.CertFreeCertificateChain(pchainctx) | ||||
_crypt32.CertFreeCertificateContext(certctx) | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def oslink(src: bytes, dst: bytes) -> None: | ||
Matt Mackall
|
r13976 | try: | ||
if not _kernel32.CreateHardLinkA(dst, src, None): | ||||
_raiseoserror(src) | ||||
Augie Fackler
|
r43346 | except AttributeError: # Wine doesn't support this function | ||
Adrian Buehlmann
|
r13375 | _raiseoserror(src) | ||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def nlinks(name: bytes) -> int: | ||
Adrian Buehlmann
|
r13375 | '''return number of hardlinks for the given file''' | ||
return _getfileinfo(name).nNumberOfLinks | ||||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52613 | def samefile(fpath1: bytes, fpath2: bytes) -> bool: | ||
'''Returns whether fpath1 and fpath2 refer to the same file or directory.''' | ||||
res1 = _getfileinfo(fpath1) | ||||
res2 = _getfileinfo(fpath2) | ||||
Augie Fackler
|
r43346 | return ( | ||
res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber | ||||
Adrian Buehlmann
|
r13375 | and res1.nFileIndexHigh == res2.nFileIndexHigh | ||
Augie Fackler
|
r43346 | and res1.nFileIndexLow == res2.nFileIndexLow | ||
) | ||||
Siddharth Agarwal
|
r10218 | |||
Matt Harbison
|
r52613 | def samedevice(fpath1: bytes, fpath2: bytes) -> bool: | ||
'''Returns whether fpath1 and fpath2 are on the same device.''' | ||||
res1 = _getfileinfo(fpath1) | ||||
res2 = _getfileinfo(fpath2) | ||||
Adrian Buehlmann
|
r13375 | return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber | ||
Siddharth Agarwal
|
r10218 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def peekpipe(pipe) -> int: | ||
Matt Harbison
|
r47542 | handle = msvcrt.get_osfhandle(pipe.fileno()) # pytype: disable=module-attr | ||
Matt Harbison
|
r24652 | avail = _DWORD() | ||
Augie Fackler
|
r43346 | if not _kernel32.PeekNamedPipe( | ||
handle, None, 0, None, ctypes.byref(avail), None | ||||
): | ||||
Matt Harbison
|
r24652 | err = _kernel32.GetLastError() | ||
if err == _ERROR_BROKEN_PIPE: | ||||
return 0 | ||||
Matt Harbison
|
r47542 | raise ctypes.WinError(err) # pytype: disable=module-attr | ||
Matt Harbison
|
r24652 | |||
return avail.value | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def lasterrorwaspipeerror(err) -> bool: | ||
Sune Foldager
|
r38575 | if err.errno != errno.EINVAL: | ||
return False | ||||
err = _kernel32.GetLastError() | ||||
return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def testpid(pid: int) -> bool: | ||
Augie Fackler
|
r46554 | """return True if pid is still running or unable to | ||
determine, False otherwise""" | ||||
Adrian Buehlmann
|
r13375 | h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid) | ||
if h: | ||||
try: | ||||
status = _DWORD() | ||||
if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)): | ||||
return status.value == _STILL_ACTIVE | ||||
finally: | ||||
_kernel32.CloseHandle(h) | ||||
return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER | ||||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def executablepath() -> bytes: | ||
Adrian Buehlmann
|
r13376 | '''return full path of hg.exe''' | ||
Adrian Buehlmann
|
r13375 | size = 600 | ||
buf = ctypes.create_string_buffer(size + 1) | ||||
len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size) | ||||
Matt Harbison
|
r47542 | # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r13375 | if len == 0: | ||
Augie Fackler
|
r43346 | raise ctypes.WinError() # Note: WinError is a function | ||
Adrian Buehlmann
|
r13375 | elif len == size: | ||
raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER) | ||||
Matt Harbison
|
r47542 | # pytype: enable=module-attr | ||
Adrian Buehlmann
|
r13376 | return buf.value | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def getvolumename(path: bytes) -> Optional[bytes]: | ||
Matt Harbison
|
r35530 | """Get the mount point of the filesystem from a directory or file | ||
(best-effort) | ||||
Matt Harbison
|
r35528 | |||
Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. | ||||
""" | ||||
# realpath() calls GetFullPathName() | ||||
realpath = os.path.realpath(path) | ||||
Yuya Nishihara
|
r35564 | # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4) | ||
# somehow fails on Windows XP | ||||
size = max(len(realpath), _MAX_PATH) + 1 | ||||
Matt Harbison
|
r35528 | buf = ctypes.create_string_buffer(size) | ||
if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size): | ||||
Matt Harbison
|
r47542 | # Note: WinError is a function | ||
raise ctypes.WinError() # pytype: disable=module-attr | ||||
Matt Harbison
|
r35528 | |||
Matt Harbison
|
r35530 | return buf.value | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def getfstype(path: bytes) -> Optional[bytes]: | ||
Matt Harbison
|
r35530 | """Get the filesystem type name from a directory or file (best-effort) | ||
Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. | ||||
""" | ||||
volume = getvolumename(path) | ||||
t = _kernel32.GetDriveTypeA(volume) | ||||
Matt Harbison
|
r35528 | |||
if t == _DRIVE_REMOTE: | ||||
Augie Fackler
|
r43347 | return b'cifs' | ||
Augie Fackler
|
r43346 | elif t not in ( | ||
_DRIVE_REMOVABLE, | ||||
_DRIVE_FIXED, | ||||
_DRIVE_CDROM, | ||||
_DRIVE_RAMDISK, | ||||
): | ||||
Matt Harbison
|
r35528 | return None | ||
Yuya Nishihara
|
r35563 | size = _MAX_PATH + 1 | ||
Matt Harbison
|
r35528 | name = ctypes.create_string_buffer(size) | ||
Augie Fackler
|
r43346 | if not _kernel32.GetVolumeInformationA( | ||
volume, None, 0, None, None, None, ctypes.byref(name), size | ||||
): | ||||
Matt Harbison
|
r47542 | # Note: WinError is a function | ||
raise ctypes.WinError() # pytype: disable=module-attr | ||||
Matt Harbison
|
r35528 | |||
return name.value | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def getuser() -> bytes: | ||
Matt Mackall
|
r7890 | '''return name of current user''' | ||
Adrian Buehlmann
|
r13375 | size = _DWORD(300) | ||
buf = ctypes.create_string_buffer(size.value + 1) | ||||
Adrian Buehlmann
|
r14345 | if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)): | ||
Matt Harbison
|
r47542 | raise ctypes.WinError() # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r13375 | return buf.value | ||
Matt Mackall
|
r7890 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | _signalhandler: List[_SIGNAL_HANDLER] = [] | ||
Adrian Buehlmann
|
r13375 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def setsignalhandler() -> None: | ||
Augie Fackler
|
r46554 | """Register a termination handler for console events including | ||
Matt Mackall
|
r7890 | CTRL+C. python signal handlers do not work well with socket | ||
operations. | ||||
Augie Fackler
|
r46554 | """ | ||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r7890 | def handler(event): | ||
Adrian Buehlmann
|
r13375 | _kernel32.ExitProcess(1) | ||
Adrian Buehlmann
|
r14237 | if _signalhandler: | ||
Augie Fackler
|
r43346 | return # already registered | ||
Adrian Buehlmann
|
r13375 | h = _SIGNAL_HANDLER(handler) | ||
Augie Fackler
|
r43346 | _signalhandler.append(h) # needed to prevent garbage collection | ||
Adrian Buehlmann
|
r13375 | if not _kernel32.SetConsoleCtrlHandler(h, True): | ||
Matt Harbison
|
r47542 | raise ctypes.WinError() # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r13375 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def hidewindow() -> None: | ||
Adrian Buehlmann
|
r13375 | def callback(hwnd, pid): | ||
wpid = _DWORD() | ||||
Adrian Buehlmann
|
r14345 | _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid)) | ||
Adrian Buehlmann
|
r13375 | if pid == wpid.value: | ||
Adrian Buehlmann
|
r14345 | _user32.ShowWindow(hwnd, _SW_HIDE) | ||
Augie Fackler
|
r43346 | return False # stop enumerating windows | ||
Adrian Buehlmann
|
r13375 | return True | ||
pid = _kernel32.GetCurrentProcessId() | ||||
Adrian Buehlmann
|
r14345 | _user32.EnumWindows(_WNDENUMPROC(callback), pid) | ||
Patrick Mezard
|
r11012 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def termsize() -> Tuple[int, int]: | ||
Adrian Buehlmann
|
r13375 | # cmd.exe does not handle CR like a unix console, the CR is | ||
# counted in the line length. On 80 columns consoles, if 80 | ||||
# characters are written, the following CR won't apply on the | ||||
# current line but on the new one. Keep room for it. | ||||
Yuya Nishihara
|
r30313 | width = 80 - 1 | ||
Yuya Nishihara
|
r30314 | height = 25 | ||
Adrian Buehlmann
|
r13375 | # Query stderr to avoid problems with redirections | ||
screenbuf = _kernel32.GetStdHandle( | ||||
Augie Fackler
|
r43346 | _STD_ERROR_HANDLE | ||
) # don't close the handle returned | ||||
Adrian Buehlmann
|
r13375 | if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE: | ||
Yuya Nishihara
|
r30314 | return width, height | ||
Adrian Buehlmann
|
r13375 | csbi = _CONSOLE_SCREEN_BUFFER_INFO() | ||
Augie Fackler
|
r43346 | if not _kernel32.GetConsoleScreenBufferInfo(screenbuf, ctypes.byref(csbi)): | ||
Yuya Nishihara
|
r30314 | return width, height | ||
Yuya Nishihara
|
r30313 | width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1' | ||
Yuya Nishihara
|
r30314 | height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1 | ||
return width, height | ||||
Adrian Buehlmann
|
r13375 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def enablevtmode() -> bool: | ||
Augie Fackler
|
r46554 | """Enable virtual terminal mode for the associated console. Return True if | ||
enabled, else False.""" | ||||
Matt Harbison
|
r32664 | |||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 | ||||
Augie Fackler
|
r43346 | handle = _kernel32.GetStdHandle( | ||
_STD_OUTPUT_HANDLE | ||||
) # don't close the handle | ||||
Matt Harbison
|
r32664 | if handle == _INVALID_HANDLE_VALUE: | ||
return False | ||||
mode = _DWORD(0) | ||||
if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)): | ||||
return False | ||||
if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: | ||||
mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ||||
if not _kernel32.SetConsoleMode(handle, mode): | ||||
return False | ||||
return True | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def spawndetached(args: List[bytes]) -> int: | ||
Adrian Buehlmann
|
r13375 | # No standard library function really spawns a fully detached | ||
# process under win32 because they allocate pipes or other objects | ||||
# to handle standard streams communications. Passing these objects | ||||
# to the child process requires handle inheritance to be enabled | ||||
# which makes really detached processes impossible. | ||||
si = _STARTUPINFO() | ||||
si.cb = ctypes.sizeof(_STARTUPINFO) | ||||
pi = _PROCESS_INFORMATION() | ||||
Augie Fackler
|
r43347 | env = b'' | ||
Pulkit Goyal
|
r30637 | for k in encoding.environ: | ||
Augie Fackler
|
r43347 | env += b"%s=%s\0" % (k, encoding.environ[k]) | ||
Adrian Buehlmann
|
r13375 | if not env: | ||
Augie Fackler
|
r43347 | env = b'\0' | ||
env += b'\0' | ||||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r39755 | args = subprocess.list2cmdline(pycompat.rapply(encoding.strfromlocal, args)) | ||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r39755 | # TODO: CreateProcessW on py3? | ||
Adrian Buehlmann
|
r13375 | res = _kernel32.CreateProcessA( | ||
Augie Fackler
|
r43346 | None, | ||
encoding.strtolocal(args), | ||||
None, | ||||
None, | ||||
False, | ||||
_CREATE_NO_WINDOW, | ||||
env, | ||||
encoding.getcwd(), | ||||
ctypes.byref(si), | ||||
ctypes.byref(pi), | ||||
) | ||||
Adrian Buehlmann
|
r13375 | if not res: | ||
Matt Harbison
|
r47542 | raise ctypes.WinError() # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r13375 | |||
Matt Harbison
|
r40964 | _kernel32.CloseHandle(pi.hProcess) | ||
_kernel32.CloseHandle(pi.hThread) | ||||
Matt Harbison
|
r32676 | return pi.dwProcessId | ||
Adrian Buehlmann
|
r13775 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52613 | def unlink(path: bytes) -> None: | ||
Adrian Buehlmann
|
r13775 | '''try to implement POSIX' unlink semantics on Windows''' | ||
Matt Harbison
|
r52613 | if os.path.isdir(path): | ||
Steve Borho
|
r21226 | # use EPERM because it is POSIX prescribed value, even though | ||
# unlink(2) on directories returns EISDIR on Linux | ||||
Augie Fackler
|
r43346 | raise IOError( | ||
errno.EPERM, | ||||
r"Unlinking directory not permitted: '%s'" | ||||
Matt Harbison
|
r52613 | % encoding.strfromlocal(path), | ||
Augie Fackler
|
r43346 | ) | ||
FUJIWARA Katsunori
|
r19159 | |||
Adrian Buehlmann
|
r13775 | # POSIX allows to unlink and rename open files. Windows has serious | ||
# problems with doing that: | ||||
# - Calling os.unlink (or os.rename) on a file f fails if f or any | ||||
# hardlinked copy of f has been opened with Python's open(). There is no | ||||
# way such a file can be deleted or renamed on Windows (other than | ||||
# scheduling the delete or rename for the next reboot). | ||||
# - Calling os.unlink on a file that has been opened with Mercurial's | ||||
# posixfile (or comparable methods) will delay the actual deletion of | ||||
# the file for as long as the file is held open. The filename is blocked | ||||
# during that time and cannot be used for recreating a new file under | ||||
# that same name ("zombie file"). Directories containing such zombie files | ||||
# cannot be removed or moved. | ||||
# A file that has been opened with posixfile can be renamed, so we rename | ||||
# f to a random temporary name before calling os.unlink on it. This allows | ||||
# callers to recreate f immediately while having other readers do their | ||||
# implicit zombie filename blocking on a temporary name. | ||||
Manuel Jacob
|
r50179 | for tries in range(10): | ||
Matt Harbison
|
r52613 | temp = b'%s-%08x' % (path, random.randint(0, 0xFFFFFFFF)) | ||
Adrian Buehlmann
|
r13775 | try: | ||
Matt Harbison
|
r52613 | os.rename(path, temp) | ||
Adrian Buehlmann
|
r13775 | break | ||
Manuel Jacob
|
r50200 | except FileExistsError: | ||
pass | ||||
Adrian Buehlmann
|
r13775 | else: | ||
Augie Fackler
|
r43809 | raise IOError(errno.EEXIST, "No usable temporary filename found") | ||
Adrian Buehlmann
|
r13775 | |||
try: | ||||
os.unlink(temp) | ||||
Adrian Buehlmann
|
r13776 | except OSError: | ||
# The unlink might have failed because the READONLY attribute may heave | ||||
# been set on the original file. Rename works fine with READONLY set, | ||||
# but not os.unlink. Reset all attributes and try again. | ||||
_kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL) | ||||
try: | ||||
os.unlink(temp) | ||||
except OSError: | ||||
# The unlink might have failed due to some very rude AV-Scanners. | ||||
# Leaking a tempfile is the lesser evil than aborting here and | ||||
# leaving some potentially serious inconsistencies. | ||||
pass | ||||
Adrian Buehlmann
|
r13795 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r50705 | def makedir(path: bytes, notindexed: bool) -> None: | ||
Adrian Buehlmann
|
r13795 | os.mkdir(path) | ||
if notindexed: | ||||
_kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) | ||||