/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 bzrlib/lockdir.py

(mbp) more gracefully handle corrupt lockdirs (missing/null held files)
 (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
118
118
        LockBreakMismatch,
119
119
        LockBroken,
120
120
        LockContention,
 
121
        LockCorrupt,
121
122
        LockFailed,
122
123
        LockNotHeld,
123
124
        NoSuchFile,
345
346
        it possibly being still active.
346
347
        """
347
348
        self._check_not_locked()
348
 
        holder_info = self.peek()
 
349
        try:
 
350
            holder_info = self.peek()
 
351
        except LockCorrupt, e:
 
352
            # The lock info is corrupt.
 
353
            if bzrlib.ui.ui_factory.get_boolean("Break (corrupt %r)" % (self,)):
 
354
                self.force_break_corrupt(e.file_data)
 
355
            return
349
356
        if holder_info is not None:
350
357
            lock_info = '\n'.join(self._format_lock_info(holder_info))
351
358
            if bzrlib.ui.ui_factory.get_boolean("Break %s" % lock_info):
392
399
        for hook in self.hooks['lock_broken']:
393
400
            hook(result)
394
401
 
 
402
    def force_break_corrupt(self, corrupt_info_lines):
 
403
        """Release a lock that has been corrupted.
 
404
        
 
405
        This is very similar to force_break, it except it doesn't assume that
 
406
        self.peek() can work.
 
407
        
 
408
        :param corrupt_info_lines: the lines of the corrupted info file, used
 
409
            to check that the lock hasn't changed between reading the (corrupt)
 
410
            info file and calling force_break_corrupt.
 
411
        """
 
412
        # XXX: this copes with unparseable info files, but what about missing
 
413
        # info files?  Or missing lock dirs?
 
414
        self._check_not_locked()
 
415
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
 
416
        self.transport.rename(self._held_dir, tmpname)
 
417
        # check that we actually broke the right lock, not someone else;
 
418
        # there's a small race window between checking it and doing the
 
419
        # rename.
 
420
        broken_info_path = tmpname + self.__INFO_NAME
 
421
        f = self.transport.get(broken_info_path)
 
422
        broken_lines = f.readlines()
 
423
        if broken_lines != corrupt_info_lines:
 
424
            raise LockBreakMismatch(self, broken_lines, corrupt_info_lines)
 
425
        self.transport.delete(broken_info_path)
 
426
        self.transport.rmdir(tmpname)
 
427
        result = lock.LockResult(self.transport.abspath(self.path))
 
428
        for hook in self.hooks['lock_broken']:
 
429
            hook(result)
 
430
 
395
431
    def _check_not_locked(self):
396
432
        """If the lock is held by this instance, raise an error."""
397
433
        if self._lock_held:
456
492
        return s.to_string()
457
493
 
458
494
    def _parse_info(self, info_file):
459
 
        stanza = rio.read_stanza(info_file.readlines())
 
495
        lines = info_file.readlines()
 
496
        try:
 
497
            stanza = rio.read_stanza(lines)
 
498
        except ValueError, e:
 
499
            mutter('Corrupt lock info file: %r', lines)
 
500
            raise LockCorrupt("could not parse lock info file: " + str(e),
 
501
                              lines)
460
502
        if stanza is None:
461
503
            # see bug 185013; we fairly often end up with the info file being
462
504
            # empty after an interruption; we could log a message here but