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