composefs_boot/
lib.rs

1//! Boot integration for composefs filesystem images.
2//!
3//! This crate provides functionality to transform composefs filesystem images for boot
4//! scenarios by extracting boot resources, applying SELinux labels, and preparing
5//! bootloader entries. It supports both Boot Loader Specification (Type 1) entries
6//! and Unified Kernel Images (Type 2) for UEFI boot.
7
8#![deny(missing_debug_implementations)]
9
10pub mod bootloader;
11pub mod cmdline;
12pub mod os_release;
13pub mod selabel;
14pub mod uki;
15pub mod write_boot;
16
17use std::ffi::OsStr;
18
19use anyhow::Result;
20
21use composefs::{fsverity::FsVerityHashValue, repository::Repository, tree::FileSystem};
22
23use crate::bootloader::{get_boot_resources, BootEntry};
24
25/// These directories are required to exist in images.
26/// They may have content in the container, but we don't
27/// want to expose them in the final merged root.
28///
29/// # /boot
30///
31/// This is how sealed UKIs are handled; the UKI in /boot has the composefs
32/// digest, so we can't include it in the rendered image.
33///
34/// # /sysroot
35///
36/// See https://github.com/containers/composefs-rs/issues/164
37/// Basically there is only content here in ostree-container cases,
38/// and us traversing there for SELinux labeling will cause problems.
39/// The ostree-container code special cases it in a different way, but
40/// here we can just ignore it.
41const REQUIRED_TOPLEVEL_TO_EMPTY_DIRS: &[&str] = &["boot", "sysroot"];
42
43/// Trait for transforming filesystem images for boot scenarios.
44///
45/// This trait provides functionality to prepare composefs filesystem images for booting by
46/// extracting boot resources and applying necessary transformations like SELinux labeling.
47pub trait BootOps<ObjectID: FsVerityHashValue> {
48    /// Transforms a filesystem image for boot by extracting boot entries and applying SELinux labels.
49    ///
50    /// This method extracts boot resources from the filesystem, empties required top-level
51    /// directories (/boot, /sysroot), and applies SELinux security contexts.
52    ///
53    /// # Arguments
54    ///
55    /// * `repo` - The composefs repository containing filesystem objects
56    ///
57    /// # Returns
58    ///
59    /// A vector of boot entries extracted from the filesystem (Type 1 BLS entries, Type 2 UKIs, etc.)
60    fn transform_for_boot(
61        &mut self,
62        repo: &Repository<ObjectID>,
63    ) -> Result<Vec<BootEntry<ObjectID>>>;
64}
65
66impl<ObjectID: FsVerityHashValue> BootOps<ObjectID> for FileSystem<ObjectID> {
67    fn transform_for_boot(
68        &mut self,
69        repo: &Repository<ObjectID>,
70    ) -> Result<Vec<BootEntry<ObjectID>>> {
71        let boot_entries = get_boot_resources(self, repo)?;
72
73        // Get /usr's mtime to use as the canonical mtime for emptied directories.
74        // This matches how we handle the root directory in copy_root_metadata_from_usr().
75        let usr_mtime = self.root.get_directory(OsStr::new("usr"))?.stat.st_mtim_sec;
76
77        for d in REQUIRED_TOPLEVEL_TO_EMPTY_DIRS {
78            let d = self.root.get_directory_mut(d.as_ref())?;
79            d.stat.st_mtim_sec = usr_mtime;
80            d.clear();
81        }
82
83        selabel::selabel(self, repo)?;
84
85        Ok(boot_entries)
86    }
87}