/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)
 
93
 
 
94
 
 
95
class LogicalLockResult(object):
 
96
    """The result of a lock_read/lock_write/lock_tree_write call on lockables.
 
97
 
 
98
    :ivar unlock: A callable which will unlock the lock.
 
99
    """
 
100
 
 
101
    def __init__(self, unlock, token=None):
 
102
        self.unlock = unlock
 
103
        self.token = token
 
104
 
 
105
    def __repr__(self):
 
106
        return "LogicalLockResult(%s)" % (self.unlock)
 
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
89
119
 
90
120
 
91
121
def cant_unlock_not_held(locked_object):
92
122
    """An attempt to unlock failed because the object was not locked.
93
123
 
94
 
    This provides a policy point from which we can generate either a warning 
95
 
    or an exception.
 
124
    This provides a policy point from which we can generate either a warning or
 
125
    an exception.
96
126
    """
97
127
    # This is typically masking some other error and called from a finally
98
128
    # block, so it's useful to have the option not to generate a new error
100
130
    # raise LockNotHeld.
101
131
    if 'unlock' in debug.debug_flags:
102
132
        warnings.warn("%r is already unlocked" % (locked_object,),
103
 
            stacklevel=3)
 
133
                      stacklevel=3)
104
134
    else:
105
135
        raise errors.LockNotHeld(locked_object)
106
136
 
111
141
except ImportError:
112
142
    have_fcntl = False
113
143
 
114
 
have_pywin32 = False
115
144
have_ctypes_win32 = False
116
145
if sys.platform == 'win32':
117
146
    import msvcrt
118
147
    try:
119
 
        import win32file, pywintypes, winerror
120
 
        have_pywin32 = True
121
 
    except ImportError:
122
 
        pass
123
 
 
124
 
    try:
125
148
        import ctypes
126
149
        have_ctypes_win32 = True
127
150
    except ImportError:
139
162
        try:
140
163
            self.f = open(self.filename, filemode)
141
164
            return self.f
142
 
        except IOError, e:
 
165
        except IOError as e:
143
166
            if e.errno in (errno.EACCES, errno.EPERM):
144
167
                raise errors.LockFailed(self.filename, str(e))
145
168
            if e.errno != errno.ENOENT:
157
180
            self.f.close()
158
181
            self.f = None
159
182
 
160
 
    def __del__(self):
161
 
        if self.f:
162
 
            from warnings import warn
163
 
            warn("lock on %r not released" % self.f)
164
 
            self.unlock()
165
 
 
166
183
    def unlock(self):
167
184
        raise NotImplementedError()
168
185
 
178
195
            fcntl.lockf(self.f, fcntl.LOCK_UN)
179
196
            self._clear_f()
180
197
 
181
 
 
182
198
    class _fcntl_WriteLock(_fcntl_FileLock):
183
199
 
184
200
        _open_locks = set()
207
223
                # LOCK_NB will cause IOError to be raised if we can't grab a
208
224
                # lock right away.
209
225
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
210
 
            except IOError, e:
 
226
            except IOError as e:
211
227
                if e.errno in (errno.EAGAIN, errno.EACCES):
212
228
                    # We couldn't grab the lock
213
229
                    self.unlock()
219
235
            _fcntl_WriteLock._open_locks.remove(self.filename)
220
236
            self._unlock()
221
237
 
222
 
 
223
238
    class _fcntl_ReadLock(_fcntl_FileLock):
224
239
 
225
240
        _open_locks = {}
242
257
                # LOCK_NB will cause IOError to be raised if we can't grab a
243
258
                # lock right away.
244
259
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
245
 
            except IOError, e:
 
260
            except IOError as e:
246
261
                # we should be more precise about whats a locking
247
262
                # error and whats a random-other error
248
263
                raise errors.LockContention(self.filename, e)
