mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-24 07:01:59 +00:00
Merge pull request #1906 from enkore/f/check-corrupted-manifest
check: handle corrupted manifest
This commit is contained in:
commit
8ddbc45c29
2 changed files with 43 additions and 3 deletions
|
@ -19,7 +19,7 @@
|
||||||
from .helpers import Error, uid2user, user2uid, gid2group, group2gid, bin_to_hex, \
|
from .helpers import Error, uid2user, user2uid, gid2group, group2gid, bin_to_hex, \
|
||||||
parse_timestamp, to_localtime, format_time, format_timedelta, remove_surrogates, \
|
parse_timestamp, to_localtime, format_time, format_timedelta, remove_surrogates, \
|
||||||
Manifest, Statistics, decode_dict, make_path_safe, StableDict, int_to_bigint, bigint_to_int, \
|
Manifest, Statistics, decode_dict, make_path_safe, StableDict, int_to_bigint, bigint_to_int, \
|
||||||
ProgressIndicatorPercent
|
ProgressIndicatorPercent, IntegrityError
|
||||||
from .platform import acl_get, acl_set
|
from .platform import acl_get, acl_set
|
||||||
from .chunker import Chunker
|
from .chunker import Chunker
|
||||||
from .hashindex import ChunkIndex
|
from .hashindex import ChunkIndex
|
||||||
|
@ -849,7 +849,13 @@ def check(self, repository, repair=False, archive=None, last=None, prefix=None,
|
||||||
self.error_found = True
|
self.error_found = True
|
||||||
self.manifest = self.rebuild_manifest()
|
self.manifest = self.rebuild_manifest()
|
||||||
else:
|
else:
|
||||||
self.manifest, _ = Manifest.load(repository, key=self.key)
|
try:
|
||||||
|
self.manifest, _ = Manifest.load(repository, key=self.key)
|
||||||
|
except IntegrityError as exc:
|
||||||
|
logger.error('Repository manifest is corrupted: %s', exc)
|
||||||
|
self.error_found = True
|
||||||
|
del self.chunks[Manifest.MANIFEST_ID]
|
||||||
|
self.manifest = self.rebuild_manifest()
|
||||||
self.rebuild_refcounts(archive=archive, last=last, prefix=prefix)
|
self.rebuild_refcounts(archive=archive, last=last, prefix=prefix)
|
||||||
self.orphan_chunks_check()
|
self.orphan_chunks_check()
|
||||||
self.finish(save_space=save_space)
|
self.finish(save_space=save_space)
|
||||||
|
@ -906,7 +912,12 @@ def valid_archive(obj):
|
||||||
archive_keys_serialized = [msgpack.packb(name) for name in ARCHIVE_KEYS]
|
archive_keys_serialized = [msgpack.packb(name) for name in ARCHIVE_KEYS]
|
||||||
for chunk_id, _ in self.chunks.iteritems():
|
for chunk_id, _ in self.chunks.iteritems():
|
||||||
cdata = self.repository.get(chunk_id)
|
cdata = self.repository.get(chunk_id)
|
||||||
data = self.key.decrypt(chunk_id, cdata)
|
try:
|
||||||
|
data = self.key.decrypt(chunk_id, cdata)
|
||||||
|
except IntegrityError as exc:
|
||||||
|
logger.error('Skipping corrupted chunk: %s', exc)
|
||||||
|
self.error_found = True
|
||||||
|
continue
|
||||||
if not valid_msgpacked_dict(data, archive_keys_serialized):
|
if not valid_msgpacked_dict(data, archive_keys_serialized):
|
||||||
continue
|
continue
|
||||||
if b'cmdline' not in data or b'\xa7version\x01' not in data:
|
if b'cmdline' not in data or b'\xa7version\x01' not in data:
|
||||||
|
|
|
@ -1433,6 +1433,35 @@ def test_missing_manifest(self):
|
||||||
self.assert_in('archive2', output)
|
self.assert_in('archive2', output)
|
||||||
self.cmd('check', self.repository_location, exit_code=0)
|
self.cmd('check', self.repository_location, exit_code=0)
|
||||||
|
|
||||||
|
def test_corrupted_manifest(self):
|
||||||
|
archive, repository = self.open_archive('archive1')
|
||||||
|
with repository:
|
||||||
|
manifest = repository.get(Manifest.MANIFEST_ID)
|
||||||
|
corrupted_manifest = manifest + b'corrupted!'
|
||||||
|
repository.put(Manifest.MANIFEST_ID, corrupted_manifest)
|
||||||
|
repository.commit()
|
||||||
|
self.cmd('check', self.repository_location, exit_code=1)
|
||||||
|
output = self.cmd('check', '-v', '--repair', self.repository_location, exit_code=0)
|
||||||
|
self.assert_in('archive1', output)
|
||||||
|
self.assert_in('archive2', output)
|
||||||
|
self.cmd('check', self.repository_location, exit_code=0)
|
||||||
|
|
||||||
|
def test_manifest_rebuild_corrupted_chunk(self):
|
||||||
|
archive, repository = self.open_archive('archive1')
|
||||||
|
with repository:
|
||||||
|
manifest = repository.get(Manifest.MANIFEST_ID)
|
||||||
|
corrupted_manifest = manifest + b'corrupted!'
|
||||||
|
repository.put(Manifest.MANIFEST_ID, corrupted_manifest)
|
||||||
|
|
||||||
|
chunk = repository.get(archive.id)
|
||||||
|
corrupted_chunk = chunk + b'corrupted!'
|
||||||
|
repository.put(archive.id, corrupted_chunk)
|
||||||
|
repository.commit()
|
||||||
|
self.cmd('check', self.repository_location, exit_code=1)
|
||||||
|
output = self.cmd('check', '-v', '--repair', self.repository_location, exit_code=0)
|
||||||
|
self.assert_in('archive2', output)
|
||||||
|
self.cmd('check', self.repository_location, exit_code=0)
|
||||||
|
|
||||||
def test_extra_chunks(self):
|
def test_extra_chunks(self):
|
||||||
self.cmd('check', self.repository_location, exit_code=0)
|
self.cmd('check', self.repository_location, exit_code=0)
|
||||||
with Repository(self.repository_location, exclusive=True) as repository:
|
with Repository(self.repository_location, exclusive=True) as repository:
|
||||||
|
|
Loading…
Reference in a new issue