##// END OF EJS Templates
win32: allocate buffer of maximum length for GetVolumeInformation()...
Yuya Nishihara -
r35563:94a12715 default
parent child Browse files
Show More
@@ -1,636 +1,638 b''
1 1 # win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import ctypes
11 import ctypes.wintypes as wintypes
11 12 import errno
12 13 import msvcrt
13 14 import os
14 15 import random
15 16 import subprocess
16 17
17 18 from . import (
18 19 encoding,
19 20 pycompat,
20 21 )
21 22
22 23 _kernel32 = ctypes.windll.kernel32
23 24 _advapi32 = ctypes.windll.advapi32
24 25 _user32 = ctypes.windll.user32
25 26 _crypt32 = ctypes.windll.crypt32
26 27
27 28 _BOOL = ctypes.c_long
28 29 _WORD = ctypes.c_ushort
29 30 _DWORD = ctypes.c_ulong
30 31 _UINT = ctypes.c_uint
31 32 _LONG = ctypes.c_long
32 33 _LPCSTR = _LPSTR = ctypes.c_char_p
33 34 _HANDLE = ctypes.c_void_p
34 35 _HWND = _HANDLE
35 36 _PCCERT_CONTEXT = ctypes.c_void_p
37 _MAX_PATH = wintypes.MAX_PATH
36 38
37 39 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
38 40
39 41 # GetLastError
40 42 _ERROR_SUCCESS = 0
41 43 _ERROR_NO_MORE_FILES = 18
42 44 _ERROR_INVALID_PARAMETER = 87
43 45 _ERROR_BROKEN_PIPE = 109
44 46 _ERROR_INSUFFICIENT_BUFFER = 122
45 47
46 48 # WPARAM is defined as UINT_PTR (unsigned type)
47 49 # LPARAM is defined as LONG_PTR (signed type)
48 50 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
49 51 _WPARAM = ctypes.c_ulong
50 52 _LPARAM = ctypes.c_long
51 53 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
52 54 _WPARAM = ctypes.c_ulonglong
53 55 _LPARAM = ctypes.c_longlong
54 56
55 57 class _FILETIME(ctypes.Structure):
56 58 _fields_ = [('dwLowDateTime', _DWORD),
57 59 ('dwHighDateTime', _DWORD)]
58 60
59 61 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
60 62 _fields_ = [('dwFileAttributes', _DWORD),
61 63 ('ftCreationTime', _FILETIME),
62 64 ('ftLastAccessTime', _FILETIME),
63 65 ('ftLastWriteTime', _FILETIME),
64 66 ('dwVolumeSerialNumber', _DWORD),
65 67 ('nFileSizeHigh', _DWORD),
66 68 ('nFileSizeLow', _DWORD),
67 69 ('nNumberOfLinks', _DWORD),
68 70 ('nFileIndexHigh', _DWORD),
69 71 ('nFileIndexLow', _DWORD)]
70 72
71 73 # CreateFile
72 74 _FILE_SHARE_READ = 0x00000001
73 75 _FILE_SHARE_WRITE = 0x00000002
74 76 _FILE_SHARE_DELETE = 0x00000004
75 77
76 78 _OPEN_EXISTING = 3
77 79
78 80 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
79 81
80 82 # SetFileAttributes
81 83 _FILE_ATTRIBUTE_NORMAL = 0x80
82 84 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
83 85
84 86 # Process Security and Access Rights
85 87 _PROCESS_QUERY_INFORMATION = 0x0400
86 88
87 89 # GetExitCodeProcess
88 90 _STILL_ACTIVE = 259
89 91
90 92 class _STARTUPINFO(ctypes.Structure):
91 93 _fields_ = [('cb', _DWORD),
92 94 ('lpReserved', _LPSTR),
93 95 ('lpDesktop', _LPSTR),
94 96 ('lpTitle', _LPSTR),
95 97 ('dwX', _DWORD),
96 98 ('dwY', _DWORD),
97 99 ('dwXSize', _DWORD),
98 100 ('dwYSize', _DWORD),
99 101 ('dwXCountChars', _DWORD),
100 102 ('dwYCountChars', _DWORD),
101 103 ('dwFillAttribute', _DWORD),
102 104 ('dwFlags', _DWORD),
103 105 ('wShowWindow', _WORD),
104 106 ('cbReserved2', _WORD),
105 107 ('lpReserved2', ctypes.c_char_p),
106 108 ('hStdInput', _HANDLE),
107 109 ('hStdOutput', _HANDLE),
108 110 ('hStdError', _HANDLE)]
109 111
110 112 class _PROCESS_INFORMATION(ctypes.Structure):
111 113 _fields_ = [('hProcess', _HANDLE),
112 114 ('hThread', _HANDLE),
113 115 ('dwProcessId', _DWORD),
114 116 ('dwThreadId', _DWORD)]
115 117
116 118 _CREATE_NO_WINDOW = 0x08000000
117 119 _SW_HIDE = 0
118 120
119 121 class _COORD(ctypes.Structure):
120 122 _fields_ = [('X', ctypes.c_short),
121 123 ('Y', ctypes.c_short)]
122 124
123 125 class _SMALL_RECT(ctypes.Structure):
124 126 _fields_ = [('Left', ctypes.c_short),
125 127 ('Top', ctypes.c_short),
126 128 ('Right', ctypes.c_short),
127 129 ('Bottom', ctypes.c_short)]
128 130
129 131 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
130 132 _fields_ = [('dwSize', _COORD),
131 133 ('dwCursorPosition', _COORD),
132 134 ('wAttributes', _WORD),
133 135 ('srWindow', _SMALL_RECT),
134 136 ('dwMaximumWindowSize', _COORD)]
135 137
136 138 _STD_OUTPUT_HANDLE = _DWORD(-11).value
137 139 _STD_ERROR_HANDLE = _DWORD(-12).value
138 140
139 141 # CERT_TRUST_STATUS dwErrorStatus
140 142 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
141 143
142 144 # CertCreateCertificateContext encodings
143 145 X509_ASN_ENCODING = 0x00000001
144 146 PKCS_7_ASN_ENCODING = 0x00010000
145 147
146 148 # These structs are only complete enough to achieve what we need.
147 149 class CERT_CHAIN_CONTEXT(ctypes.Structure):
148 150 _fields_ = (
149 151 ("cbSize", _DWORD),
150 152
151 153 # CERT_TRUST_STATUS struct
152 154 ("dwErrorStatus", _DWORD),
153 155 ("dwInfoStatus", _DWORD),
154 156
155 157 ("cChain", _DWORD),
156 158 ("rgpChain", ctypes.c_void_p),
157 159 ("cLowerQualityChainContext", _DWORD),
158 160 ("rgpLowerQualityChainContext", ctypes.c_void_p),
159 161 ("fHasRevocationFreshnessTime", _BOOL),
160 162 ("dwRevocationFreshnessTime", _DWORD),
161 163 )
162 164
163 165 class CERT_USAGE_MATCH(ctypes.Structure):
164 166 _fields_ = (
165 167 ("dwType", _DWORD),
166 168
167 169 # CERT_ENHKEY_USAGE struct
168 170 ("cUsageIdentifier", _DWORD),
169 171 ("rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
170 172 )
171 173
172 174 class CERT_CHAIN_PARA(ctypes.Structure):
173 175 _fields_ = (
174 176 ("cbSize", _DWORD),
175 177 ("RequestedUsage", CERT_USAGE_MATCH),
176 178 ("RequestedIssuancePolicy", CERT_USAGE_MATCH),
177 179 ("dwUrlRetrievalTimeout", _DWORD),
178 180 ("fCheckRevocationFreshnessTime", _BOOL),
179 181 ("dwRevocationFreshnessTime", _DWORD),
180 182 ("pftCacheResync", ctypes.c_void_p), # LPFILETIME
181 183 ("pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
182 184 ("dwStrongSignFlags", _DWORD),
183 185 )
184 186
185 187 # types of parameters of C functions used (required by pypy)
186 188
187 189 _crypt32.CertCreateCertificateContext.argtypes = [_DWORD, # cert encoding
188 190 ctypes.c_char_p, # cert
189 191 _DWORD] # cert size
190 192 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
191 193
192 194 _crypt32.CertGetCertificateChain.argtypes = [
193 195 ctypes.c_void_p, # HCERTCHAINENGINE
194 196 _PCCERT_CONTEXT,
195 197 ctypes.c_void_p, # LPFILETIME
196 198 ctypes.c_void_p, # HCERTSTORE
197 199 ctypes.c_void_p, # PCERT_CHAIN_PARA
198 200 _DWORD,
199 201 ctypes.c_void_p, # LPVOID
200 202 ctypes.c_void_p # PCCERT_CHAIN_CONTEXT *
201 203 ]
202 204 _crypt32.CertGetCertificateChain.restype = _BOOL
203 205
204 206 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
205 207 _crypt32.CertFreeCertificateContext.restype = _BOOL
206 208
207 209 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
208 210 _DWORD, _DWORD, _HANDLE]
209 211 _kernel32.CreateFileA.restype = _HANDLE
210 212
211 213 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
212 214 _kernel32.GetFileInformationByHandle.restype = _BOOL
213 215
214 216 _kernel32.CloseHandle.argtypes = [_HANDLE]
215 217 _kernel32.CloseHandle.restype = _BOOL
216 218
217 219 try:
218 220 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
219 221 _kernel32.CreateHardLinkA.restype = _BOOL
220 222 except AttributeError:
221 223 pass
222 224
223 225 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
224 226 _kernel32.SetFileAttributesA.restype = _BOOL
225 227
226 228 _DRIVE_UNKNOWN = 0
227 229 _DRIVE_NO_ROOT_DIR = 1
228 230 _DRIVE_REMOVABLE = 2
229 231 _DRIVE_FIXED = 3
230 232 _DRIVE_REMOTE = 4
231 233 _DRIVE_CDROM = 5
232 234 _DRIVE_RAMDISK = 6
233 235
234 236 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
235 237 _kernel32.GetDriveTypeA.restype = _UINT
236 238
237 239 _kernel32.GetVolumeInformationA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD,
238 240 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, _DWORD]
239 241 _kernel32.GetVolumeInformationA.restype = _BOOL
240 242
241 243 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
242 244 _kernel32.GetVolumePathNameA.restype = _BOOL
243 245
244 246 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
245 247 _kernel32.OpenProcess.restype = _HANDLE
246 248
247 249 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
248 250 _kernel32.GetExitCodeProcess.restype = _BOOL
249 251
250 252 _kernel32.GetLastError.argtypes = []
251 253 _kernel32.GetLastError.restype = _DWORD
252 254
253 255 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
254 256 _kernel32.GetModuleFileNameA.restype = _DWORD
255 257
256 258 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
257 259 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
258 260 ctypes.c_void_p]
259 261 _kernel32.CreateProcessA.restype = _BOOL
260 262
261 263 _kernel32.ExitProcess.argtypes = [_UINT]
262 264 _kernel32.ExitProcess.restype = None
263 265
264 266 _kernel32.GetCurrentProcessId.argtypes = []
265 267 _kernel32.GetCurrentProcessId.restype = _DWORD
266 268
267 269 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
268 270 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
269 271 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
270 272
271 273 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
272 274 _kernel32.SetConsoleMode.restype = _BOOL
273 275
274 276 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
275 277 _kernel32.GetConsoleMode.restype = _BOOL
276 278
277 279 _kernel32.GetStdHandle.argtypes = [_DWORD]
278 280 _kernel32.GetStdHandle.restype = _HANDLE
279 281
280 282 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
281 283 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
282 284
283 285 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
284 286 _advapi32.GetUserNameA.restype = _BOOL
285 287
286 288 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
287 289 _user32.GetWindowThreadProcessId.restype = _DWORD
288 290
289 291 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
290 292 _user32.ShowWindow.restype = _BOOL
291 293
292 294 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
293 295 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
294 296 _user32.EnumWindows.restype = _BOOL
295 297
296 298 _kernel32.PeekNamedPipe.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD,
297 299 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
298 300 _kernel32.PeekNamedPipe.restype = _BOOL
299 301
300 302 def _raiseoserror(name):
301 303 # Force the code to a signed int to avoid an 'int too large' error.
302 304 # See https://bugs.python.org/issue28474
303 305 code = _kernel32.GetLastError()
304 306 if code > 0x7fffffff:
305 307 code -= 2**32
306 308 err = ctypes.WinError(code=code)
307 309 raise OSError(err.errno, '%s: %s' % (name,
308 310 encoding.strtolocal(err.strerror)))
309 311
310 312 def _getfileinfo(name):
311 313 fh = _kernel32.CreateFileA(name, 0,
312 314 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
313 315 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
314 316 if fh == _INVALID_HANDLE_VALUE:
315 317 _raiseoserror(name)
316 318 try:
317 319 fi = _BY_HANDLE_FILE_INFORMATION()
318 320 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
319 321 _raiseoserror(name)
320 322 return fi
321 323 finally:
322 324 _kernel32.CloseHandle(fh)
323 325
324 326 def checkcertificatechain(cert, build=True):
325 327 '''Tests the given certificate to see if there is a complete chain to a
326 328 trusted root certificate. As a side effect, missing certificates are
327 329 downloaded and installed unless ``build=False``. True is returned if a
328 330 chain to a trusted root exists (even if built on the fly), otherwise
329 331 False. NB: A chain to a trusted root does NOT imply that the certificate
330 332 is valid.
331 333 '''
332 334
333 335 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
334 336
335 337 pchainctx = chainctxptr()
336 338 chainpara = CERT_CHAIN_PARA(cbSize=ctypes.sizeof(CERT_CHAIN_PARA),
337 339 RequestedUsage=CERT_USAGE_MATCH())
338 340
339 341 certctx = _crypt32.CertCreateCertificateContext(X509_ASN_ENCODING, cert,
340 342 len(cert))
341 343 if certctx is None:
342 344 _raiseoserror('CertCreateCertificateContext')
343 345
344 346 flags = 0
345 347
346 348 if not build:
347 349 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
348 350
349 351 try:
350 352 # Building the certificate chain will update root certs as necessary.
351 353 if not _crypt32.CertGetCertificateChain(None, # hChainEngine
352 354 certctx, # pCertContext
353 355 None, # pTime
354 356 None, # hAdditionalStore
355 357 ctypes.byref(chainpara),
356 358 flags,
357 359 None, # pvReserved
358 360 ctypes.byref(pchainctx)):
359 361 _raiseoserror('CertGetCertificateChain')
360 362
361 363 chainctx = pchainctx.contents
362 364
363 365 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
364 366 finally:
365 367 if pchainctx:
366 368 _crypt32.CertFreeCertificateChain(pchainctx)
367 369 _crypt32.CertFreeCertificateContext(certctx)
368 370
369 371 def oslink(src, dst):
370 372 try:
371 373 if not _kernel32.CreateHardLinkA(dst, src, None):
372 374 _raiseoserror(src)
373 375 except AttributeError: # Wine doesn't support this function
374 376 _raiseoserror(src)
375 377
376 378 def nlinks(name):
377 379 '''return number of hardlinks for the given file'''
378 380 return _getfileinfo(name).nNumberOfLinks
379 381
380 382 def samefile(path1, path2):
381 383 '''Returns whether path1 and path2 refer to the same file or directory.'''
382 384 res1 = _getfileinfo(path1)
383 385 res2 = _getfileinfo(path2)
384 386 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
385 387 and res1.nFileIndexHigh == res2.nFileIndexHigh
386 388 and res1.nFileIndexLow == res2.nFileIndexLow)
387 389
388 390 def samedevice(path1, path2):
389 391 '''Returns whether path1 and path2 are on the same device.'''
390 392 res1 = _getfileinfo(path1)
391 393 res2 = _getfileinfo(path2)
392 394 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
393 395
394 396 def peekpipe(pipe):
395 397 handle = msvcrt.get_osfhandle(pipe.fileno())
396 398 avail = _DWORD()
397 399
398 400 if not _kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(avail),
399 401 None):
400 402 err = _kernel32.GetLastError()
401 403 if err == _ERROR_BROKEN_PIPE:
402 404 return 0
403 405 raise ctypes.WinError(err)
404 406
405 407 return avail.value
406 408
407 409 def testpid(pid):
408 410 '''return True if pid is still running or unable to
409 411 determine, False otherwise'''
410 412 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
411 413 if h:
412 414 try:
413 415 status = _DWORD()
414 416 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
415 417 return status.value == _STILL_ACTIVE
416 418 finally:
417 419 _kernel32.CloseHandle(h)
418 420 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
419 421
420 422 def executablepath():
421 423 '''return full path of hg.exe'''
422 424 size = 600
423 425 buf = ctypes.create_string_buffer(size + 1)
424 426 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
425 427 if len == 0:
426 428 raise ctypes.WinError() # Note: WinError is a function
427 429 elif len == size:
428 430 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
429 431 return buf.value
430 432
431 433 def getvolumename(path):
432 434 """Get the mount point of the filesystem from a directory or file
433 435 (best-effort)
434 436
435 437 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
436 438 """
437 439 # realpath() calls GetFullPathName()
438 440 realpath = os.path.realpath(path)
439 441
440 442 size = len(realpath) + 1
441 443 buf = ctypes.create_string_buffer(size)
442 444
443 445 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
444 446 raise ctypes.WinError() # Note: WinError is a function
445 447
446 448 return buf.value
447 449
448 450 def getfstype(path):
449 451 """Get the filesystem type name from a directory or file (best-effort)
450 452
451 453 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
452 454 """
453 455 volume = getvolumename(path)
454 456
455 457 t = _kernel32.GetDriveTypeA(volume)
456 458
457 459 if t == _DRIVE_REMOTE:
458 460 return 'cifs'
459 461 elif t not in (_DRIVE_REMOVABLE, _DRIVE_FIXED, _DRIVE_CDROM,
460 462 _DRIVE_RAMDISK):
461 463 return None
462 464
463 size = 256
465 size = _MAX_PATH + 1
464 466 name = ctypes.create_string_buffer(size)
465 467
466 468 if not _kernel32.GetVolumeInformationA(volume, None, 0, None, None, None,
467 469 ctypes.byref(name), size):
468 470 raise ctypes.WinError() # Note: WinError is a function
469 471
470 472 return name.value
471 473
472 474 def getuser():
473 475 '''return name of current user'''
474 476 size = _DWORD(300)
475 477 buf = ctypes.create_string_buffer(size.value + 1)
476 478 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
477 479 raise ctypes.WinError()
478 480 return buf.value
479 481
480 482 _signalhandler = []
481 483
482 484 def setsignalhandler():
483 485 '''Register a termination handler for console events including
484 486 CTRL+C. python signal handlers do not work well with socket
485 487 operations.
486 488 '''
487 489 def handler(event):
488 490 _kernel32.ExitProcess(1)
489 491
490 492 if _signalhandler:
491 493 return # already registered
492 494 h = _SIGNAL_HANDLER(handler)
493 495 _signalhandler.append(h) # needed to prevent garbage collection
494 496 if not _kernel32.SetConsoleCtrlHandler(h, True):
495 497 raise ctypes.WinError()
496 498
497 499 def hidewindow():
498 500
499 501 def callback(hwnd, pid):
500 502 wpid = _DWORD()
501 503 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
502 504 if pid == wpid.value:
503 505 _user32.ShowWindow(hwnd, _SW_HIDE)
504 506 return False # stop enumerating windows
505 507 return True
506 508
507 509 pid = _kernel32.GetCurrentProcessId()
508 510 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
509 511
510 512 def termsize():
511 513 # cmd.exe does not handle CR like a unix console, the CR is
512 514 # counted in the line length. On 80 columns consoles, if 80
513 515 # characters are written, the following CR won't apply on the
514 516 # current line but on the new one. Keep room for it.
515 517 width = 80 - 1
516 518 height = 25
517 519 # Query stderr to avoid problems with redirections
518 520 screenbuf = _kernel32.GetStdHandle(
519 521 _STD_ERROR_HANDLE) # don't close the handle returned
520 522 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
521 523 return width, height
522 524 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
523 525 if not _kernel32.GetConsoleScreenBufferInfo(
524 526 screenbuf, ctypes.byref(csbi)):
525 527 return width, height
526 528 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
527 529 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
528 530 return width, height
529 531
530 532 def enablevtmode():
531 533 '''Enable virtual terminal mode for the associated console. Return True if
532 534 enabled, else False.'''
533 535
534 536 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
535 537
536 538 handle = _kernel32.GetStdHandle(_STD_OUTPUT_HANDLE) # don't close the handle
537 539 if handle == _INVALID_HANDLE_VALUE:
538 540 return False
539 541
540 542 mode = _DWORD(0)
541 543
542 544 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
543 545 return False
544 546
545 547 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
546 548 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
547 549
548 550 if not _kernel32.SetConsoleMode(handle, mode):
549 551 return False
550 552
551 553 return True
552 554
553 555 def spawndetached(args):
554 556 # No standard library function really spawns a fully detached
555 557 # process under win32 because they allocate pipes or other objects
556 558 # to handle standard streams communications. Passing these objects
557 559 # to the child process requires handle inheritance to be enabled
558 560 # which makes really detached processes impossible.
559 561 si = _STARTUPINFO()
560 562 si.cb = ctypes.sizeof(_STARTUPINFO)
561 563
562 564 pi = _PROCESS_INFORMATION()
563 565
564 566 env = ''
565 567 for k in encoding.environ:
566 568 env += "%s=%s\0" % (k, encoding.environ[k])
567 569 if not env:
568 570 env = '\0'
569 571 env += '\0'
570 572
571 573 args = subprocess.list2cmdline(args)
572 574
573 575 res = _kernel32.CreateProcessA(
574 576 None, args, None, None, False, _CREATE_NO_WINDOW,
575 577 env, pycompat.getcwd(), ctypes.byref(si), ctypes.byref(pi))
576 578 if not res:
577 579 raise ctypes.WinError()
578 580
579 581 return pi.dwProcessId
580 582
581 583 def unlink(f):
582 584 '''try to implement POSIX' unlink semantics on Windows'''
583 585
584 586 if os.path.isdir(f):
585 587 # use EPERM because it is POSIX prescribed value, even though
586 588 # unlink(2) on directories returns EISDIR on Linux
587 589 raise IOError(errno.EPERM,
588 590 "Unlinking directory not permitted: '%s'" % f)
589 591
590 592 # POSIX allows to unlink and rename open files. Windows has serious
591 593 # problems with doing that:
592 594 # - Calling os.unlink (or os.rename) on a file f fails if f or any
593 595 # hardlinked copy of f has been opened with Python's open(). There is no
594 596 # way such a file can be deleted or renamed on Windows (other than
595 597 # scheduling the delete or rename for the next reboot).
596 598 # - Calling os.unlink on a file that has been opened with Mercurial's
597 599 # posixfile (or comparable methods) will delay the actual deletion of
598 600 # the file for as long as the file is held open. The filename is blocked
599 601 # during that time and cannot be used for recreating a new file under
600 602 # that same name ("zombie file"). Directories containing such zombie files
601 603 # cannot be removed or moved.
602 604 # A file that has been opened with posixfile can be renamed, so we rename
603 605 # f to a random temporary name before calling os.unlink on it. This allows
604 606 # callers to recreate f immediately while having other readers do their
605 607 # implicit zombie filename blocking on a temporary name.
606 608
607 609 for tries in xrange(10):
608 610 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
609 611 try:
610 612 os.rename(f, temp) # raises OSError EEXIST if temp exists
611 613 break
612 614 except OSError as e:
613 615 if e.errno != errno.EEXIST:
614 616 raise
615 617 else:
616 618 raise IOError(errno.EEXIST, "No usable temporary filename found")
617 619
618 620 try:
619 621 os.unlink(temp)
620 622 except OSError:
621 623 # The unlink might have failed because the READONLY attribute may heave
622 624 # been set on the original file. Rename works fine with READONLY set,
623 625 # but not os.unlink. Reset all attributes and try again.
624 626 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
625 627 try:
626 628 os.unlink(temp)
627 629 except OSError:
628 630 # The unlink might have failed due to some very rude AV-Scanners.
629 631 # Leaking a tempfile is the lesser evil than aborting here and
630 632 # leaving some potentially serious inconsistencies.
631 633 pass
632 634
633 635 def makedir(path, notindexed):
634 636 os.mkdir(path)
635 637 if notindexed:
636 638 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now