267
282
            """
268
283
            if self.filename in _fcntl_WriteLock._open_locks:
269
284
                raise AssertionError('file already locked: %r'
270
 
                    % (self.filename,))
 
285
                                     % (self.filename,))
271
286
            try:
272
287
                wlock = _fcntl_TemporaryWriteLock(self)
273
288
            except errors.LockError:
275
290
                return False, self
276
291
            return True, wlock
277
292
 
278
 
 
279
293
    class _fcntl_TemporaryWriteLock(_OSLock):
280
294
        """A token used when grabbing a temporary_write_lock.
281
295
 
295
309
 
296
310
            if self.filename in _fcntl_WriteLock._open_locks:
297
311
                raise AssertionError('file already locked: %r'
298
 
                    % (self.filename,))
 
312
                                     % (self.filename,))
299
313
 
300
314
            # See if we can open the file for writing. Another process might
301
315
            # have a read lock. We don't use self._open() because we don't want
303
317
            # done by _fcntl_ReadLock
304
318
            try:
305
319
                new_f = open(self.filename, 'rb+')
306
 
            except IOError, e:
 
320
            except IOError as e:
307
321
                if e.errno in (errno.EACCES, errno.EPERM):
308
322
                    raise errors.LockFailed(self.filename, str(e))
309
323
                raise
311
325
                # LOCK_NB will cause IOError to be raised if we can't grab a
312
326
                # lock right away.
313
327
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
314
 
            except IOError, e:
 
328
            except IOError as e:
315
329
                # TODO: Raise a more specific error based on the type of error
316
330
                raise errors.LockContention(self.filename, e)
317
331
            _fcntl_WriteLock._open_locks.add(self.filename)
320
334
 
321
335
        def restore_read_lock(self):
322
336
            """Restore the original ReadLock."""
323
 
            # For fcntl, since we never released the read lock, just release the
324
 
            # 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.
325
339
            fcntl.lockf(self.f, fcntl.LOCK_UN)
326
340
            self._clear_f()
327
341
            _fcntl_WriteLock._open_locks.remove(self.filename)
330
344
            self._read_lock = None
331
345
            return read_lock
332
346
 
333
 
 
334
347
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
335
348
 
336
349
 
337
 
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
341
 
    else:
342
 
        # for Windows 98
343
 
        win32file_CreateFile = win32file.CreateFile
344
 
 
345
 
    class _w32c_FileLock(_OSLock):
346
 
 
347
 
        def _open(self, filename, access, share, cflags, pymode):
348
 
            self.filename = osutils.realpath(filename)
349
 
            try:
350
 
                self._handle = win32file_CreateFile(filename, access, share,
351
 
                    None, win32file.OPEN_ALWAYS,
352
 
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
353
 
            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)
358
 
                raise
359
 
            fd = win32file._open_osfhandle(self._handle, cflags)
360
 
            self.f = os.fdopen(fd, pymode)
361
 
            return self.f
362
 
 
363
 
        def unlock(self):
364
 
            self._clear_f()
365
 
            self._handle = None
366
 
 
367
 
 
368
 
    class _w32c_ReadLock(_w32c_FileLock):
369
 
        def __init__(self, filename):
370
 
            super(_w32c_ReadLock, self).__init__()
