##// END OF EJS Templates
win32: quietly ignore missing CreateHardLinkA for Wine
Matt Mackall -
r15095:ec222a29 stable
parent child Browse files
Show More
@@ -1,449 +1,452 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 import encoding
9 9 import ctypes, errno, os, struct, subprocess, random
10 10
11 11 _kernel32 = ctypes.windll.kernel32
12 12 _advapi32 = ctypes.windll.advapi32
13 13 _user32 = ctypes.windll.user32
14 14
15 15 _BOOL = ctypes.c_long
16 16 _WORD = ctypes.c_ushort
17 17 _DWORD = ctypes.c_ulong
18 18 _UINT = ctypes.c_uint
19 19 _LONG = ctypes.c_long
20 20 _LPCSTR = _LPSTR = ctypes.c_char_p
21 21 _HANDLE = ctypes.c_void_p
22 22 _HWND = _HANDLE
23 23
24 24 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
25 25
26 26 # GetLastError
27 27 _ERROR_SUCCESS = 0
28 28 _ERROR_INVALID_PARAMETER = 87
29 29 _ERROR_INSUFFICIENT_BUFFER = 122
30 30
31 31 # WPARAM is defined as UINT_PTR (unsigned type)
32 32 # LPARAM is defined as LONG_PTR (signed type)
33 33 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
34 34 _WPARAM = ctypes.c_ulong
35 35 _LPARAM = ctypes.c_long
36 36 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
37 37 _WPARAM = ctypes.c_ulonglong
38 38 _LPARAM = ctypes.c_longlong
39 39
40 40 class _FILETIME(ctypes.Structure):
41 41 _fields_ = [('dwLowDateTime', _DWORD),
42 42 ('dwHighDateTime', _DWORD)]
43 43
44 44 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
45 45 _fields_ = [('dwFileAttributes', _DWORD),
46 46 ('ftCreationTime', _FILETIME),
47 47 ('ftLastAccessTime', _FILETIME),
48 48 ('ftLastWriteTime', _FILETIME),
49 49 ('dwVolumeSerialNumber', _DWORD),
50 50 ('nFileSizeHigh', _DWORD),
51 51 ('nFileSizeLow', _DWORD),
52 52 ('nNumberOfLinks', _DWORD),
53 53 ('nFileIndexHigh', _DWORD),
54 54 ('nFileIndexLow', _DWORD)]
55 55
56 56 # CreateFile
57 57 _FILE_SHARE_READ = 0x00000001
58 58 _FILE_SHARE_WRITE = 0x00000002
59 59 _FILE_SHARE_DELETE = 0x00000004
60 60
61 61 _OPEN_EXISTING = 3
62 62
63 63 # SetFileAttributes
64 64 _FILE_ATTRIBUTE_NORMAL = 0x80
65 65 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
66 66
67 67 # Process Security and Access Rights
68 68 _PROCESS_QUERY_INFORMATION = 0x0400
69 69
70 70 # GetExitCodeProcess
71 71 _STILL_ACTIVE = 259
72 72
73 73 # registry
74 74 _HKEY_CURRENT_USER = 0x80000001L
75 75 _HKEY_LOCAL_MACHINE = 0x80000002L
76 76 _KEY_READ = 0x20019
77 77 _REG_SZ = 1
78 78 _REG_DWORD = 4
79 79
80 80 class _STARTUPINFO(ctypes.Structure):
81 81 _fields_ = [('cb', _DWORD),
82 82 ('lpReserved', _LPSTR),
83 83 ('lpDesktop', _LPSTR),
84 84 ('lpTitle', _LPSTR),
85 85 ('dwX', _DWORD),
86 86 ('dwY', _DWORD),
87 87 ('dwXSize', _DWORD),
88 88 ('dwYSize', _DWORD),
89 89 ('dwXCountChars', _DWORD),
90 90 ('dwYCountChars', _DWORD),
91 91 ('dwFillAttribute', _DWORD),
92 92 ('dwFlags', _DWORD),
93 93 ('wShowWindow', _WORD),
94 94 ('cbReserved2', _WORD),
95 95 ('lpReserved2', ctypes.c_char_p),
96 96 ('hStdInput', _HANDLE),
97 97 ('hStdOutput', _HANDLE),
98 98 ('hStdError', _HANDLE)]
99 99
100 100 class _PROCESS_INFORMATION(ctypes.Structure):
101 101 _fields_ = [('hProcess', _HANDLE),
102 102 ('hThread', _HANDLE),
103 103 ('dwProcessId', _DWORD),
104 104 ('dwThreadId', _DWORD)]
105 105
106 106 _DETACHED_PROCESS = 0x00000008
107 107 _STARTF_USESHOWWINDOW = 0x00000001
108 108 _SW_HIDE = 0
109 109
110 110 class _COORD(ctypes.Structure):
111 111 _fields_ = [('X', ctypes.c_short),
112 112 ('Y', ctypes.c_short)]
113 113
114 114 class _SMALL_RECT(ctypes.Structure):
115 115 _fields_ = [('Left', ctypes.c_short),
116 116 ('Top', ctypes.c_short),
117 117 ('Right', ctypes.c_short),
118 118 ('Bottom', ctypes.c_short)]
119 119
120 120 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
121 121 _fields_ = [('dwSize', _COORD),
122 122 ('dwCursorPosition', _COORD),
123 123 ('wAttributes', _WORD),
124 124 ('srWindow', _SMALL_RECT),
125 125 ('dwMaximumWindowSize', _COORD)]
126 126
127 127 _STD_ERROR_HANDLE = _DWORD(-12).value
128 128
129 129 # types of parameters of C functions used (required by pypy)
130 130
131 131 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
132 132 _DWORD, _DWORD, _HANDLE]
133 133 _kernel32.CreateFileA.restype = _HANDLE
134 134
135 135 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
136 136 _kernel32.GetFileInformationByHandle.restype = _BOOL
137 137
138 138 _kernel32.CloseHandle.argtypes = [_HANDLE]
139 139 _kernel32.CloseHandle.restype = _BOOL
140 140
141 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
142 _kernel32.CreateHardLinkA.restype = _BOOL
141 try:
142 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
143 _kernel32.CreateHardLinkA.restype = _BOOL
144 except AttributeError:
145 pass
143 146
144 147 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
145 148 _kernel32.SetFileAttributesA.restype = _BOOL
146 149
147 150 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
148 151 _kernel32.OpenProcess.restype = _HANDLE
149 152
150 153 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
151 154 _kernel32.GetExitCodeProcess.restype = _BOOL
152 155
153 156 _kernel32.GetLastError.argtypes = []
154 157 _kernel32.GetLastError.restype = _DWORD
155 158
156 159 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
157 160 _kernel32.GetModuleFileNameA.restype = _DWORD
158 161
159 162 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
160 163 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
161 164 ctypes.c_void_p]
162 165 _kernel32.CreateProcessA.restype = _BOOL
163 166
164 167 _kernel32.ExitProcess.argtypes = [_UINT]
165 168 _kernel32.ExitProcess.restype = None
166 169
167 170 _kernel32.GetCurrentProcessId.argtypes = []
168 171 _kernel32.GetCurrentProcessId.restype = _DWORD
169 172
170 173 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
171 174 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
172 175 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
173 176
174 177 _kernel32.GetStdHandle.argtypes = [_DWORD]
175 178 _kernel32.GetStdHandle.restype = _HANDLE
176 179
177 180 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
178 181 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
179 182
180 183 _advapi32.RegOpenKeyExA.argtypes = [_HANDLE, _LPCSTR, _DWORD, _DWORD,
181 184 ctypes.c_void_p]
182 185 _advapi32.RegOpenKeyExA.restype = _LONG
183 186
184 187 _advapi32.RegQueryValueExA.argtypes = [_HANDLE, _LPCSTR, ctypes.c_void_p,
185 188 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
186 189 _advapi32.RegQueryValueExA.restype = _LONG
187 190
188 191 _advapi32.RegCloseKey.argtypes = [_HANDLE]
189 192 _advapi32.RegCloseKey.restype = _LONG
190 193
191 194 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
192 195 _advapi32.GetUserNameA.restype = _BOOL
193 196
194 197 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
195 198 _user32.GetWindowThreadProcessId.restype = _DWORD
196 199
197 200 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
198 201 _user32.ShowWindow.restype = _BOOL
199 202
200 203 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
201 204 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
202 205 _user32.EnumWindows.restype = _BOOL
203 206
204 207 def _raiseoserror(name):
205 208 err = ctypes.WinError()
206 209 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
207 210
208 211 def _getfileinfo(name):
209 212 fh = _kernel32.CreateFileA(name, 0,
210 213 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
211 214 None, _OPEN_EXISTING, 0, None)
212 215 if fh == _INVALID_HANDLE_VALUE:
213 216 _raiseoserror(name)
214 217 try:
215 218 fi = _BY_HANDLE_FILE_INFORMATION()
216 219 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
217 220 _raiseoserror(name)
218 221 return fi
219 222 finally:
220 223 _kernel32.CloseHandle(fh)
221 224
222 225 def oslink(src, dst):
223 226 try:
224 227 if not _kernel32.CreateHardLinkA(dst, src, None):
225 228 _raiseoserror(src)
226 229 except AttributeError: # Wine doesn't support this function
227 230 _raiseoserror(src)
228 231
229 232 def nlinks(name):
230 233 '''return number of hardlinks for the given file'''
231 234 return _getfileinfo(name).nNumberOfLinks
232 235
233 236 def samefile(fpath1, fpath2):
234 237 '''Returns whether fpath1 and fpath2 refer to the same file. This is only
235 238 guaranteed to work for files, not directories.'''
236 239 res1 = _getfileinfo(fpath1)
237 240 res2 = _getfileinfo(fpath2)
238 241 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
239 242 and res1.nFileIndexHigh == res2.nFileIndexHigh
240 243 and res1.nFileIndexLow == res2.nFileIndexLow)
241 244
242 245 def samedevice(fpath1, fpath2):
243 246 '''Returns whether fpath1 and fpath2 are on the same device. This is only
244 247 guaranteed to work for files, not directories.'''
245 248 res1 = _getfileinfo(fpath1)
246 249 res2 = _getfileinfo(fpath2)
247 250 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
248 251
249 252 def testpid(pid):
250 253 '''return True if pid is still running or unable to
251 254 determine, False otherwise'''
252 255 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
253 256 if h:
254 257 try:
255 258 status = _DWORD()
256 259 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
257 260 return status.value == _STILL_ACTIVE
258 261 finally:
259 262 _kernel32.CloseHandle(h)
260 263 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
261 264
262 265 def lookupreg(key, valname=None, scope=None):
263 266 ''' Look up a key/value name in the Windows registry.
264 267
265 268 valname: value name. If unspecified, the default value for the key
266 269 is used.
267 270 scope: optionally specify scope for registry lookup, this can be
268 271 a sequence of scopes to look up in order. Default (CURRENT_USER,
269 272 LOCAL_MACHINE).
270 273 '''
271 274 byref = ctypes.byref
272 275 if scope is None:
273 276 scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
274 277 elif not isinstance(scope, (list, tuple)):
275 278 scope = (scope,)
276 279 for s in scope:
277 280 kh = _HANDLE()
278 281 res = _advapi32.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
279 282 if res != _ERROR_SUCCESS:
280 283 continue
281 284 try:
282 285 size = _DWORD(600)
283 286 type = _DWORD()
284 287 buf = ctypes.create_string_buffer(size.value + 1)
285 288 res = _advapi32.RegQueryValueExA(kh.value, valname, None,
286 289 byref(type), buf, byref(size))
287 290 if res != _ERROR_SUCCESS:
288 291 continue
289 292 if type.value == _REG_SZ:
290 293 # never let a Unicode string escape into the wild
291 294 return encoding.tolocal(buf.value.encode('UTF-8'))
292 295 elif type.value == _REG_DWORD:
293 296 fmt = '<L'
294 297 s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
295 298 return struct.unpack(fmt, s)[0]
296 299 finally:
297 300 _advapi32.RegCloseKey(kh.value)
298 301
299 302 def executablepath():
300 303 '''return full path of hg.exe'''
301 304 size = 600
302 305 buf = ctypes.create_string_buffer(size + 1)
303 306 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
304 307 if len == 0:
305 308 raise ctypes.WinError()
306 309 elif len == size:
307 310 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
308 311 return buf.value
309 312
310 313 def getuser():
311 314 '''return name of current user'''
312 315 size = _DWORD(300)
313 316 buf = ctypes.create_string_buffer(size.value + 1)
314 317 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
315 318 raise ctypes.WinError()
316 319 return buf.value
317 320
318 321 _signalhandler = []
319 322
320 323 def setsignalhandler():
321 324 '''Register a termination handler for console events including
322 325 CTRL+C. python signal handlers do not work well with socket
323 326 operations.
324 327 '''
325 328 def handler(event):
326 329 _kernel32.ExitProcess(1)
327 330
328 331 if _signalhandler:
329 332 return # already registered
330 333 h = _SIGNAL_HANDLER(handler)
331 334 _signalhandler.append(h) # needed to prevent garbage collection
332 335 if not _kernel32.SetConsoleCtrlHandler(h, True):
333 336 raise ctypes.WinError()
334 337
335 338 def hidewindow():
336 339
337 340 def callback(hwnd, pid):
338 341 wpid = _DWORD()
339 342 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
340 343 if pid == wpid.value:
341 344 _user32.ShowWindow(hwnd, _SW_HIDE)
342 345 return False # stop enumerating windows
343 346 return True
344 347
345 348 pid = _kernel32.GetCurrentProcessId()
346 349 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
347 350
348 351 def termwidth():
349 352 # cmd.exe does not handle CR like a unix console, the CR is
350 353 # counted in the line length. On 80 columns consoles, if 80
351 354 # characters are written, the following CR won't apply on the
352 355 # current line but on the new one. Keep room for it.
353 356 width = 79
354 357 # Query stderr to avoid problems with redirections
355 358 screenbuf = _kernel32.GetStdHandle(
356 359 _STD_ERROR_HANDLE) # don't close the handle returned
357 360 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
358 361 return width
359 362 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
360 363 if not _kernel32.GetConsoleScreenBufferInfo(
361 364 screenbuf, ctypes.byref(csbi)):
362 365 return width
363 366 width = csbi.srWindow.Right - csbi.srWindow.Left
364 367 return width
365 368
366 369 def spawndetached(args):
367 370 # No standard library function really spawns a fully detached
368 371 # process under win32 because they allocate pipes or other objects
369 372 # to handle standard streams communications. Passing these objects
370 373 # to the child process requires handle inheritance to be enabled
371 374 # which makes really detached processes impossible.
372 375 si = _STARTUPINFO()
373 376 si.cb = ctypes.sizeof(_STARTUPINFO)
374 377 si.dwFlags = _STARTF_USESHOWWINDOW
375 378 si.wShowWindow = _SW_HIDE
376 379
377 380 pi = _PROCESS_INFORMATION()
378 381
379 382 env = ''
380 383 for k in os.environ:
381 384 env += "%s=%s\0" % (k, os.environ[k])
382 385 if not env:
383 386 env = '\0'
384 387 env += '\0'
385 388
386 389 args = subprocess.list2cmdline(args)
387 390 # Not running the command in shell mode makes python26 hang when
388 391 # writing to hgweb output socket.
389 392 comspec = os.environ.get("COMSPEC", "cmd.exe")
390 393 args = comspec + " /c " + args
391 394
392 395 res = _kernel32.CreateProcessA(
393 396 None, args, None, None, False, _DETACHED_PROCESS,
394 397 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
395 398 if not res:
396 399 raise ctypes.WinError()
397 400
398 401 return pi.dwProcessId
399 402
400 403 def unlink(f):
401 404 '''try to implement POSIX' unlink semantics on Windows'''
402 405
403 406 # POSIX allows to unlink and rename open files. Windows has serious
404 407 # problems with doing that:
405 408 # - Calling os.unlink (or os.rename) on a file f fails if f or any
406 409 # hardlinked copy of f has been opened with Python's open(). There is no
407 410 # way such a file can be deleted or renamed on Windows (other than
408 411 # scheduling the delete or rename for the next reboot).
409 412 # - Calling os.unlink on a file that has been opened with Mercurial's
410 413 # posixfile (or comparable methods) will delay the actual deletion of
411 414 # the file for as long as the file is held open. The filename is blocked
412 415 # during that time and cannot be used for recreating a new file under
413 416 # that same name ("zombie file"). Directories containing such zombie files
414 417 # cannot be removed or moved.
415 418 # A file that has been opened with posixfile can be renamed, so we rename
416 419 # f to a random temporary name before calling os.unlink on it. This allows
417 420 # callers to recreate f immediately while having other readers do their
418 421 # implicit zombie filename blocking on a temporary name.
419 422
420 423 for tries in xrange(10):
421 424 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
422 425 try:
423 426 os.rename(f, temp) # raises OSError EEXIST if temp exists
424 427 break
425 428 except OSError, e:
426 429 if e.errno != errno.EEXIST:
427 430 raise
428 431 else:
429 432 raise IOError, (errno.EEXIST, "No usable temporary filename found")
430 433
431 434 try:
432 435 os.unlink(temp)
433 436 except OSError:
434 437 # The unlink might have failed because the READONLY attribute may heave
435 438 # been set on the original file. Rename works fine with READONLY set,
436 439 # but not os.unlink. Reset all attributes and try again.
437 440 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
438 441 try:
439 442 os.unlink(temp)
440 443 except OSError:
441 444 # The unlink might have failed due to some very rude AV-Scanners.
442 445 # Leaking a tempfile is the lesser evil than aborting here and
443 446 # leaving some potentially serious inconsistencies.
444 447 pass
445 448
446 449 def makedir(path, notindexed):
447 450 os.mkdir(path)
448 451 if notindexed:
449 452 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now