42
40
from bzrlib import (
 
48
 
from bzrlib.hooks import HookPoint, Hooks
 
51
 
class LockHooks(Hooks):
 
55
 
        self.create_hook(HookPoint('lock_acquired',
 
56
 
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
57
 
            "acquired.", (1, 8), None))
 
58
 
        self.create_hook(HookPoint('lock_released',
 
59
 
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
60
 
            "released.", (1, 8), None))
 
61
 
        self.create_hook(HookPoint('lock_broken',
 
62
 
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
63
 
            "broken.", (1, 15), None))
 
67
 
    """Base class for locks.
 
69
 
    :cvar hooks: Hook dictionary for operations on locks.
 
75
 
class LockResult(object):
 
76
 
    """Result of an operation on a lock; passed to a hook"""
 
78
 
    def __init__(self, lock_url, details=None):
 
79
 
        """Create a lock result for lock with optional details about the lock."""
 
80
 
        self.lock_url = lock_url
 
81
 
        self.details = details
 
83
 
    def __eq__(self, other):
 
84
 
        return self.lock_url == other.lock_url and self.details == other.details
 
87
 
        return '%s(%s, %s)' % (self.__class__.__name__,
 
88
 
                             self.lock_url, self.details)
 
91
 
def cant_unlock_not_held(locked_object):
 
92
 
    """An attempt to unlock failed because the object was not locked.
 
94
 
    This provides a policy point from which we can generate either a warning 
 
97
 
    # This is typically masking some other error and called from a finally
 
98
 
    # block, so it's useful to have the option not to generate a new error
 
99
 
    # here.  You can use -Werror to make it fatal.  It should possibly also
 
101
 
    if 'unlock' in debug.debug_flags:
 
102
 
        warnings.warn("%r is already unlocked" % (locked_object,),
 
105
 
        raise errors.LockNotHeld(locked_object)
 
115
 
have_ctypes_win32 = False
 
116
 
if sys.platform == 'win32':
 
119
 
        import win32file, pywintypes, winerror
 
126
 
        have_ctypes_win32 = True
 
131
47
class _OSLock(object):
 
 
337
255
if have_pywin32 and sys.platform == 'win32':
 
338
 
    if os.path.supports_unicode_filenames:
 
339
 
        # for Windows NT/2K/XP/etc
 
340
 
        win32file_CreateFile = win32file.CreateFileW
 
343
 
        win32file_CreateFile = win32file.CreateFile
 
 
256
    LOCK_SH = 0 # the default
 
 
257
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
 
 
258
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
 
345
261
    class _w32c_FileLock(_OSLock):
 
347
 
        def _open(self, filename, access, share, cflags, pymode):
 
348
 
            self.filename = osutils.realpath(filename)
 
 
263
        def _lock(self, filename, openmode, lockmode):
 
 
264
            self._open(filename, openmode)
 
 
266
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
 
 
267
            overlapped = pywintypes.OVERLAPPED()
 
350
 
                self._handle = win32file_CreateFile(filename, access, share,
 
351
 
                    None, win32file.OPEN_ALWAYS,
 
352
 
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
 
 
269
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
 
353
271
            except pywintypes.error, e:
 
354
 
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
355
 
                    raise errors.LockFailed(filename, e)
 
356
 
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
357
 
                    raise errors.LockContention(filename, e)
 
 
273
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
 
 
274
                    raise errors.LockContention(filename)
 
 
275
                ## import pdb; pdb.set_trace()
 
359
 
            fd = win32file._open_osfhandle(self._handle, cflags)
 
360
 
            self.f = os.fdopen(fd, pymode)
 
 
279
                raise errors.LockContention(e)
 
363
281
        def unlock(self):
 
 
282
            overlapped = pywintypes.OVERLAPPED()
 
 
284
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
 
 
287
                raise errors.LockContention(e)
 
368
290
    class _w32c_ReadLock(_w32c_FileLock):
 
369
291
        def __init__(self, filename):
 
370
292
            super(_w32c_ReadLock, self).__init__()
 
371
 
            self._open(filename, win32file.GENERIC_READ,
 
372
 
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
 
293
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
374
295
        def temporary_write_lock(self):
 
375
296
            """Try to grab a write lock on the file.
 
 
409
328
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
 
412
 
if have_ctypes_win32:
 
413
 
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
414
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
415
 
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
416
 
    if os.path.supports_unicode_filenames:
 
417
 
        _function_name = "CreateFileW"
 
420
 
        _function_name = "CreateFileA"
 
421
 
        class LPTSTR(LPCSTR):
 
422
 
            def __new__(cls, obj):
 
423
 
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
425
 
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
 
426
 
    _CreateFile = ctypes.WINFUNCTYPE(
 
427
 
            HANDLE,                # return value
 
429
 
            DWORD,                 # dwDesiredAccess
 
431
 
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
432
 
            DWORD,                 # dwCreationDisposition
 
433
 
            DWORD,                 # dwFlagsAndAttributes
 
434
 
            HANDLE                 # hTemplateFile
 
435
 
        )((_function_name, ctypes.windll.kernel32))
 
437
 
    INVALID_HANDLE_VALUE = -1
 
439
 
    GENERIC_READ = 0x80000000
 
440
 
    GENERIC_WRITE = 0x40000000
 
443
 
    FILE_ATTRIBUTE_NORMAL = 128
 
445
 
    ERROR_ACCESS_DENIED = 5
 
446
 
    ERROR_SHARING_VIOLATION = 32
 
 
331
if have_ctypes and sys.platform == 'win32':
 
 
332
    # These constants were copied from the win32con.py module.
 
 
333
    LOCKFILE_FAIL_IMMEDIATELY = 1
 
 
334
    LOCKFILE_EXCLUSIVE_LOCK = 2
 
 
335
    # Constant taken from winerror.py module
 
 
336
    ERROR_LOCK_VIOLATION = 33
 
 
339
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
 
 
340
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
 
 
341
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
 
 
342
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
 
 
343
    _GetLastError = ctypes.windll.kernel32.GetLastError
 
 
345
    ### Define the OVERLAPPED structure.
 
 
346
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
 
 
347
    # typedef struct _OVERLAPPED {
 
 
348
    #   ULONG_PTR Internal;
 
 
349
    #   ULONG_PTR InternalHigh;
 
 
360
    class _inner_struct(ctypes.Structure):
 
 
361
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
 
 
362
                    ('OffsetHigh', ctypes.c_uint), # DWORD
 
 
365
    class _inner_union(ctypes.Union):
 
 
366
        _fields_  = [('anon_struct', _inner_struct), # struct
 
 
367
                     ('Pointer', ctypes.c_void_p), # PVOID
 
 
370
    class OVERLAPPED(ctypes.Structure):
 
 
371
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
 
 
372
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
 
 
373
                    ('_inner_union', _inner_union),
 
 
374
                    ('hEvent', ctypes.c_void_p), # HANDLE
 
448
377
    class _ctypes_FileLock(_OSLock):
 
450
 
        def _open(self, filename, access, share, cflags, pymode):
 
451
 
            self.filename = osutils.realpath(filename)
 
452
 
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
 
453
 
                FILE_ATTRIBUTE_NORMAL, 0)
 
454
 
            if handle in (INVALID_HANDLE_VALUE, 0):
 
455
 
                e = ctypes.WinError()
 
456
 
                if e.args[0] == ERROR_ACCESS_DENIED:
 
457
 
                    raise errors.LockFailed(filename, e)
 
458
 
                if e.args[0] == ERROR_SHARING_VIOLATION:
 
459
 
                    raise errors.LockContention(filename, e)
 
461
 
            fd = msvcrt.open_osfhandle(handle, cflags)
 
462
 
            self.f = os.fdopen(fd, pymode)
 
 
379
        def _lock(self, filename, openmode, lockmode):
 
 
380
            self._open(filename, openmode)
 
 
382
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
 
 
383
            overlapped = OVERLAPPED()
 
 
384
            result = _LockFileEx(self.hfile, # HANDLE hFile
 
 
385
                                 lockmode,   # DWORD dwFlags
 
 
386
                                 0,          # DWORD dwReserved
 
 
387
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
 
 
388
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
 
 
389
                                 ctypes.byref(overlapped), # lpOverlapped
 
 
393
                last_err = _GetLastError()
 
 
394
                if last_err in (ERROR_LOCK_VIOLATION,):
 
 
395
                    raise errors.LockContention(filename)
 
 
396
                raise errors.LockContention('Unknown locking error: %s'
 
465
399
        def unlock(self):
 
 
400
            overlapped = OVERLAPPED()
 
 
401
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
 
 
402
                                   0,          # DWORD dwReserved
 
 
403
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
 
 
404
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
 
 
405
                                   ctypes.byref(overlapped), # lpOverlapped
 
 
410
                last_err = _GetLastError()
 
 
411
                raise errors.LockContention('Unknown unlocking error: %s'
 
469
415
    class _ctypes_ReadLock(_ctypes_FileLock):
 
470
416
        def __init__(self, filename):
 
471
417
            super(_ctypes_ReadLock, self).__init__()
 
472
 
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
 
418
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
475
420
        def temporary_write_lock(self):
 
476
421
            """Try to grab a write lock on the file.