371
 
            self._open(filename, win32file.GENERIC_READ,
372
 
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
373
 
 
374
 
        def temporary_write_lock(self):
375
 
            """Try to grab a write lock on the file.
376
 
 
377
 
            On platforms that support it, this will upgrade to a write lock
378
 
            without unlocking the file.
379
 
            Otherwise, this will release the read lock, and try to acquire a
380
 
            write lock.
381
 
 
382
 
            :return: A token which can be used to switch back to a read lock.
383
 
            """
384
 
            # I can't find a way to upgrade a read lock to a write lock without
385
 
            # unlocking first. So here, we do just that.
386
 
            self.unlock()
387
 
            try:
388
 
                wlock = _w32c_WriteLock(self.filename)
389
 
            except errors.LockError:
390
 
                return False, _w32c_ReadLock(self.filename)
391
 
            return True, wlock
392
 
 
393
 
 
394
 
    class _w32c_WriteLock(_w32c_FileLock):
395
 
        def __init__(self, filename):
396
 
            super(_w32c_WriteLock, self).__init__()
397
 
            self._open(filename,
398
 
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
399
 
                os.O_RDWR, "rb+")
400
 
 
401
 
        def restore_read_lock(self):
402
 
            """Restore the original ReadLock."""
403
 
            # For win32 we had to completely let go of the original lock, so we
404
 
            # just unlock and create a new read lock.
405
 
            self.unlock()
406
 
            return _w32c_ReadLock(self.filename)
407
 
 
408
 
 
409
 
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
410
 
 
411
 
 
412
350
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"
418
 
        LPTSTR = LPCWSTR
419
 
    else:
420
 
        _function_name = "CreateFileA"
421
 
        class LPTSTR(LPCSTR):
422
 
            def __new__(cls, obj):
423
 
                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"
424
355
 
425
356
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
426
357
    _CreateFile = ctypes.WINFUNCTYPE(
427
 
            HANDLE,                # return value
428
 
            LPTSTR,                # lpFileName
429
 
            DWORD,                 # dwDesiredAccess
430
 
            DWORD,                 # dwShareMode
431
 
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
432
 
            DWORD,                 # dwCreationDisposition
433
 
            DWORD,                 # dwFlagsAndAttributes
434
 
            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
435
366
        )((_function_name, ctypes.windll.kernel32))
436
367
 
437
368
    INVALID_HANDLE_VALUE = -1
450
381
        def _open(self, filename, access, share, cflags, pymode):
451
382
            self.filename = osutils.realpath(filename)
452
383
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
453
 
                FILE_ATTRIBUTE_NORMAL, 0)
 
384
                                 FILE_ATTRIBUTE_NORMAL, 0)
454
385
            if handle in (INVALID_HANDLE_VALUE, 0):
455
386
                e = ctypes.WinError()
456
387
                if e.args[0] == ERROR_ACCESS_DENIED:
465
396
        def unlock(self):
466
397
            self._clear_f()
467
398
 
468
 
 
469
399
    class _ctypes_ReadLock(_ctypes_FileLock):
470
400
        def __init__(self, filename):
471
401
            super(_ctypes_ReadLock, self).__init__()
472
402
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
473
 
                "rb")
 
403
                       "rb")
474
404
 
475
405
        def temporary_write_lock(self):
476
406
            """Try to grab a write lock on the file.
495
425
        def __init__(self, filename):
496
426
            super(_ctypes_WriteLock, self).__init__()
497
427
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
498
 
                "rb+")
 
428
                       "rb+")
499
429
 
500
430
        def restore_read_lock(self):
501
431
            """Restore the original ReadLock."""
504
434
            self.unlock()
505
435
            return _ctypes_ReadLock(self.filename)
506
436
 
507
 
 
508
437
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
509
438
 
510
439
 
511
440
if len(_lock_classes) == 0:
512
441
    raise NotImplementedError(
513
 
        "We must have one of fcntl, pywin32, or ctypes available"
 
442
        "We must have one of fcntl or ctypes available"
514
443
        " to support OS locking."
515
444
        )
516
445
 
527
456
    locked the same way), and -Drelock is set, then this will trace.note a
528
457
    message about it.
529
458
    """
530
 
    
 
459
 
531
460
    _prev_lock = None
532
461
 
533
462
    def _note_lock(self, lock_type):
536
465
                type_name = 'read'
537
466
            else:
538
467
                type_name = 'write'
539
 
            trace.note('%r was %s locked again', self, type_name)
 
468
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
540
469
        self._prev_lock = lock_type
541
470
 
 
471
 
 
472
@contextlib.contextmanager
 
473
def write_locked(lockable):
 
474
    lockable.lock_write()
 
475
    try:
 
476
        yield lockable
 
477
    finally:
 
478
        lockable.unlock()