/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/lock.py

  • Committer: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
 
18
17
"""Locking using OS file locks or file existence.
19
18
 
20
19
Note: This method of locking is generally deprecated in favour of LockDir, but
34
33
unlock() method.
35
34
"""
36
35
 
 
36
import contextlib
37
37
import errno
38
38
import os
39
39
import sys
40
40
import warnings
41
41
 
42
 
from bzrlib import (
 
42
from . import (
43
43
    debug,
44
44
    errors,
45
45
    osutils,
46
46
    trace,
47
47
    )
48
 
from bzrlib.hooks import HookPoint, Hooks
 
48
from .hooks import Hooks
 
49
from .i18n import gettext
49
50
 
50
51
 
51
52
class LockHooks(Hooks):
52
53
 
53
54
    def __init__(self):
54
 
        Hooks.__init__(self)
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))
 
55
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
 
56
        self.add_hook(
 
57
            'lock_acquired',
 
58
            "Called with a breezy.lock.LockResult when a physical lock is "
 
59
            "acquired.", (1, 8))
 
60
        self.add_hook(
 
61
            'lock_released',
 
62
            "Called with a breezy.lock.LockResult when a physical lock is "
 
63
            "released.", (1, 8))
 
64
        self.add_hook(
 
65
            'lock_broken',
 
66
            "Called with a breezy.lock.LockResult when a physical lock is "
 
67
            "broken.", (1, 15))
64
68
 
65
69
 
66
70
class Lock(object):
85
89
 
86
90
    def __repr__(self):
87
91
        return '%s(%s, %s)' % (self.__class__.__name__,
88
 
                             self.lock_url, self.details)
 
92
                               self.lock_url, self.details)
89
93
 
90
94
 
91
95
class LogicalLockResult(object):
94
98
    :ivar unlock: A callable which will unlock the lock.
95
99
    """
96
100
 
97
 
    def __init__(self, unlock):
 
101
    def __init__(self, unlock, token=None):
98
102
        self.unlock = unlock
 
103
        self.token = token
99
104
 
100
105
    def __repr__(self):
101
106
        return "LogicalLockResult(%s)" % (self.unlock)
102
107
 
 
108
    def __enter__(self):
 
109
        return self
 
110
 
 
111
    def __exit__(self, exc_type, exc_val, exc_tb):
 
112
        # If there was an error raised, prefer the original one
 
113
        try:
 
114
            self.unlock()
 
115
        except BaseException:
 
116
            if exc_type is None:
 
117
                raise
 
118
        return False
103
119
 
104
120
 
105
121
def cant_unlock_not_held(locked_object):
106
122
    """An attempt to unlock failed because the object was not locked.
107
123
 
108
 
    This provides a policy point from which we can generate either a warning 
109
 
    or an exception.
 
124
    This provides a policy point from which we can generate either a warning or
 
125
    an exception.
110
126
    """
111
127
    # This is typically masking some other error and called from a finally
112
128
    # block, so it's useful to have the option not to generate a new error
114
130
    # raise LockNotHeld.
115
131
    if 'unlock' in debug.debug_flags:
116
132
        warnings.warn("%r is already unlocked" % (locked_object,),
117
 
            stacklevel=3)
 
133
                      stacklevel=3)
118
134
    else:
119
135
        raise errors.LockNotHeld(locked_object)
120
136
 
125
141
except ImportError:
126
142
    have_fcntl = False
127
143
 
128
 
have_pywin32 = False
129
144
have_ctypes_win32 = False
130
145
if sys.platform == 'win32':
131
146
    import msvcrt
132
147
    try:
133
 
        import win32file, pywintypes, winerror
134
 
        have_pywin32 = True
135
 
    except ImportError:
136
 
        pass
137
 
 
138
 
    try:
139
148
        import ctypes
140
149
        have_ctypes_win32 = True
141
150
    except ImportError:
153
162
        try:
154
163
            self.f = open(self.filename, filemode)
155
164
            return self.f
156
 
        except IOError, e:
 
165
        except IOError as e:
157
166
            if e.errno in (errno.EACCES, errno.EPERM):
158
167
                raise errors.LockFailed(self.filename, str(e))
159
168
            if e.errno != errno.ENOENT:
171
180
            self.f.close()
172
181
            self.f = None
173
182
 
174
 
    def __del__(self):
175
 
        if self.f:
176
 
            from warnings import warn
177
 
            warn("lock on %r not released" % self.f)
178
 
            self.unlock()
179
 
 
180
183
    def unlock(self):
181
184
        raise NotImplementedError()
182
185
 
192
195
            fcntl.lockf(self.f, fcntl.LOCK_UN)
193
196
            self._clear_f()
194
197
 
195
 
 
196
198
    class _fcntl_WriteLock(_fcntl_FileLock):
197
199
 
198
200
        _open_locks = set()
221
223
                # LOCK_NB will cause IOError to be raised if we can't grab a
222
224
                # lock right away.
223
225
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
224
 
            except IOError, e:
 
226
            except IOError as e:
225
227
                if e.errno in (errno.EAGAIN, errno.EACCES):
226
228
                    # We couldn't grab the lock
227
229
                    self.unlock()
233
235
            _fcntl_WriteLock._open_locks.remove(self.filename)
234
236
            self._unlock()
235
237
 
236
 
 
237
238
    class _fcntl_ReadLock(_fcntl_FileLock):
238
239
 
239
240
        _open_locks = {}
256
257
                # LOCK_NB will cause IOError to be raised if we can't grab a
257
258
                # lock right away.
258
259
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
259
 
            except IOError, e:
 
260
            except IOError as e:
260
261
                # we should be more precise about whats a locking
261
262
                # error and whats a random-other error
262
263
                raise errors.LockContention(self.filename, e)
281
282
            """
