summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gemato/exceptions.py15
-rw-r--r--gemato/manifest.py9
-rw-r--r--gemato/verify.py11
-rw-r--r--tests/test_recursiveloader.py54
4 files changed, 82 insertions, 7 deletions
diff --git a/gemato/exceptions.py b/gemato/exceptions.py
index 6d7e8f3..db754ac 100644
--- a/gemato/exceptions.py
+++ b/gemato/exceptions.py
@@ -264,3 +264,18 @@ class ManifestInvalidFilename(GematoException):
f'to Manifest: disallowed character '
f'U+{ord(self.filename[self.pos]):04X} at position '
f'{self.pos}')
+
+
+class ManifestNoSupportedHashes(GematoException):
+ """
+ An exception raised when all hashes given for an entry are
+ unsupported.
+ """
+
+ def __init__(self, entry):
+ super().__init__()
+ self.entry = entry
+
+ def __str__(self):
+ return (f"No hashes provided for file {self.entry.path!r} are "
+ f"supported (out of {' '.join(self.entry.checksums)})")
diff --git a/gemato/manifest.py b/gemato/manifest.py
index 56f725f..2ce4811 100644
--- a/gemato/manifest.py
+++ b/gemato/manifest.py
@@ -1,6 +1,6 @@
# gemato: Manifest file objects
# vim:fileencoding=utf-8
-# (c) 2017-2020 Michał Górny
+# (c) 2017-2022 Michał Górny
# Licensed under the terms of 2-clause BSD license
import datetime
@@ -557,3 +557,10 @@ def manifest_hashes_to_hashlib(hashes):
"""
for h in hashes:
yield MANIFEST_HASH_MAPPING[h]
+
+
+def is_hash_supported(h):
+ """
+ Return True if the passed hash (in Manifest naming) is supported.
+ """
+ return h in MANIFEST_HASH_MAPPING
diff --git a/gemato/verify.py b/gemato/verify.py
index 64d367e..363c9cc 100644
--- a/gemato/verify.py
+++ b/gemato/verify.py
@@ -1,6 +1,6 @@
# gemato: File verification routines
# vim:fileencoding=utf-8
-# (c) 2017-2020 Michał Górny
+# (c) 2017-2022 Michał Górny
# Licensed under the terms of 2-clause BSD license
import contextlib
@@ -12,9 +12,10 @@ import stat
from gemato.exceptions import (
ManifestCrossDevice,
ManifestInvalidPath,
+ ManifestNoSupportedHashes,
)
from gemato.hash import hash_file
-from gemato.manifest import manifest_hashes_to_hashlib
+from gemato.manifest import manifest_hashes_to_hashlib, is_hash_supported
def get_file_metadata(path, hashes):
@@ -163,7 +164,9 @@ def verify_path(path, e, expected_dev=None, last_mtime=None):
checksums = ()
else:
expect_exist = True
- checksums = e.checksums
+ checksums = list(filter(is_hash_supported, e.checksums))
+ if not checksums:
+ raise ManifestNoSupportedHashes(e)
with contextlib.closing(get_file_metadata(path, checksums)) as g:
# 1. verify whether the file existed in the first place
@@ -204,7 +207,7 @@ def verify_path(path, e, expected_dev=None, last_mtime=None):
diff.append(('__size__', e.size, size))
# 7. verify the checksums
- for h in sorted(e.checksums):
+ for h in sorted(checksums):
exp = e.checksums[h]
got = checksums[h]
if got != exp:
diff --git a/tests/test_recursiveloader.py b/tests/test_recursiveloader.py
index 2d2c9dc..a8ef939 100644
--- a/tests/test_recursiveloader.py
+++ b/tests/test_recursiveloader.py
@@ -1,6 +1,6 @@
# gemato: Recursive loader tests
# vim:fileencoding=utf-8
-# (c) 2017-2020 Michał Górny
+# (c) 2017-2022 Michał Górny
# Licensed under the terms of 2-clause BSD license
import base64
@@ -20,8 +20,9 @@ from gemato.exceptions import (
ManifestIncompatibleEntry,
ManifestCrossDevice,
ManifestSymlinkLoop,
+ ManifestNoSupportedHashes,
)
-from gemato.manifest import ManifestPathEntry
+from gemato.manifest import ManifestPathEntry, ManifestFileEntry
from gemato.recursiveloader import ManifestRecursiveLoader
from tests.test_compression import COMPRESSION_ALGOS
@@ -788,6 +789,32 @@ DATA test 11 SHA1 561295c9cbf9d6b2f6428414504a8deed3020641
f'TIMESTAMP {future_dt.strftime("%Y-%m-%dT%H:%M:%SZ")}'))
+class UnknownHashLayout(BaseLayout):
+ """Layout with a supported and unknown hash"""
+
+ MANIFESTS = {
+ 'Manifest': '''
+DATA test 0 MD5 d41d8cd98f00b204e9800998ecf8427e X-UNKNOWN 0123456789abcdef
+''',
+ }
+ FILES = {
+ 'test': '',
+ }
+
+
+class UnknownHashOnlyLayout(BaseLayout):
+ """Layout with unknown hash only"""
+
+ MANIFESTS = {
+ 'Manifest': '''
+DATA test 0 X-UNKNOWN 0123456789abcdef
+''',
+ }
+ FILES = {
+ 'test': '',
+ }
+
+
FLAT_LAYOUTS = [
DuplicateEntryLayout,
DuplicateEbuildEntryLayout,
@@ -818,6 +845,8 @@ FLAT_LAYOUTS = [
SymlinkLoopLayout,
SymlinkLoopIgnoreLayout,
MismatchedFileLayout,
+ UnknownHashLayout,
+ UnknownHashOnlyLayout,
]
SUB_LAYOUTS = [
SubTimestampLayout,
@@ -1069,6 +1098,7 @@ COMMON_VERIFY_PATH_VARIANTS = [
[('MD5',
'5f8db599de986fab7a21625b7916589c',
'6f8db599de986fab7a21625b7916589c')]),
+ (UnknownHashLayout, 'test', True, []),
]
@@ -1151,6 +1181,8 @@ def test_assert_path_verifies(layout_factory, layout, path, expected, diff):
{'': {'metadata.xml': ('DATA', 'metadata.xml', ['MD5'])}}),
(IncompatibleTypeLayout, '', 'MISC',
{'': {'metadata.xml': ('MISC', 'metadata.xml', ['MD5'])}}),
+ (UnknownHashLayout, '', None,
+ {'': {'test': ('DATA', 'test', ['MD5', 'X-UNKNOWN'])}}),
])
def test_get_file_entry_dict(layout_factory, layout, path, only_types,
expected):
@@ -1281,6 +1313,7 @@ COMMON_DIRECTORY_VERIFICATION_VARIANTS = [
[('MD5',
'5f8db599de986fab7a21625b7916589c',
'6f8db599de986fab7a21625b7916589c')]),
+ (UnknownHashLayout, '', None, []),
]
@@ -1323,6 +1356,7 @@ COMMON_DIRECTORY_VERIFICATION_VARIANTS = [
(SymlinkLoopLayout, '', lambda e: False, ManifestSymlinkLoop, None,
[]),
(SymlinkLoopIgnoreLayout, '', None, True, None, []),
+ (UnknownHashOnlyLayout, '', None, ManifestNoSupportedHashes, None, []),
])
def test_assert_directory_verifies(layout_factory, layout, path, fail_handler,
expected, fail_path, diff):
@@ -1380,6 +1414,9 @@ def test_assert_directory_verifies(layout_factory, layout, path, fail_handler,
(SymlinkLoopLayout, '', '',
str(ManifestSymlinkLoop('<path>')).split('<path>', 1)[1]),
(SymlinkLoopIgnoreLayout, '', '', None),
+ (UnknownHashOnlyLayout, '', '',
+ str(ManifestNoSupportedHashes(ManifestFileEntry(
+ 'test', 0, {"X-UNKNOWN": ""})))),
])
def test_cli_verify(layout_factory, caplog, layout, path, args, expected):
tmp_path = layout_factory.create(layout, readonly=True)
@@ -1942,6 +1979,19 @@ def test_update_entry_raise(layout_factory, layout, path, expected, reason):
@pytest.mark.parametrize(
+ 'layout,path',
+ [(UnknownHashLayout, 'test'),
+ (UnknownHashOnlyLayout, 'test'),
+ ])
+def test_update_entry_unknown_hash(layout_factory, layout, path):
+ tmp_path = layout_factory.create(layout)
+ m = ManifestRecursiveLoader(tmp_path / layout.TOP_MANIFEST,
+ allow_xdev=False)
+ with pytest.raises(KeyError) as exc:
+ m.update_entry_for_path(path)
+
+
+@pytest.mark.parametrize(
'layout,path,expected',
[(BasicTestLayout, 'nonexist', FileNotFoundError),
(CrossDeviceLayout, '', ManifestCrossDevice),