Pārlūkot izejas kodu

Merge pull request #1906 from enkore/f/check-corrupted-manifest

check: handle corrupted manifest
TW 8 gadi atpakaļ
vecāks
revīzija
8ddbc45c29
2 mainītis faili ar 43 papildinājumiem un 3 dzēšanām
  1. 14 3
      borg/archive.py
  2. 29 0
      borg/testsuite/archiver.py

+ 14 - 3
borg/archive.py

@@ -19,7 +19,7 @@ from . import xattr
 from .helpers import Error, uid2user, user2uid, gid2group, group2gid, bin_to_hex, \
     parse_timestamp, to_localtime, format_time, format_timedelta, remove_surrogates, \
     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 .chunker import Chunker
 from .hashindex import ChunkIndex
@@ -849,7 +849,13 @@ class ArchiveChecker:
             self.error_found = True
             self.manifest = self.rebuild_manifest()
         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.orphan_chunks_check()
         self.finish(save_space=save_space)
@@ -906,7 +912,12 @@ class ArchiveChecker:
         archive_keys_serialized = [msgpack.packb(name) for name in ARCHIVE_KEYS]
         for chunk_id, _ in self.chunks.iteritems():
             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):
                 continue
             if b'cmdline' not in data or b'\xa7version\x01' not in data:

+ 29 - 0
borg/testsuite/archiver.py

@@ -1433,6 +1433,35 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
         self.assert_in('archive2', output)
         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):
         self.cmd('check', self.repository_location, exit_code=0)
         with Repository(self.repository_location, exclusive=True) as repository: