summaryrefslogtreecommitdiff
path: root/buildstream/_yaml.py
diff options
context:
space:
mode:
authorTiago Gomes <tiago.gomes@codethink.co.uk>2018-07-31 12:34:25 +0100
committerTiago Gomes <tiago.avv@gmail.com>2018-08-02 11:24:43 +0000
commite788bda4969de17178f3facc0d31b36c91121402 (patch)
tree0c94dc6911bb3efa0ab768a15b3f00120079662e /buildstream/_yaml.py
parent039d43e43431957a42193f12f06acb7b95c2eae4 (diff)
downloadbuildstream-e788bda4969de17178f3facc0d31b36c91121402.tar.gz
plugin: bake API to get and validate a project path
A project path is a path relative to a project directory. A project path can not also refer to the parent directory in the first path component, or point to symbolic links, fifos, sockets and block/character devices.
Diffstat (limited to 'buildstream/_yaml.py')
-rw-r--r--buildstream/_yaml.py92
1 files changed, 91 insertions, 1 deletions
diff --git a/buildstream/_yaml.py b/buildstream/_yaml.py
index 0e090e2e7..33ee444aa 100644
--- a/buildstream/_yaml.py
+++ b/buildstream/_yaml.py
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2016 Codethink Limited
+# Copyright (C) 2018 Codethink Limited
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,7 @@ import collections
import string
from copy import deepcopy
from contextlib import ExitStack
+from pathlib import Path
from ruamel import yaml
from ruamel.yaml.representer import SafeRepresenter, RoundTripRepresenter
@@ -392,6 +393,95 @@ def node_get(node, expected_type, key, indices=None, default_value=_get_sentinel
return value
+# node_get_project_path()
+#
+# Fetches a project path from a dictionary node and validates it
+#
+# Paths are asserted to never lead to a directory outside of the project
+# directory. In addition, paths can not point to symbolic links, fifos,
+# sockets and block/character devices.
+#
+# The `check_is_file` and `check_is_dir` parameters can be used to
+# perform additional validations on the path. Note that an exception
+# will always be raised if both parameters are set to ``True``.
+#
+# Args:
+# node (dict): A dictionary loaded from YAML
+# key (str): The key whose value contains a path to validate
+# project_dir (str): The project directory
+# check_is_file (bool): If ``True`` an error will also be raised
+# if path does not point to a regular file.
+# Defaults to ``False``
+# check_is_dir (bool): If ``True`` an error will be also raised
+# if path does not point to a directory.
+# Defaults to ``False``
+# Returns:
+# (str): The project path
+#
+# Raises:
+# (LoadError): In case that the project path is not valid or does not
+# exist
+#
+def node_get_project_path(node, key, project_dir, *,
+ check_is_file=False, check_is_dir=False):
+ path_str = node_get(node, str, key)
+ path = Path(path_str)
+ project_dir_path = Path(project_dir)
+
+ provenance = node_get_provenance(node, key=key)
+
+ if (project_dir_path / path).is_symlink():
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID_KIND,
+ "{}: Specified path '{}' must not point to "
+ "symbolic links "
+ .format(provenance, path_str))
+
+ if path.parts and path.parts[0] == '..':
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID,
+ "{}: Specified path '{}' first component must "
+ "not be '..'"
+ .format(provenance, path_str))
+
+ try:
+ if sys.version_info[0] == 3 and sys.version_info[1] < 6:
+ full_resolved_path = (project_dir_path / path).resolve()
+ else:
+ full_resolved_path = (project_dir_path / path).resolve(strict=True)
+ except FileNotFoundError:
+ raise LoadError(LoadErrorReason.MISSING_FILE,
+ "{}: Specified path '{}' does not exist"
+ .format(provenance, path_str))
+
+ is_inside = project_dir_path in full_resolved_path.parents or (
+ full_resolved_path == project_dir_path)
+
+ if path.is_absolute() or not is_inside:
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID,
+ "{}: Specified path '{}' must not lead outside of the "
+ "project directory"
+ .format(provenance, path_str))
+
+ if full_resolved_path.is_socket() or (
+ full_resolved_path.is_fifo() or
+ full_resolved_path.is_block_device()):
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID_KIND,
+ "{}: Specified path '{}' points to an unsupported "
+ "file kind"
+ .format(provenance, path_str))
+
+ if check_is_file and not full_resolved_path.is_file():
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID_KIND,
+ "{}: Specified path '{}' is not a regular file"
+ .format(provenance, path_str))
+
+ if check_is_dir and not full_resolved_path.is_dir():
+ raise LoadError(LoadErrorReason.PROJ_PATH_INVALID_KIND,
+ "{}: Specified path '{}' is not a directory"
+ .format(provenance, path_str))
+
+ return path_str
+
+
# node_items()
#
# A convenience generator for iterating over loaded key/value