bootc_lib/install/
osbuild.rs

1//! # Helper APIs for interacting with bootc-image-builder
2//!
3//! See <https://github.com/osbuild/bootc-image-builder>
4//!
5
6use std::process::Command;
7
8use anyhow::Result;
9use bootc_utils::CommandRunExt as _;
10use camino::Utf8Path;
11use cap_std_ext::{cap_std::fs::Dir, cmdext::CapStdExtCommandExt};
12use fn_error_context::context;
13
14/// Handle /etc/containers readonly mount.
15///
16/// Ufortunately today podman requires that /etc be writable for
17/// `/etc/containers/networks`. bib today creates this as a readonly mount:
18/// https://github.com/osbuild/osbuild/blob/4edbe227d41c767441b9bf4390398afc6dc8f901/osbuild/buildroot.py#L243
19///
20/// Work around that by adding a transient, writable overlayfs.
21fn adjust_etc_containers(tempdir: &Dir) -> Result<()> {
22    let etc_containers = Utf8Path::new("/etc/containers");
23    // If there's no /etc/containers, nothing to do
24    if !etc_containers.try_exists()? {
25        return Ok(());
26    }
27    if rustix::fs::access(etc_containers.as_std_path(), rustix::fs::Access::WRITE_OK).is_ok() {
28        return Ok(());
29    }
30    // Create dirs for the overlayfs upper and work in the install-global tmpdir.
31    tempdir.create_dir_all("etc-ovl/upper")?;
32    tempdir.create_dir("etc-ovl/work")?;
33    let opts = format!("lowerdir={etc_containers},workdir=etc-ovl/work,upperdir=etc-ovl/upper");
34    Command::new("mount")
35        .log_debug()
36        .args(["-t", "overlay", "overlay", "-o", opts.as_str()])
37        .arg(etc_containers)
38        .cwd_dir(tempdir.try_clone()?)
39        .run_capture_stderr()?;
40    Ok(())
41}
42
43/// osbuild mounts the host's /var/lib/containers at /run/osbuild/containers; mount
44/// it back to /var/lib/containers where the default container stack expects to find it.
45fn propagate_run_osbuild_containers(root: &Dir) -> Result<()> {
46    let osbuild_run_containers = Utf8Path::new("run/osbuild/containers");
47    // If we're not apparently running under osbuild, then we no-op.
48    if !root.try_exists(osbuild_run_containers)? {
49        return Ok(());
50    }
51    // If we do seem to have a valid container store though, use that
52    if crate::podman::storage_exists_default(root)? {
53        return Ok(());
54    }
55    let relative_storage = Utf8Path::new(crate::podman::CONTAINER_STORAGE.trim_start_matches('/'));
56    root.create_dir_all(relative_storage)?;
57    Command::new("mount")
58        .log_debug()
59        .arg("--rbind")
60        .args([osbuild_run_containers, relative_storage])
61        .cwd_dir(root.try_clone()?)
62        .run_capture_stderr()?;
63    Ok(())
64}
65
66/// bootc-image-builder today does a few things that we need to
67/// deal with.
68#[context("bootc-image-builder adjustments")]
69pub(crate) fn adjust_for_bootc_image_builder(root: &Dir, tempdir: &Dir) -> Result<()> {
70    adjust_etc_containers(tempdir)?;
71    propagate_run_osbuild_containers(root)?;
72    Ok(())
73}