Show More
@@ -13,6 +13,7 b' posixfile = open' | |||||
13 | nulldev = '/dev/null' |
|
13 | nulldev = '/dev/null' | |
14 | normpath = os.path.normpath |
|
14 | normpath = os.path.normpath | |
15 | samestat = os.path.samestat |
|
15 | samestat = os.path.samestat | |
|
16 | unlink = os.unlink | |||
16 | rename = os.rename |
|
17 | rename = os.rename | |
17 | expandglobs = False |
|
18 | expandglobs = False | |
18 |
|
19 |
@@ -285,6 +285,46 b' def unlinkpath(f):' | |||||
285 | except OSError: |
|
285 | except OSError: | |
286 | pass |
|
286 | pass | |
287 |
|
287 | |||
|
288 | def unlink(f): | |||
|
289 | '''try to implement POSIX' unlink semantics on Windows''' | |||
|
290 | ||||
|
291 | # POSIX allows to unlink and rename open files. Windows has serious | |||
|
292 | # problems with doing that: | |||
|
293 | # - Calling os.unlink (or os.rename) on a file f fails if f or any | |||
|
294 | # hardlinked copy of f has been opened with Python's open(). There is no | |||
|
295 | # way such a file can be deleted or renamed on Windows (other than | |||
|
296 | # scheduling the delete or rename for the next reboot). | |||
|
297 | # - Calling os.unlink on a file that has been opened with Mercurial's | |||
|
298 | # posixfile (or comparable methods) will delay the actual deletion of | |||
|
299 | # the file for as long as the file is held open. The filename is blocked | |||
|
300 | # during that time and cannot be used for recreating a new file under | |||
|
301 | # that same name ("zombie file"). Directories containing such zombie files | |||
|
302 | # cannot be removed or moved. | |||
|
303 | # A file that has been opened with posixfile can be renamed, so we rename | |||
|
304 | # f to a random temporary name before calling os.unlink on it. This allows | |||
|
305 | # callers to recreate f immediately while having other readers do their | |||
|
306 | # implicit zombie filename blocking on a temporary name. | |||
|
307 | ||||
|
308 | for tries in xrange(10): | |||
|
309 | temp = '%s-%08x' % (f, random.randint(0, 0xffffffff)) | |||
|
310 | try: | |||
|
311 | os.rename(f, temp) # raises OSError EEXIST if temp exists | |||
|
312 | break | |||
|
313 | except OSError, e: | |||
|
314 | if e.errno != errno.EEXIST: | |||
|
315 | raise | |||
|
316 | else: | |||
|
317 | raise IOError, (errno.EEXIST, "No usable temporary filename found") | |||
|
318 | ||||
|
319 | try: | |||
|
320 | os.unlink(temp) | |||
|
321 | except: | |||
|
322 | # Some very rude AV-scanners on Windows may cause this unlink to fail. | |||
|
323 | # Not aborting here just leaks the temp file, whereas aborting at this | |||
|
324 | # point may leave serious inconsistencies. Ideally, we would notify | |||
|
325 | # the user in this case here. | |||
|
326 | pass | |||
|
327 | ||||
288 | def rename(src, dst): |
|
328 | def rename(src, dst): | |
289 | '''atomically rename file src to dst, replacing dst if it exists''' |
|
329 | '''atomically rename file src to dst, replacing dst if it exists''' | |
290 | try: |
|
330 | try: | |
@@ -292,35 +332,7 b' def rename(src, dst):' | |||||
292 | except OSError, e: |
|
332 | except OSError, e: | |
293 | if e.errno != errno.EEXIST: |
|
333 | if e.errno != errno.EEXIST: | |
294 | raise |
|
334 | raise | |
295 |
|
335 | unlink(dst) | ||
296 | # On windows, rename to existing file is not allowed, so we |
|
|||
297 | # must delete destination first. But if a file is open, unlink |
|
|||
298 | # schedules it for delete but does not delete it. Rename |
|
|||
299 | # happens immediately even for open files, so we rename |
|
|||
300 | # destination to a temporary name, then delete that. Then |
|
|||
301 | # rename is safe to do. |
|
|||
302 | # The temporary name is chosen at random to avoid the situation |
|
|||
303 | # where a file is left lying around from a previous aborted run. |
|
|||
304 |
|
||||
305 | for tries in xrange(10): |
|
|||
306 | temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff)) |
|
|||
307 | try: |
|
|||
308 | os.rename(dst, temp) # raises OSError EEXIST if temp exists |
|
|||
309 | break |
|
|||
310 | except OSError, e: |
|
|||
311 | if e.errno != errno.EEXIST: |
|
|||
312 | raise |
|
|||
313 | else: |
|
|||
314 | raise IOError, (errno.EEXIST, "No usable temporary filename found") |
|
|||
315 |
|
||||
316 | try: |
|
|||
317 | os.unlink(temp) |
|
|||
318 | except: |
|
|||
319 | # Some rude AV-scanners on Windows may cause the unlink to |
|
|||
320 | # fail. Not aborting here just leaks the temp file, whereas |
|
|||
321 | # aborting at this point may leave serious inconsistencies. |
|
|||
322 | # Ideally, we would notify the user here. |
|
|||
323 | pass |
|
|||
324 | os.rename(src, dst) |
|
336 | os.rename(src, dst) | |
325 |
|
337 | |||
326 | def spawndetached(args): |
|
338 | def spawndetached(args): |
General Comments 0
You need to be logged in to leave comments.
Login now