]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add RepartOffline= option
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 11 Dec 2023 09:17:19 +0000 (10:17 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 11 Dec 2023 10:29:01 +0000 (11:29 +0100)
Instead of auto-detecting all cases where --offline=no has to be
used with systemd-repart, let's allow configuring it via an option
so that if we discover any new cases, users can easily disable
offline mode themselves.

NEWS.md
mkosi/__init__.py
mkosi/config.py
mkosi/resources/mkosi.md
tests/test_json.py

diff --git a/NEWS.md b/NEWS.md
index 80a290d1ab6b8746123242395210c30552783051..d9f487eee79b43bd7bbb38f2d198b2b237423454 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -2,6 +2,10 @@
 
 ## v20
 
+- We don't automatically set `--offline=no` anymore when we detect the
+  `Subvolumes=` setting is used in a `systemd-repart` partition
+  definition file. Instead, use the new `RepartOffline` option to
+  explicitly disable running `systemd-repart` in offline mode.
 - During the image build we now install UKIs/kernels/initrds to `/boot`
   instead of `/efi`. While this will generally not be noticeable, users
   with custom systemd-repart ESP partition definitions will need to add
index 9200f2196812dc45e7a00f87a0bff4d7cde2b418..a5c02695e4ff6b5c62774839c75bfaf9f1f320b3 100644 (file)
@@ -43,6 +43,7 @@ from mkosi.config import (
     format_tree,
     parse_config,
     summary,
+    yes_no,
 )
 from mkosi.distributions import Distribution
 from mkosi.installer import clean_package_manager_metadata, package_manager_scripts
@@ -2203,7 +2204,7 @@ def make_image(
         "--dry-run=no",
         "--json=pretty",
         "--no-pager",
-        "--offline=yes",
+        f"--offline={yes_no(state.config.repart_offline)}",
         "--seed", str(state.config.seed) if state.config.seed else "random",
         state.staging / state.config.output_with_format,
     ]
@@ -2227,15 +2228,8 @@ def make_image(
     if state.config.sector_size:
         cmdline += ["--sector-size", str(state.config.sector_size)]
 
-    if definitions:
-        for d in definitions:
-            cmdline += ["--definitions", d]
-
-        # Subvolumes= only works with --offline=no.
-        grep = run(["grep", "--recursive", "--include=*.conf", "Subvolumes=", *definitions],
-                   stdout=subprocess.DEVNULL, check=False)
-        if grep.returncode == 0:
-            cmdline += ["--offline=no"]
+    for d in definitions:
+        cmdline += ["--definitions", d]
 
     env = {
         option: value
@@ -3038,6 +3032,10 @@ def run_verb(args: MkosiArgs, images: Sequence[MkosiConfig]) -> None:
 
         die(f"mkosi {config.minimum_version} or newer is required to build this configuration (found {__version__})")
 
+    for config in images:
+        if not config.repart_offline and os.getuid() != 0:
+            die(f"Must be root to build {config.name()} image configured with RepartOffline=no")
+
     for config in images:
         check_workspace_directory(config)
 
index 015c3203ae91275ee03de9f8f0ceda4f67a2ebbd..77127d90f91511d1d35ec43607902be1f6114b53 100644 (file)
@@ -910,6 +910,7 @@ class MkosiConfig:
     split_artifacts: bool
     repart_dirs: list[Path]
     sector_size: Optional[int]
+    repart_offline: bool
     overlay: bool
     use_subvolumes: ConfigFeature
     seed: Optional[uuid.UUID]
@@ -1459,6 +1460,13 @@ SETTINGS = (
         parse=config_parse_sector_size,
         help="Set the disk image sector size",
     ),
+    MkosiConfigSetting(
+        dest="repart_offline",
+        section="Output",
+        parse=config_parse_boolean,
+        help="Build disk images without using loopback devices",
+        default=True,
+    ),
     MkosiConfigSetting(
         dest="overlay",
         metavar="BOOL",
@@ -3006,6 +3014,7 @@ def summary(config: MkosiConfig) -> str:
                     Split Artifacts: {yes_no(config.split_artifacts)}
                  Repart Directories: {line_join_list(config.repart_dirs)}
                         Sector Size: {none_to_default(config.sector_size)}
+                     Repart Offline: {yes_no(config.repart_offline)}
                             Overlay: {yes_no(config.overlay)}
                      Use Subvolumes: {yes_no_auto(config.use_subvolumes)}
                                Seed: {none_to_random(config.seed)}
index 19c489bf37773f66b0bb2116d0c80afd3b2d362a..1b79b17f1ca8df4c3c253276ee9667f0da05e7e2 100644 (file)
@@ -722,6 +722,26 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
 : Override the default sector size that systemd-repart uses when building a disk
   image.
 
+`Offline=`, `--offline=`
+
+: Specifies whether to build disk images using loopback devices. Enabled
+  by default. When enabled, `systemd-repart` will not use loopback
+  devices to build disk images. When disabled, `systemd-repart` will
+  always use loopback devices to build disk images.
+
+: Note that when using `Offline=no` mkosi cannot run unprivileged and
+  the image build has to be done as the root user outside of any
+  containers and with loopback devices available on the host system.
+
+: There are currently two known scenarios where `Offline=no` has to be
+  used. The first is when using `Subvolumes=` in a repart partition
+  definition file, as subvolumes cannot be created without using
+  loopback devices. The second is when creating a system with SELinux
+  and an XFS root partition. Because `mkfs.xfs` does not support
+  populating an XFS filesystem with extended attributes, loopback
+  devices have to be used to ensure the SELinux extended attributes end
+  up in the generated XFS filesystem.
+
 `Overlay=`, `--overlay`
 
 : When used together with `BaseTrees=`, the output will consist only out of
index 64736e1885fef13e072750d763e3a1c8bf04b6d8..730695e5a0fc2de28ee5c4e378378fcc26d7d976 100644 (file)
@@ -223,6 +223,7 @@ def test_config() -> None:
                 "all"
             ],
             "RepartDirectories": [],
+            "RepartOffline": true,
             "Repositories": [],
             "RepositoryKeyCheck": false,
             "RootPassword": [
@@ -362,6 +363,7 @@ def test_config() -> None:
         remove_files = [],
         remove_packages = ["all"],
         repart_dirs = [],
+        repart_offline = True,
         repositories = [],
         repository_key_check = False,
         root_password = ("test1234", False),