From: Daan De Meyer Date: Fri, 6 Sep 2024 11:28:56 +0000 (+0200) Subject: Always mount parent directories under /work X-Git-Tag: v25~309^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b966beab2ab7137d7e8d979fa0dcecc9c5a08b42;p=thirdparty%2Fmkosi.git Always mount parent directories under /work When we do something where we need to mount the parent directory because we're creating or deleting a file or directory, let's operate under /work in the sandbox. Otherwise there's a good chance we'll end up interfering with regular mounts in the sandbox e.g /var/tmp when the workspace directory is in /var/tmp. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index cd8bcc8e7..214fa04f4 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -93,6 +93,7 @@ from mkosi.run import ( finalize_passwd_mounts, fork_and_wait, run, + workdir, ) from mkosi.sandbox import ( CLONE_NEWNS, @@ -996,12 +997,15 @@ def install_tree( extract_tar(src, t, sandbox=config.sandbox) elif src.suffix == ".raw": run( - ["systemd-dissect", "--copy-from", src, "/", t], + ["systemd-dissect", "--copy-from", workdir(src), "/", workdir(t)], sandbox=config.sandbox( binary="systemd-dissect", devices=True, network=True, - options=["--ro-bind", src, src, "--bind", t.parent, t.parent], + options=[ + "--ro-bind", src, workdir(src), + "--bind", t.parent, workdir(t.parent), + ], ), ) else: @@ -1435,13 +1439,13 @@ def build_uki( *(["--cmdline", f"@{context.workspace / 'cmdline'}"] if cmdline else []), "--os-release", f"@{context.root / 'usr/lib/os-release'}", "--stub", stub, - "--output", output, + "--output", workdir(output), "--efi-arch", arch, "--uname", kver, ] options: list[PathString] = [ - "--bind", output.parent, output.parent, + "--bind", output.parent, workdir(output.parent), "--ro-bind", context.workspace / "cmdline", context.workspace / "cmdline", "--ro-bind", context.root / "usr/lib/os-release", context.root / "usr/lib/os-release", "--ro-bind", stub, stub, @@ -2940,13 +2944,13 @@ def make_extension_image(context: Context, output: Path) -> None: "--empty=create", "--size=auto", "--definitions", r, - output, + workdir(output), ] options: list[PathString] = [ # Make sure we're root so that the mkfs tools invoked by systemd-repart think the files that go # into the disk image are owned by root. "--become-root", - "--bind", output.parent, output.parent, + "--bind", output.parent, workdir(output.parent), "--ro-bind", context.root, "/buildroot", "--ro-bind", r, r, ] diff --git a/mkosi/qemu.py b/mkosi/qemu.py index b98bec651..ba66c7ed7 100644 --- a/mkosi/qemu.py +++ b/mkosi/qemu.py @@ -41,7 +41,7 @@ from mkosi.config import ( ) from mkosi.log import ARG_DEBUG, die from mkosi.partition import finalize_root, find_partitions -from mkosi.run import SD_LISTEN_FDS_START, AsyncioThread, find_binary, fork_and_wait, run, spawn +from mkosi.run import SD_LISTEN_FDS_START, AsyncioThread, find_binary, fork_and_wait, run, spawn, workdir from mkosi.tree import copy_tree, rmtree from mkosi.types import PathString from mkosi.user import INVOKING_USER, become_root_in_subuid_range, become_root_in_subuid_range_cmd @@ -496,14 +496,14 @@ def start_journal_remote(config: Config, sockfd: int) -> Iterator[None]: with spawn( [ bin, - "--output", config.forward_journal, + "--output", workdir(config.forward_journal), "--split-mode", "none" if config.forward_journal.suffix == ".journal" else "host", ], pass_fds=(sockfd,), sandbox=config.sandbox( binary=bin, options=[ - "--bind", config.forward_journal.parent, config.forward_journal.parent, + "--bind", config.forward_journal.parent, workdir(config.forward_journal.parent), "--ro-bind", f.name, "/etc/systemd/journal-remote.conf", ], setup=scope, @@ -1009,13 +1009,16 @@ def run_qemu(args: Args, config: Config) -> None: "--empty=create", "--size=auto", "--sector-size=2048", - "--copy-from", src, - fname, + "--copy-from", workdir(src), + workdir(fname), ], sandbox=config.sandbox( binary="systemd-repart", vartmp=True, - options=["--bind", fname.parent, fname.parent, "--ro-bind", src, src], + options=[ + "--bind", fname.parent, workdir(fname.parent), + "--ro-bind", src, workdir(src), + ], ), ) stack.callback(lambda: fname.unlink()) diff --git a/mkosi/run.py b/mkosi/run.py index 5c85ecbd8..b7a12f833 100644 --- a/mkosi/run.py +++ b/mkosi/run.py @@ -24,6 +24,7 @@ from typing import Any, Callable, NoReturn, Optional, Protocol import mkosi.sandbox from mkosi.log import ARG_DEBUG, ARG_DEBUG_SHELL, die +from mkosi.sandbox import joinpath from mkosi.types import _FILE, CompletedProcess, PathString, Popen from mkosi.util import flatten, one_zero @@ -400,6 +401,11 @@ def nosandbox( return contextlib.nullcontext([]) +def workdir(path: Path, sandbox: Optional[SandboxProtocol] = None) -> str: + subdir = "/" if sandbox and sandbox == nosandbox else "/work" + return joinpath(subdir, str(path)) + + def finalize_passwd_mounts(root: PathString) -> list[PathString]: """ If passwd or a related file exists in the apivfs directory, bind mount it over the host files while we diff --git a/mkosi/tree.py b/mkosi/tree.py index d2bf3003c..fb7d29631 100644 --- a/mkosi/tree.py +++ b/mkosi/tree.py @@ -11,7 +11,7 @@ from pathlib import Path from mkosi.config import ConfigFeature from mkosi.log import ARG_DEBUG, die -from mkosi.run import SandboxProtocol, nosandbox, run +from mkosi.run import SandboxProtocol, nosandbox, run, workdir from mkosi.sandbox import BTRFS_SUPER_MAGIC, statfs from mkosi.types import PathString from mkosi.util import flatten @@ -38,6 +38,8 @@ def make_tree( use_subvolumes: ConfigFeature = ConfigFeature.disabled, sandbox: SandboxProtocol = nosandbox, ) -> Path: + path = path.absolute() + if statfs(str(path.parent)) != BTRFS_SUPER_MAGIC: if use_subvolumes == ConfigFeature.enabled: die(f"Subvolumes requested but {path} is not located on a btrfs filesystem") @@ -46,9 +48,11 @@ def make_tree( return path if use_subvolumes != ConfigFeature.disabled: - result = run(["btrfs", "subvolume", "create", path], - sandbox=sandbox(binary="btrfs", options=["--bind", path.parent, path.parent]), - check=use_subvolumes == ConfigFeature.enabled).returncode + result = run( + ["btrfs", "subvolume", "create", workdir(path, sandbox)], + sandbox=sandbox(binary="btrfs", options=["--bind", path.parent, workdir(path.parent, sandbox)]), + check=use_subvolumes == ConfigFeature.enabled + ).returncode else: result = 1 @@ -82,7 +86,13 @@ def copy_tree( use_subvolumes: ConfigFeature = ConfigFeature.disabled, sandbox: SandboxProtocol = nosandbox, ) -> Path: - options: list[PathString] = ["--ro-bind", src, src, "--bind", dst.parent, dst.parent] + src = src.absolute() + dst = dst.absolute() + + options: list[PathString] = [ + "--ro-bind", src, workdir(src, sandbox), + "--bind", dst.parent, workdir(dst.parent, sandbox), + ] def copy() -> None: cmdline: list[PathString] = [ @@ -92,7 +102,7 @@ def copy_tree( f"--preserve=mode,links{',timestamps,ownership,xattr' if preserve else ''}", "--reflink=auto", "--copy-contents", - src, dst, + workdir(src, sandbox), workdir(dst, sandbox), ] if dst.exists() and dst.is_dir() and any(dst.iterdir()) and cp_version(sandbox=sandbox) >= "9.5": @@ -127,7 +137,7 @@ def copy_tree( dst.rmdir() result = run( - ["btrfs", "subvolume", "snapshot", src, dst], + ["btrfs", "subvolume", "snapshot", workdir(src, sandbox), workdir(dst, sandbox)], check=use_subvolumes == ConfigFeature.enabled, sandbox=sandbox(binary="btrfs", options=options), ).returncode @@ -147,19 +157,29 @@ def rmtree(*paths: Path, sandbox: SandboxProtocol = nosandbox) -> None: if not paths: return + paths = tuple(p.absolute() for p in paths) + if subvolumes := sorted({p for p in paths if p.exists() and is_subvolume(p)}): # Silence and ignore failures since when not running as root, this will fail with a permission error unless the # btrfs filesystem is mounted with user_subvol_rm_allowed. - run(["btrfs", "subvolume", "delete", *subvolumes], + run(["btrfs", "subvolume", "delete", *(workdir(p, sandbox) for p in subvolumes)], check=False, - sandbox=sandbox(binary="btrfs", options=flatten(("--bind", p.parent, p.parent) for p in subvolumes)), + sandbox=sandbox( + binary="btrfs", + options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in subvolumes) + ), stdout=subprocess.DEVNULL if not ARG_DEBUG.get() else None, stderr=subprocess.DEVNULL if not ARG_DEBUG.get() else None) filtered = sorted({p for p in paths if p.exists() or p.is_symlink()}) if filtered: - run(["rm", "-rf", "--", *filtered], - sandbox=sandbox(binary="rm", options=flatten(("--bind", p.parent, p.parent) for p in filtered))) + run( + ["rm", "-rf", "--", *(workdir(p, sandbox) for p in filtered)], + sandbox=sandbox( + binary="rm", + options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in filtered), + ), + ) def move_tree( @@ -169,6 +189,9 @@ def move_tree( use_subvolumes: ConfigFeature = ConfigFeature.disabled, sandbox: SandboxProtocol = nosandbox ) -> Path: + src = src.absolute() + dst = dst.absolute() + if src == dst: return dst