diff --git a/nixos/modules/programs/nix-required-mounts.nix b/nixos/modules/programs/nix-required-mounts.nix index c05c2016a6be..611c065815e1 100644 --- a/nixos/modules/programs/nix-required-mounts.nix +++ b/nixos/modules/programs/nix-required-mounts.nix @@ -5,16 +5,23 @@ let package = pkgs.nix-required-mounts; overridenPackage = package.override { inherit (cfg) allowedPatterns; }; + Mount = with lib; types.submodule { + options.host = mkOption { type = types.str; description = "Host path to mount"; }; + options.guest = mkOption { + type = types.str; + description = "Location in the sandbox to mount the host path at"; + }; + }; Pattern = with lib.types; - submodule ({ config, name, ... }: { + types.submodule ({ config, name, ... }: { options.onFeatures = lib.mkOption { - type = listOf str; + type = listOf types.str; description = "Which requiredSystemFeatures should trigger relaxation of the sandbox"; default = [ name ]; }; options.paths = lib.mkOption { - type = listOf path; + type = listOf (oneOf [ path Mount ]); description = "A list of glob patterns, indicating which paths to expose to the sandbox"; }; diff --git a/nixos/tests/nix-required-mounts/default.nix b/nixos/tests/nix-required-mounts/default.nix index 4550e6ac50a2..38f94bf6fd98 100644 --- a/nixos/tests/nix-required-mounts/default.nix +++ b/nixos/tests/nix-required-mounts/default.nix @@ -19,10 +19,19 @@ in programs.nix-required-mounts.enable = true; programs.nix-required-mounts.allowedPatterns.supported-feature = { onFeatures = [ "supported-feature" ]; - paths = [ "/supported-feature-files" ]; + paths = [ + "/supported-feature-files" + { + host = "/usr/lib/imaginary-fhs-drivers"; + guest = "/run/opengl-driver/lib"; + } + ]; }; users.users.person.isNormalUser = true; - virtualisation.fileSystems."/supported-feature-files".fsType = "tmpfs"; + systemd.tmpfiles.rules = [ + "d /supported-feature-files 0755 person users -" + "f /usr/lib/imaginary-fhs-drivers/libcuda.so 0444 root root -" + ]; }; testScript = '' import shlex diff --git a/nixos/tests/nix-required-mounts/test-require-feature.nix b/nixos/tests/nix-required-mounts/test-require-feature.nix index ddfd068b87fc..061b59e1628a 100644 --- a/nixos/tests/nix-required-mounts/test-require-feature.nix +++ b/nixos/tests/nix-required-mounts/test-require-feature.nix @@ -4,9 +4,13 @@ pkgs.runCommandNoCC "${feature}-present" { requiredSystemFeatures = [ feature ]; } '' - if [[ -e /${feature}-files ]]; then - touch $out - else + if [[ ! -e /${feature}-files ]]; then echo "The host declares ${feature} support, but doesn't expose /${feature}-files" >&2 + exit 1 fi + if [[ ! -f /run/opengl-driver/lib/libcuda.so ]] ; then + echo "The host declares ${feature} support, but it the hook fails to handle the hostPath != guestPath cases" >&2 + exit 1 + fi + touch $out '' diff --git a/pkgs/by-name/ni/nix-required-mounts/nix_required_mounts.py b/pkgs/by-name/ni/nix-required-mounts/nix_required_mounts.py index 1d263dcd1028..5edf61ff115b 100644 --- a/pkgs/by-name/ni/nix-required-mounts/nix_required_mounts.py +++ b/pkgs/by-name/ni/nix-required-mounts/nix_required_mounts.py @@ -8,12 +8,20 @@ from argparse import ArgumentParser from itertools import chain from pathlib import Path from sys import stderr -from typing import Dict, List, TypedDict +from typing import Dict, List, Tuple, TypeAlias, TypedDict + +Glob: TypeAlias = str +PathString: TypeAlias = str + + +class Mount(TypedDict): + host: PathString + guest: PathString class Pattern(TypedDict): onFeatures: List[str] - paths: List[str] # List of glob patterns + paths: List[Glob | Mount] class HookConfig(TypedDict): @@ -106,12 +114,21 @@ def entrypoint(): features = get_strings(drv_env, "requiredSystemFeatures") features = list(filter(known_features.__contains__, features)) - patterns = list( + patterns: List[PathString | Mount] = list( chain.from_iterable(allowed_patterns[f]["paths"] for f in features) ) # noqa: E501 - roots = sorted( - set(Path(path) for pattern in patterns for path in glob.glob(pattern)) + # TODO: Would it make sense to preserve the original order instead? + roots: List[Tuple[PathString, PathString]] = sorted( + set( + mnt + for pattern in patterns + for mnt in ( + ((path, path) for path in glob.glob(pattern)) + if isinstance(pattern, PathString) + else [(pattern["guest"], pattern["host"])] + ) + ) ) # the pre-build-hook command @@ -121,8 +138,7 @@ def entrypoint(): print("extra-sandbox-paths") # arguments, one per line - for p in roots: - guest_path, host_path = p, p + for guest_path, host_path in roots: print(f"{guest_path}={host_path}") # terminated by an empty line diff --git a/pkgs/by-name/ni/nix-required-mounts/package.nix b/pkgs/by-name/ni/nix-required-mounts/package.nix index 42fa3ff1d90d..a17962183de1 100644 --- a/pkgs/by-name/ni/nix-required-mounts/package.nix +++ b/pkgs/by-name/ni/nix-required-mounts/package.nix @@ -3,7 +3,7 @@ , allowedPatterns ? rec { # This config is just an example. # When the hook observes either of the following requiredSystemFeatures: - nvidia-gpu.onFeatures = [ "gpu" "opengl" "vulkan" "cuda" ]; + nvidia-gpu.onFeatures = [ "gpu" "nvidia-gpu" "opengl" "cuda" ]; # It exposes these paths in the sandbox: nvidia-gpu.paths = [ # Note that mounting /run/opengl-driver/lib actually isn't sufficient,