use std::{
fs::{create_dir_all, write},
path::Path,
};
use anyhow::{Context, Result, ensure};
use composefs::{fsverity::FsVerityHashValue, repository::Repository};
use crate::{
bootloader::{BootEntry, Type1Entry, Type2Entry},
cmdline::ComposefsCmdline,
uki,
};
pub fn write_t1_simple<ObjectID: FsVerityHashValue>(
mut t1: Type1Entry<ObjectID>,
bootdir: &Path,
boot_subdir: Option<&str>,
karg: &ComposefsCmdline<ObjectID>,
cmdline_extra: &[&str],
repo: &Repository<ObjectID>,
) -> Result<()> {
let bootdir = if let Some(subdir) = boot_subdir {
let subdir_path = Path::new(subdir);
bootdir.join(subdir_path.strip_prefix("/").unwrap_or(subdir_path))
} else {
bootdir.to_path_buf()
};
let karg_str = karg.to_cmdline_arg();
t1.entry.adjust_cmdline(Some(&karg_str), cmdline_extra);
for (filename, file) in &t1.files {
let pathname = Path::new(filename.as_ref());
let file_path = bootdir.join(pathname.strip_prefix(Path::new("/"))?);
create_dir_all(file_path.parent().unwrap())?;
write(file_path, composefs::fs::read_file(file, repo)?)?;
}
let loader_entries = bootdir.join("loader/entries");
create_dir_all(&loader_entries)?;
let entry = loader_entries.join(t1.filename.as_ref());
let entry_content = t1.entry.lines.join("\n") + "\n";
write(entry, entry_content)?;
Ok(())
}
pub fn write_t2_simple<ObjectID: FsVerityHashValue>(
t2: Type2Entry<ObjectID>,
bootdir: &Path,
acceptable_digests: &[&ObjectID],
repo: &Repository<ObjectID>,
) -> Result<()> {
let efi_linux = bootdir.join("EFI/Linux");
create_dir_all(&efi_linux)?;
let filename = efi_linux.join(t2.file_path);
let content = composefs::fs::read_file(&t2.file, repo)?;
let cmdline = uki::get_cmdline(&content)?;
let parsed = ComposefsCmdline::<ObjectID>::from_cmdline(cmdline)
.with_context(|| format!("parsing UKI .cmdline section: {cmdline:?}"))?
.ok_or_else(|| {
anyhow::anyhow!(
"UKI .cmdline has no composefs karg (composefs= or composefs.digest=): {cmdline:?}"
)
})?;
parsed.validate_digest(acceptable_digests.iter().copied())?;
write(filename, content)?;
Ok(())
}
pub fn write_boot_simple<ObjectID: FsVerityHashValue>(
repo: &Repository<ObjectID>,
entry: BootEntry<ObjectID>,
karg: &ComposefsCmdline<ObjectID>,
boot_partition: &Path,
boot_subdir: Option<&str>,
entry_id: Option<&str>,
cmdline_extra: &[&str],
) -> Result<()> {
match entry {
BootEntry::Type1(mut t1) => {
if let Some(name) = entry_id {
t1.relocate(boot_subdir, name);
}
write_t1_simple(t1, boot_partition, boot_subdir, karg, cmdline_extra, repo)?;
}
BootEntry::Type2(mut t2) => {
if let Some(name) = entry_id {
t2.rename(name);
}
ensure!(cmdline_extra.is_empty(), "Can't add --cmdline args to UKIs");
write_t2_simple(t2, boot_partition, &[karg.digest()], repo)?;
}
BootEntry::UsrLibModulesVmLinuz(entry) => {
let mut t1 = entry.into_type1(entry_id)?;
if let Some(name) = entry_id {
t1.relocate(boot_subdir, name);
}
write_t1_simple(t1, boot_partition, boot_subdir, karg, cmdline_extra, repo)?;
}
};
Ok(())
}