summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gemato/exceptions.py17
-rw-r--r--gemato/recursiveloader.py37
-rw-r--r--tests/test_recursiveloader.py13
3 files changed, 60 insertions, 7 deletions
diff --git a/gemato/exceptions.py b/gemato/exceptions.py
index d544f62..d2c818e 100644
--- a/gemato/exceptions.py
+++ b/gemato/exceptions.py
@@ -89,6 +89,23 @@ class ManifestCrossDevice(GematoException):
.format(self.path))
+class ManifestSymlinkLoop(GematoException):
+ """
+ An exception caused by hitting a symlink loop (symlink to itself
+ or a parent directory).
+ """
+
+ __slots__ = ['path']
+
+ def __init__(self, path):
+ super(ManifestSymlinkLoop, self).__init__(path)
+ self.path = path
+
+ def __str__(self):
+ return ("Path {} is a symlink to one of its parent directories, it must be IGNORE-d explicitly"
+ .format(self.path))
+
+
class ManifestUnsignedData(GematoException):
"""
An exception caused by a Manifest file containing non-whitespace
diff --git a/gemato/recursiveloader.py b/gemato/recursiveloader.py
index 53a328e..b89df87 100644
--- a/gemato/recursiveloader.py
+++ b/gemato/recursiveloader.py
@@ -593,11 +593,21 @@ class ManifestRecursiveLoader(object):
Pre-process os.walk() result for verification. Yield objects
suitable to passing to subprocesses.
"""
+ directory_ids = {}
+
for dirpath, dirnames, filenames in it:
dir_st = os.stat(dirpath)
if dir_st.st_dev != self.manifest_device:
raise gemato.exceptions.ManifestCrossDevice(dirpath)
+ dir_id = (dir_st.st_dev, dir_st.st_ino)
+ # if this directory was already processed for one of its
+ # parents, we're in a loop
+ parent_dir = os.path.dirname(dirpath)
+ parent_dir_ids = directory_ids.get(parent_dir, [])
+ if dir_id in parent_dir_ids:
+ raise gemato.exceptions.ManifestSymlinkLoop(dirpath)
+
relpath = os.path.relpath(dirpath, self.root_directory)
# strip dot to avoid matching problems
if relpath == '.':
@@ -625,6 +635,9 @@ class ManifestRecursiveLoader(object):
# skip scanning ignored directories
for d in skip_dirs:
dirnames.remove(d)
+ # if we are planning to recur, record this dir
+ if dirnames:
+ directory_ids[dirpath] = parent_dir_ids + [dir_id]
yield (dirpath, relpath, dirnames, filenames, dirdict)
@@ -962,6 +975,7 @@ class ManifestRecursiveLoader(object):
entry_dict = self.get_file_entry_dict(path,
only_types=['IGNORE'], verify_manifests=verify_manifests)
new_manifests = []
+ directory_ids = {}
it = os.walk(os.path.join(self.root_directory, path),
onerror=gemato.util.throw_exception,
followlinks=True)
@@ -971,6 +985,14 @@ class ManifestRecursiveLoader(object):
if dir_st.st_dev != self.manifest_device:
raise gemato.exceptions.ManifestCrossDevice(dirpath)
+ dir_id = (dir_st.st_dev, dir_st.st_ino)
+ # if this directory was already processed for one of its
+ # parents, we're in a loop
+ parent_dir = os.path.dirname(dirpath)
+ parent_dir_ids = directory_ids.get(parent_dir, [])
+ if dir_id in parent_dir_ids:
+ raise gemato.exceptions.ManifestSymlinkLoop(dirpath)
+
relpath = os.path.relpath(dirpath, self.root_directory)
# strip dot to avoid matching problems
if relpath == '.':
@@ -994,6 +1016,9 @@ class ManifestRecursiveLoader(object):
# skip scanning ignored directories
for d in skip_dirs:
dirnames.remove(d)
+ # if we are planning to recur, record this dir
+ if dirnames:
+ directory_ids[dirpath] = parent_dir_ids + [dir_id]
# check for unregistered Manifest
for mname in manifest_filenames:
@@ -1061,6 +1086,7 @@ class ManifestRecursiveLoader(object):
._iter_manifests_for_path(path)):
manifest_stack.append((mpath, mrpath, m))
break
+ directory_ids = {}
it = os.walk(os.path.join(self.root_directory, path),
onerror=gemato.util.throw_exception,
@@ -1071,6 +1097,14 @@ class ManifestRecursiveLoader(object):
if dir_st.st_dev != self.manifest_device:
raise gemato.exceptions.ManifestCrossDevice(dirpath)
+ dir_id = (dir_st.st_dev, dir_st.st_ino)
+ # if this directory was already processed for one of its
+ # parents, we're in a loop
+ parent_dir = os.path.dirname(dirpath)
+ parent_dir_ids = directory_ids.get(parent_dir, [])
+ if dir_id in parent_dir_ids:
+ raise gemato.exceptions.ManifestSymlinkLoop(dirpath)
+
relpath = os.path.relpath(dirpath, self.root_directory)
# strip dot to avoid matching problems
if relpath == '.':
@@ -1110,6 +1144,9 @@ class ManifestRecursiveLoader(object):
# skip scanning ignored directories
for d in skip_dirs:
dirnames.remove(d)
+ # if we are planning to recur, record this dir
+ if dirnames:
+ directory_ids[dirpath] = parent_dir_ids + [dir_id]
new_entries = []
for f in filenames:
diff --git a/tests/test_recursiveloader.py b/tests/test_recursiveloader.py
index 94002d2..19646df 100644
--- a/tests/test_recursiveloader.py
+++ b/tests/test_recursiveloader.py
@@ -2737,21 +2737,20 @@ class SymlinkLoopTest(TempDirTestCase):
def test_assert_directory_verifies(self):
m = gemato.recursiveloader.ManifestRecursiveLoader(
os.path.join(self.dir, 'Manifest'))
- self.assertRaises(OSError,
+ self.assertRaises(gemato.exceptions.ManifestSymlinkLoop,
m.assert_directory_verifies, '')
def test_cli_verifies(self):
- self.assertRaises(OSError,
- gemato.cli.main, ['gemato', 'verify', self.dir])
+ self.assertEqual(gemato.cli.main(['gemato', 'verify', self.dir]),
+ 1)
def test_update_entries_for_directory(self):
m = gemato.recursiveloader.ManifestRecursiveLoader(
os.path.join(self.dir, 'Manifest'),
hashes=['SHA256', 'SHA512'])
- self.assertRaises(OSError,
+ self.assertRaises(gemato.exceptions.ManifestSymlinkLoop,
m.update_entries_for_directory, '')
def test_cli_update(self):
- self.assertRaises(OSError,
- gemato.cli.main, ['gemato', 'update',
- '--hashes=SHA256 SHA512', self.dir])
+ self.assertEqual(gemato.cli.main(['gemato', 'update',
+ '--hashes=SHA256 SHA512', self.dir]), 1)