]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Always mount parent directories under /work
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 6 Sep 2024 11:28:56 +0000 (13:28 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 9 Sep 2024 16:15:20 +0000 (18:15 +0200)
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.

mkosi/__init__.py
mkosi/qemu.py
mkosi/run.py
mkosi/tree.py

index cd8bcc8e7a54e02ee4c82df6418f1c10e1039a8f..214fa04f45ee7dda6df648b63b6957971eb23f07 100644 (file)
@@ -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,
     ]
index b98bec65126bd84b57d617b26ddb3253e188e0e5..ba66c7ed7c4c8c320ec3e130e315f3adb3e0ea56 100644 (file)
@@ -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())
index 5c85ecbd8f714d9a337a8344fd8cee850bcb9380..b7a12f8339794d32e5a78ba60ff6590f895097e7 100644 (file)
@@ -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
index d2bf3003c5463d1c1bbcc3de48dd26f096d4f34c..fb7d2963169d1979ac7013d581e4e098704ecca3 100644 (file)
@@ -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