浏览代码

repository: add complementary index corruption test

Marian Beermann 8 年之前
父节点
当前提交
54e023c75a
共有 2 个文件被更改,包括 36 次插入6 次删除
  1. 2 0
      src/borg/repository.py
  2. 34 6
      src/borg/testsuite/repository.py

+ 2 - 0
src/borg/repository.py

@@ -490,6 +490,8 @@ class Repository:
             b'storage_quota_use': self.storage_quota_use,
         }
         integrity = {
+            # Integrity version started at 2, the current hints version.
+            # Thus, integrity version == hints version, for now.
             b'version': 2,
         }
         transaction_id = self.io.get_segments_transaction_id()

+ 34 - 6
src/borg/testsuite/repository.py

@@ -503,11 +503,6 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase):
         self.repository.commit()
         self.repository.close()
 
-    def corrupt(self, file):
-        with open(file, 'r+b') as fd:
-            fd.seek(-1, io.SEEK_END)
-            fd.write(b'1')
-
     def do_commit(self):
         with self.repository:
             self.repository.put(H(0), b'fox')
@@ -544,12 +539,42 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase):
         with self.repository:
             assert len(self.repository) == 1
 
+    def _corrupt_index(self):
+        # HashIndex is able to detect incorrect headers and file lengths,
+        # but on its own it can't tell if the data is correct.
+        index_path = os.path.join(self.repository.path, 'index.1')
+        with open(index_path, 'r+b') as fd:
+            index_data = fd.read()
+            # Flip one bit in a key stored in the index
+            corrupted_key = (int.from_bytes(H(0), 'little') ^ 1).to_bytes(32, 'little')
+            corrupted_index_data = index_data.replace(H(0), corrupted_key)
+            assert corrupted_index_data != index_data
+            assert len(corrupted_index_data) == len(index_data)
+            fd.seek(0)
+            fd.write(corrupted_index_data)
+
     def test_index_corrupted(self):
-        self.corrupt(os.path.join(self.repository.path, 'index.1'))
+        # HashIndex is able to detect incorrect headers and file lengths,
+        # but on its own it can't tell if the data itself is correct.
+        self._corrupt_index()
         with self.repository:
+            # Data corruption is detected due to mismatching checksums
+            # and fixed by rebuilding the index.
             assert len(self.repository) == 1
             assert self.repository.get(H(0)) == b'foo'
 
+    def test_index_corrupted_without_integrity(self):
+        self._corrupt_index()
+        integrity_path = os.path.join(self.repository.path, 'integrity.1')
+        os.unlink(integrity_path)
+        with self.repository:
+            # Since the corrupted key is not noticed, the repository still thinks
+            # it contains one key...
+            assert len(self.repository) == 1
+            with pytest.raises(Repository.ObjectNotFound):
+                # ... but the real, uncorrupted key is not found in the corrupted index.
+                self.repository.get(H(0))
+
     def test_unreadable_index(self):
         index = os.path.join(self.repository.path, 'index.1')
         os.unlink(index)
@@ -558,13 +583,16 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase):
             self.do_commit()
 
     def test_unknown_integrity_version(self):
+        # For now an unknown integrity data version is ignored and not an error.
         integrity_path = os.path.join(self.repository.path, 'integrity.1')
         with open(integrity_path, 'r+b') as fd:
             msgpack.pack({
+                # Borg only understands version 2
                 b'version': 4.7,
             }, fd)
             fd.truncate()
         with self.repository:
+            # No issues accessing the repository
             assert len(self.repository) == 1
             assert self.repository.get(H(0)) == b'foo'