282
283
            if self.filename in _fcntl_WriteLock._open_locks:
283
284
                raise AssertionError('file already locked: %r'
284
 
                    % (self.filename,))
 
285
                                     % (self.filename,))
285
286
            try:
286
287
                wlock = _fcntl_TemporaryWriteLock(self)
287
288
            except errors.LockError:
289
290
                return False, self
290
291
            return True, wlock
291
292
 
292
 
 
293
293
    class _fcntl_TemporaryWriteLock(_OSLock):
294
294
        """A token used when grabbing a temporary_write_lock.
295
295
 
309
309
 
310
310
            if self.filename in _fcntl_WriteLock._open_locks:
311
311
                raise AssertionError('file already locked: %r'
312
 
                    % (self.filename,))
 
312
                                     % (self.filename,))
313
313
 
314
314
            # See if we can open the file for writing. Another process might
315
315
            # have a read lock. We don't use self._open() because we don't want
317
317
            # done by _fcntl_ReadLock
318
318
            try:
319
319
                new_f = open(self.filename, 'rb+')
320
 
            except IOError, e:
 
320
            except IOError as e:
321
321
                if e.errno in (errno.EACCES, errno.EPERM):
322
322
                    raise errors.LockFailed(self.filename, str(e))
323
323
                raise
325
325
                # LOCK_NB will cause IOError to be raised if we can't grab a
326
326
                # lock right away.
327
327
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
328
 
            except IOError, e:
 
328
            except IOError as e:
329
329
                # TODO: Raise a more specific error based on the type of error
330
330
                raise errors.LockContention(self.filename, e)
331
331
            _fcntl_WriteLock._open_locks.add(self.filename)
334
334
 
335
335
        def restore_read_lock(self):
336
336
            """Restore the original ReadLock."""
337
 
            # For fcntl, since we never released the read lock, just release the
338
 
            # write lock, and return the original lock.
 
337
            # For fcntl, since we never released the read lock, just release
 
338
            # the write lock, and return the original lock.
339
339
            fcntl.lockf(self.f, fcntl.LOCK_UN)
340
340
            self._clear_f()
341
341
            _fcntl_WriteLock._open_locks.remove(self.filename)
344
344
            self._read_lock = None
345
345
            return read_lock
346
346
 
347
 
 
348
347
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
349
348
 
350
349
 
351
 
if have_pywin32 and sys.platform == 'win32':
352
 
    if os.path.supports_unicode_filenames:
353
 
        # for Windows NT/2K/XP/etc
354
 
        win32file_CreateFile = win32file.CreateFileW
355
 
    else:
356
 
        # for Windows 98
357
 
        win32file_CreateFile = win32file.CreateFile
358
 
 
359
 
    class _w32c_FileLock(_OSLock):
360
 
 
361
 
        def _open(self, filename, access, share, cflags, pymode):
362
 
            self.filename = osutils.realpath(filename)
363
 
            try:
364
 
                self._handle = win32file_CreateFile(filename, access, share,
365
 
                    None, win32file.OPEN_ALWAYS,
366
 
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
367
 
            except pywintypes.error, e:
368
 
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
369
 
                    raise errors.LockFailed(filename, e)
370
 
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
371
 
                    raise errors.LockContention(filename, e)
372
 
                raise
373
 
            fd = win32file._open_osfhandle(self._handle, cflags)
374
 
            self.f = os.fdopen(fd, pymode)
375
 
            return self.f
376
 
 
377
 
        def unlock(self):
378
 
            self._clear_f()
379
 
            self._handle = None
380
 
 
381
 
 
382
 
    class _w32c_ReadLock(_w32c_FileLock):
383
 
        def __init__(self, filename):
384
 
            super(_w32c_ReadLock, self).__init__()
385
 
            self._open(filename, win32file.GENERIC_READ,
386
 
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
387
 
 
388
 
        def temporary_write_lock(self):
389
 
            """Try to grab a write lock on the file.
390
 
 
391
 
            On platforms that support it, this will upgrade to a write lock
392
 
            without unlocking the file.
393
 
            Otherwise, this will release the read lock, and try to acquire a
394
 
            write lock.
395
 
 
396
 
            :return: A token which can be used to switch back to a read lock.
