From 9163511743a6d8c9b62fb6cd4b2a47f2ceae0b61 Mon Sep 17 00:00:00 2001 From: Michał Górny Date: Wed, 25 Oct 2017 00:07:19 +0200 Subject: verify: Support checking for cross-filesystem files --- gemato/exceptions.py | 12 ++++++++++++ gemato/verify.py | 15 ++++++++++++--- tests/test_verify.py | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/gemato/exceptions.py b/gemato/exceptions.py index e580309..c9cb646 100644 --- a/gemato/exceptions.py +++ b/gemato/exceptions.py @@ -38,3 +38,15 @@ class ManifestMismatch(Exception): self.path = path self.entry = entry self.diff = diff + + +class ManifestCrossDevice(Exception): + """ + An exception caused by attempting to cross filesystem boundaries. + """ + + def __init__(self, path): + self.path = path + super(ManifestCrossDevice, self).__init__( + "Path {} crosses filesystem boundaries, it must be IGNORE-d explicitly" + .format(path)) diff --git a/gemato/verify.py b/gemato/verify.py index ef8092b..74791ee 100644 --- a/gemato/verify.py +++ b/gemato/verify.py @@ -13,7 +13,7 @@ import gemato.hash import gemato.manifest -def verify_path(path, e): +def verify_path(path, e, expected_dev=None): """ Verify the file at system path @path against the data in entry @e. The path/filename is not matched against the entry -- the correct @@ -24,6 +24,11 @@ def verify_path(path, e): the file at path and the Manifest entry. Each list element is a tuple of (name, expected, got). + If @expected_dev is not None, verifies that the file resides + on specified device. If the device does not match, raises + ManifestCrossDevice exception. It can be used to verify that + the files do not cross filesystem boundaries. + Each name can be: - __exists__ (boolean) to indicate whether the file existed, - __type__ (string) as a human-readable description of file type, @@ -70,6 +75,10 @@ def verify_path(path, e): st = os.fstat(fd) else: st = os.stat(path) + if expected_dev is not None and st.st_dev != expected_dev: + if opened: + os.close(fd) + raise gemato.exceptions.ManifestCrossDevice(path) if not opened or not stat.S_ISREG(st.st_mode): if opened: os.close(fd) @@ -169,12 +178,12 @@ def verify_entry_compatibility(e1, e2): return (ret, diff) -def assert_path_verifies(path, e): +def assert_path_verifies(path, e, expected_dev=None): """ Verify the path @path against entry @e. Raises an exception if it does not pass the verification. """ - ret, diff = verify_path(path, e) + ret, diff = verify_path(path, e, expected_dev=expected_dev) if not ret: raise gemato.exceptions.ManifestMismatch(path, e, diff) diff --git a/tests/test_verify.py b/tests/test_verify.py index 4902ec8..bc41532 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -241,6 +241,30 @@ class EmptyFileVerificationTest(unittest.TestCase): self.assertEqual(gemato.verify.verify_path(self.path, None), (False, [('__exists__', False, True)])) + def testCrossFilesystem(self): + try: + st = os.stat('/proc') + except OSError: + raise unittest.SkipTest('Unable to stat /proc') + + e = gemato.manifest.ManifestEntryDATA.from_list( + ('DATA', os.path.basename(self.path), '0')) + self.assertRaises(gemato.exceptions.ManifestCrossDevice, + gemato.verify.verify_path, self.path, e, + expected_dev=st.st_dev) + + def testCrossFilesystemAssert(self): + try: + st = os.stat('/proc') + except OSError: + raise unittest.SkipTest('Unable to stat /proc') + + e = gemato.manifest.ManifestEntryDATA.from_list( + ('DATA', os.path.basename(self.path), '0')) + self.assertRaises(gemato.exceptions.ManifestCrossDevice, + gemato.verify.assert_path_verifies, self.path, e, + expected_dev=st.st_dev) + class NonEmptyFileVerificationTest(unittest.TestCase): def setUp(self): -- cgit v1.2.3