bootc_lib/bootc_composefs/
repo.rs1use fn_error_context::context;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5
6use ostree_ext::composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
7use ostree_ext::composefs_boot::{BootOps, bootloader::BootEntry as ComposefsBootEntry};
8use ostree_ext::composefs_oci::{
9 image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
10};
11
12use ostree_ext::container::ImageReference as OstreeExtImgRef;
13
14use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
15
16use crate::install::{RootSetup, State};
17
18pub(crate) fn open_composefs_repo(rootfs_dir: &Dir) -> Result<crate::store::ComposefsRepository> {
19 crate::store::ComposefsRepository::open_path(rootfs_dir, "composefs")
20 .context("Failed to open composefs repository")
21}
22
23pub(crate) async fn initialize_composefs_repository(
24 state: &State,
25 root_setup: &RootSetup,
26) -> Result<(String, impl FsVerityHashValue)> {
27 let rootfs_dir = &root_setup.physical_root;
28
29 crate::store::ensure_composefs_dir(rootfs_dir)?;
30
31 let repo = open_composefs_repo(rootfs_dir)?;
32
33 let OstreeExtImgRef {
34 name: image_name,
35 transport,
36 } = &state.source.imageref;
37
38 composefs_oci_pull(
40 &Arc::new(repo),
41 &format!("{transport}{image_name}"),
42 None,
43 None,
44 )
45 .await
46}
47
48pub(crate) fn get_imgref(transport: &str, image: &str) -> String {
56 let img = image.strip_prefix(":").unwrap_or(&image);
57 let transport = transport.strip_suffix(":").unwrap_or(&transport);
58
59 if transport == "registry" || transport == "docker://" {
60 format!("docker://{img}")
61 } else if transport == "docker-daemon" {
62 format!("docker-daemon:{img}")
63 } else {
64 format!("{transport}:{img}")
65 }
66}
67
68#[context("Pulling composefs repository")]
71pub(crate) async fn pull_composefs_repo(
72 transport: &String,
73 image: &String,
74) -> Result<(
75 crate::store::ComposefsRepository,
76 Vec<ComposefsBootEntry<Sha512HashValue>>,
77 Sha512HashValue,
78 crate::store::ComposefsFilesystem,
79)> {
80 let rootfs_dir = Dir::open_ambient_dir("/sysroot", ambient_authority())?;
81
82 let repo = open_composefs_repo(&rootfs_dir).context("Opening composefs repo")?;
83
84 let final_imgref = get_imgref(transport, image);
85
86 tracing::debug!("Image to pull {final_imgref}");
87
88 let (id, verity) = composefs_oci_pull(&Arc::new(repo), &final_imgref, None, None)
89 .await
90 .context("Pulling composefs repo")?;
91
92 tracing::info!("ID: {id}, Verity: {}", verity.to_hex());
93
94 let repo = open_composefs_repo(&rootfs_dir)?;
95 let mut fs: crate::store::ComposefsFilesystem =
96 create_composefs_filesystem(&repo, &id, None)
97 .context("Failed to create composefs filesystem")?;
98
99 let entries = fs.transform_for_boot(&repo)?;
100 let id = fs.commit_image(&repo, None)?;
101
102 Ok((repo, entries, id, fs))
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 const IMAGE_NAME: &str = "quay.io/example/image:latest";
110
111 #[test]
112 fn test_get_imgref_registry_transport() {
113 assert_eq!(
114 get_imgref("registry:", IMAGE_NAME),
115 format!("docker://{IMAGE_NAME}")
116 );
117 }
118
119 #[test]
120 fn test_get_imgref_containers_storage() {
121 assert_eq!(
122 get_imgref("containers-storage", IMAGE_NAME),
123 format!("containers-storage:{IMAGE_NAME}")
124 );
125
126 assert_eq!(
127 get_imgref("containers-storage:", IMAGE_NAME),
128 format!("containers-storage:{IMAGE_NAME}")
129 );
130 }
131
132 #[test]
133 fn test_get_imgref_edge_cases() {
134 assert_eq!(
135 get_imgref("registry", IMAGE_NAME),
136 format!("docker://{IMAGE_NAME}")
137 );
138 }
139
140 #[test]
141 fn test_get_imgref_docker_daemon_transport() {
142 assert_eq!(
143 get_imgref("docker-daemon", IMAGE_NAME),
144 format!("docker-daemon:{IMAGE_NAME}")
145 );
146 }
147}