mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-20 21:27:32 +00:00
repository: add refcount corruption test
This commit is contained in:
parent
f61ee038d0
commit
2e067a7ae8
2 changed files with 68 additions and 8 deletions
|
@ -373,17 +373,18 @@ def commit(self, save_space=False):
|
|||
self.write_index()
|
||||
self.rollback()
|
||||
|
||||
def _read_integrity(self, transaction_id, key=None):
|
||||
integrity_path = os.path.join(self.path, 'integrity.%d' % transaction_id)
|
||||
def _read_integrity(self, transaction_id, key):
|
||||
integrity_file = 'integrity.%d' % transaction_id
|
||||
integrity_path = os.path.join(self.path, integrity_file)
|
||||
try:
|
||||
with open(integrity_path, 'rb') as fd:
|
||||
integrity = msgpack.unpack(fd)
|
||||
except FileNotFoundError:
|
||||
return
|
||||
if key:
|
||||
return integrity[key].decode()
|
||||
else:
|
||||
return integrity
|
||||
if integrity.get(b'version') != 2:
|
||||
logger.warning('Unknown integrity data version %r in %s', integrity.get(b'version'), integrity_file)
|
||||
return
|
||||
return integrity[key].decode()
|
||||
|
||||
def open_index(self, transaction_id, auto_recover=True):
|
||||
if transaction_id is None:
|
||||
|
@ -617,7 +618,7 @@ def complete_xfer(intermediate=True):
|
|||
# get rid of the old, sparse, unused segments. free space.
|
||||
for segment in unused:
|
||||
logger.debug('complete_xfer: deleting unused segment %d', segment)
|
||||
assert self.segments.pop(segment) == 0
|
||||
assert self.segments.pop(segment) == 0, 'Corrupted segment reference count - corrupted index or hints'
|
||||
self.io.delete_segment(segment)
|
||||
del self.compact[segment]
|
||||
unused = []
|
||||
|
@ -711,7 +712,7 @@ def complete_xfer(intermediate=True):
|
|||
new_segment, size = self.io.write_delete(key)
|
||||
self.compact[new_segment] += size
|
||||
segments.setdefault(new_segment, 0)
|
||||
assert segments[segment] == 0
|
||||
assert segments[segment] == 0, 'Corrupted segment reference count - corrupted index or hints'
|
||||
unused.append(segment)
|
||||
pi.show()
|
||||
pi.finish()
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
import msgpack
|
||||
|
||||
import pytest
|
||||
|
||||
from ..hashindex import NSIndex
|
||||
|
@ -555,6 +557,63 @@ def test_unreadable_index(self):
|
|||
with self.assert_raises(OSError):
|
||||
self.do_commit()
|
||||
|
||||
def test_unknown_integrity_version(self):
|
||||
integrity_path = os.path.join(self.repository.path, 'integrity.1')
|
||||
with open(integrity_path, 'r+b') as fd:
|
||||
msgpack.pack({
|
||||
b'version': 4.7,
|
||||
}, fd)
|
||||
fd.truncate()
|
||||
with self.repository:
|
||||
assert len(self.repository) == 1
|
||||
assert self.repository.get(H(0)) == b'foo'
|
||||
|
||||
def _subtly_corrupted_hints_setup(self):
|
||||
with self.repository:
|
||||
self.repository.append_only = True
|
||||
assert len(self.repository) == 1
|
||||
assert self.repository.get(H(0)) == b'foo'
|
||||
self.repository.put(H(1), b'bar')
|
||||
self.repository.put(H(2), b'baz')
|
||||
self.repository.commit()
|
||||
self.repository.put(H(2), b'bazz')
|
||||
self.repository.commit()
|
||||
|
||||
hints_path = os.path.join(self.repository.path, 'hints.5')
|
||||
with open(hints_path, 'r+b') as fd:
|
||||
hints = msgpack.unpack(fd)
|
||||
fd.seek(0)
|
||||
# Corrupt segment refcount
|
||||
assert hints[b'segments'][2] == 1
|
||||
hints[b'segments'][2] = 0
|
||||
msgpack.pack(hints, fd)
|
||||
fd.truncate()
|
||||
|
||||
def test_subtly_corrupted_hints(self):
|
||||
self._subtly_corrupted_hints_setup()
|
||||
with self.repository:
|
||||
self.repository.append_only = False
|
||||
self.repository.put(H(3), b'1234')
|
||||
# Do a compaction run. Succeeds, since the failed checksum prompted a rebuild of the index+hints.
|
||||
self.repository.commit()
|
||||
|
||||
assert len(self.repository) == 4
|
||||
assert self.repository.get(H(0)) == b'foo'
|
||||
assert self.repository.get(H(1)) == b'bar'
|
||||
assert self.repository.get(H(2)) == b'bazz'
|
||||
|
||||
def test_subtly_corrupted_hints_without_integrity(self):
|
||||
self._subtly_corrupted_hints_setup()
|
||||
integrity_path = os.path.join(self.repository.path, 'integrity.5')
|
||||
os.unlink(integrity_path)
|
||||
with self.repository:
|
||||
self.repository.append_only = False
|
||||
self.repository.put(H(3), b'1234')
|
||||
# Do a compaction run. Fails, since the corrupted refcount was not detected and leads to an assertion failure.
|
||||
with pytest.raises(AssertionError) as exc_info:
|
||||
self.repository.commit()
|
||||
assert 'Corrupted segment reference count' in str(exc_info.value)
|
||||
|
||||
|
||||
class RepositoryCheckTestCase(RepositoryTestCaseBase):
|
||||
|
||||
|
|
Loading…
Reference in a new issue