interpreter: do not use pathlib in validate_within_subproject

pathlib's implementation of Path iteration is expensive;
"foo.parents" has quadratic complexity when building it:

        return self._path._from_parsed_parts(self._drv, self._root,
                                             self._tail[:-idx - 1])

and comparisons performed by "path in file.parents" potentially
have the same issue.  Use the newly introduced "is_parent_path"
function to check whether a file is under a path in the same way.
This removes Path from its hottest use.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
pull/14110/head
Paolo Bonzini 2 months ago
parent 2a6d9e173d
commit 2672b9a542
  1. 34
      mesonbuild/interpreter/interpreter.py

@ -19,7 +19,7 @@ from .. import envconfig
from ..wrap import wrap, WrapMode
from .. import mesonlib
from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject,
FileMode, MachineChoice, listify,
FileMode, MachineChoice, is_parent_path, listify,
extract_as_list, has_path_sep, path_is_in_root, PerMachine)
from ..options import OptionKey
from ..programs import ExternalProgram, NonExistingExternalProgram
@ -3133,8 +3133,8 @@ class Interpreter(InterpreterBase, HoldableObject):
# subproject than it is defined in (due to e.g. a
# declare_dependency).
def validate_within_subproject(self, subdir, fname):
srcdir = Path(self.environment.source_dir)
builddir = Path(self.environment.build_dir)
srcdir = self.environment.source_dir
builddir = self.environment.build_dir
if isinstance(fname, P_OBJ.DependencyVariableString):
def validate_installable_file(fpath: Path) -> bool:
installablefiles: T.Set[Path] = set()
@ -3155,34 +3155,36 @@ class Interpreter(InterpreterBase, HoldableObject):
if validate_installable_file(norm):
return
def do_validate_within_subproject(norm: Path) -> None:
def do_validate_within_subproject(norm: str) -> None:
if os.path.isdir(norm):
inputtype = 'directory'
else:
inputtype = 'file'
if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents:
if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and is_parent_path(builddir, norm):
return
if srcdir not in norm.parents:
if not is_parent_path(srcdir, norm):
# Grabbing files outside the source tree is ok.
# This is for vendor stuff like:
#
# /opt/vendorsdk/src/file_with_license_restrictions.c
return
project_root = Path(srcdir, self.root_subdir)
subproject_dir = project_root / self.subproject_dir
if norm == project_root:
return
if project_root not in norm.parents:
raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.')
if subproject_dir == norm or subproject_dir in norm.parents:
raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.')
project_root = os.path.join(srcdir, self.root_subdir)
if not is_parent_path(project_root, norm):
name = os.path.basename(norm)
raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} outside current (sub)project.')
subproject_dir = os.path.join(project_root, self.subproject_dir)
if is_parent_path(subproject_dir, norm):
name = os.path.basename(norm)
raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} from a nested subproject.')
fname = os.path.join(subdir, fname)
if fname in self.validated_cache:
return
# Use os.path.abspath() to eliminate .. segments, but do not resolve symlinks
norm = Path(os.path.abspath(Path(srcdir, fname)))
norm = os.path.abspath(os.path.join(srcdir, fname))
do_validate_within_subproject(norm)
self.validated_cache.add(fname)

Loading…
Cancel
Save