397
 
            """
398
 
            # I can't find a way to upgrade a read lock to a write lock without
399
 
            # unlocking first. So here, we do just that.
400
 
            self.unlock()
401
 
            try:
402
 
                wlock = _w32c_WriteLock(self.filename)
403
 
            except errors.LockError:
404
 
                return False, _w32c_ReadLock(self.filename)
405
 
            return True, wlock
406
 
 
407
 
 
408
 
    class _w32c_WriteLock(_w32c_FileLock):
409
 
        def __init__(self, filename):
410
 
            super(_w32c_WriteLock, self).__init__()
411
 
            self._open(filename,
412
 
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
413
 
                os.O_RDWR, "rb+")
414
 
 
415
 
        def restore_read_lock(self):
416
 
            """Restore the original ReadLock."""
417
 
            # For win32 we had to completely let go of the original lock, so we
418
 
            # just unlock and create a new read lock.
419
 
            self.unlock()
420
 
            return _w32c_ReadLock(self.filename)
421
 
 
422
 
 
423
 
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
424
 
 
425
 
 
426
350
if have_ctypes_win32:
427
 
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
428
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
429
 
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
430
 
    if os.path.supports_unicode_filenames:
431
 
        _function_name = "CreateFileW"
432
 
        LPTSTR = LPCWSTR
433
 
    else:
434
 
        _function_name = "CreateFileA"
435
 
        class LPTSTR(LPCSTR):
436
 
            def __new__(cls, obj):
437
 
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
351
    from ctypes.wintypes import DWORD, LPWSTR
 
352
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p  # used as NULL no need to declare
 
353
    HANDLE = ctypes.c_int  # rather than unsigned as in ctypes.wintypes
 
354
    _function_name = "CreateFileW"
438
355
 
439
356
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
440
357
    _CreateFile = ctypes.WINFUNCTYPE(
441
 
            HANDLE,                # return value
442
 
            LPTSTR,                # lpFileName
443
 
            DWORD,                 # dwDesiredAccess
444
 
            DWORD,                 # dwShareMode
445
 
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
446
 
            DWORD,                 # dwCreationDisposition
447
 
            DWORD,                 # dwFlagsAndAttributes
448
 
            HANDLE                 # hTemplateFile
 
358
        HANDLE,                # return value
 
359
        LPWSTR,                # lpFileName
 
360
        DWORD,                 # dwDesiredAccess
 
361
        DWORD,                 # dwShareMode
 
362
        LPSECURITY_ATTRIBUTES,  # lpSecurityAttributes
 
363
        DWORD,                 # dwCreationDisposition
 
364
        DWORD,                 # dwFlagsAndAttributes
 
365
        HANDLE                 # hTemplateFile
449
366
        )((_function_name, ctypes.windll.kernel32))
450
367
 
451
368
    INVALID_HANDLE_VALUE = -1
464
381
        def _open(self, filename, access, share, cflags, pymode):
465
382
            self.filename = osutils.realpath(filename)
466
383
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
467
 
                FILE_ATTRIBUTE_NORMAL, 0)
 
384
                                 FILE_ATTRIBUTE_NORMAL, 0)
468
385
            if handle in (INVALID_HANDLE_VALUE, 0):
469
386
                e = ctypes.WinError()
470
387
                if e.args[0] == ERROR_ACCESS_DENIED:
479
396
        def unlock(self):
480
397
            self._clear_f()
481
398
 
482
 
 
483
399
    class _ctypes_ReadLock(_ctypes_FileLock):
484
400
        def __init__(self, filename):
485
401
            super(_ctypes_ReadLock, self).__init__()
486
402
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
487
 
                "rb")
 
403
                       "rb")
488
404
 
489
405
        def temporary_write_lock(self):
490
406
            """Try to grab a write lock on the file.
509
425
        def __init__(self, filename):
510
426
            super(_ctypes_WriteLock, self).__init__()
511
427
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
512
 
                "rb+")
 
428
                       "rb+")
513
429
 
514
430
        def restore_read_lock(self):
515
431
            """Restore the original ReadLock."""
518
434
            self.unlock()
519
435
            return _ctypes_ReadLock(self.filename)
520
436
 
521
 
 
522
437
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
523
438
 
524
439
 
525
440
if len(_lock_classes) == 0:
526
441
    raise NotImplementedError(
527
 
        "We must have one of fcntl, pywin32, or ctypes available"
 
442
        "We must have one of fcntl or ctypes available"
528
443
        " to support OS locking."
529
444
        )
530
445
 
541
456
    locked the same way), and -Drelock is set, then this will trace.note a
542
457
    message about it.
543
458
    """
544
 
    
 
459
 
545
460
    _prev_lock = None
546
461
 
547
462
    def _note_lock(self, lock_type):
550
465
                type_name = 'read'
551
466
            else:
552
467
                type_name = 'write'
553
 
            trace.note('%r was %s locked again', self, type_name)
 
468
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
554
469
        self._prev_lock = lock_type
555
470
 
 
471
 
 
472
@contextlib.contextmanager
 
473
def write_locked(lockable):
 
474
    lockable.lock_write()
 
475
    try:
 
476
        yield lockable
 
477
    finally:
 
478
        lockable.unlock()