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}