Skip to main content

bootc_lib/bootc_composefs/
image.rs

1//! Detecting which backend a container image's root filesystem is built for.
2
3use anyhow::Result;
4use cap_std_ext::cap_std::fs::Dir;
5use cap_std_ext::dirext::CapStdExtDirExt as _;
6
7/// The setup-root configuration, relative to the root directory.
8pub(crate) fn setup_root_conf_path() -> &'static str {
9    bootc_initramfs_setup::SETUP_ROOT_CONF_PATH.trim_start_matches('/')
10}
11
12/// Whether the image is intended to be deployed with the composefs
13/// backend, which is signaled by the presence of a setup-root configuration
14/// file (even if empty).
15pub(crate) fn is_composefs_native(root: &Dir) -> Result<bool> {
16    Ok(root
17        .symlink_metadata_optional(setup_root_conf_path())?
18        .is_some())
19}
20
21/// Whether `bootc install` should default to the composefs backend for this
22/// root: it is composefs-native, and it has no ostree `prepare-root.conf`
23/// (`has_ostree_prepareroot`, which the caller loads anyway), without which
24/// it can't be installed with the ostree backend. An image that has both
25/// configurations still defaults to ostree.
26pub(crate) fn defaults_to_composefs_backend(
27    root: &Dir,
28    has_ostree_prepareroot: bool,
29) -> Result<bool> {
30    Ok(!has_ostree_prepareroot && is_composefs_native(root)?)
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use camino::Utf8Path;
37    use ostree_ext::ostree_prepareroot;
38
39    const OSTREE_PREPAREROOT: &str = "usr/lib/ostree/prepare-root.conf";
40    const OSTREE_PREPAREROOT_ETC: &str = "etc/ostree/prepare-root.conf";
41
42    #[test]
43    fn test_defaults_to_composefs_backend() -> Result<()> {
44        let setup_root = setup_root_conf_path();
45        // (files present in the image) => (composefs-native, defaults to composefs)
46        let cases: &[(&[&str], bool, bool)] = &[
47            (&[], false, false),
48            (&[setup_root], true, true),
49            (&[OSTREE_PREPAREROOT], false, false),
50            (&[OSTREE_PREPAREROOT_ETC], false, false),
51            (&[setup_root, OSTREE_PREPAREROOT], true, false),
52            (&[setup_root, OSTREE_PREPAREROOT_ETC], true, false),
53        ];
54        for &(files, native, composefs) in cases {
55            let td = cap_std_ext::cap_tempfile::tempdir(cap_std_ext::cap_std::ambient_authority())?;
56            for f in files {
57                let f = Utf8Path::new(f);
58                td.create_dir_all(f.parent().unwrap())?;
59                // Both files are signals even if empty
60                td.write(f, "")?;
61            }
62            assert_eq!(is_composefs_native(&td)?, native, "{files:?}");
63            let has_ostree = ostree_prepareroot::load_config_from_root(&td)?.is_some();
64            assert_eq!(
65                defaults_to_composefs_backend(&td, has_ostree)?,
66                composefs,
67                "{files:?}"
68            );
69        }
70        Ok(())
71    }
72}