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