Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 16 additions & 15 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::Path;

use anyhow::{anyhow, Context, Result};
use bootc_blockdev::find_parent_devices;
use bootc_mount::inspect_filesystem;
use bootc_mount::inspect_filesystem_of_dir;
use bootc_mount::tempmount::TempMount;
use camino::{Utf8Path, Utf8PathBuf};
use cap_std_ext::{
Expand Down Expand Up @@ -37,7 +37,10 @@ use crate::composefs_consts::{TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED};
use crate::parsers::bls_config::{BLSConfig, BLSConfigType};
use crate::parsers::grub_menuconfig::MenuEntry;
use crate::task::Task;
use crate::{bootc_composefs::repo::open_composefs_repo, store::ComposefsFilesystem};
use crate::{
bootc_composefs::repo::open_composefs_repo,
store::{ComposefsFilesystem, Storage},
};
use crate::{
bootc_composefs::state::{get_booted_bls, write_composefs_state},
bootloader::esp_in,
Expand Down Expand Up @@ -76,7 +79,7 @@ pub(crate) enum BootSetupType<'a> {
/// For initial setup, i.e. install to-disk
Setup((&'a RootSetup, &'a State, &'a ComposefsFilesystem)),
/// For `bootc upgrade`
Upgrade((&'a ComposefsFilesystem, &'a Host)),
Upgrade((&'a Storage, &'a ComposefsFilesystem, &'a Host)),
}

#[derive(
Expand Down Expand Up @@ -168,17 +171,15 @@ pub fn mount_esp(device: &str) -> Result<TempMount> {
TempMount::mount_dev(device, "vfat", flags, Some(c"fmask=0177,dmask=0077"))
}

pub fn get_sysroot_parent_dev() -> Result<String> {
let sysroot = Utf8PathBuf::from("/sysroot");

let fsinfo = inspect_filesystem(&sysroot)?;
pub fn get_sysroot_parent_dev(physical_root: &Dir) -> Result<String> {
let fsinfo = inspect_filesystem_of_dir(physical_root)?;
let parent_devices = find_parent_devices(&fsinfo.source)?;

let Some(parent) = parent_devices.into_iter().next() else {
anyhow::bail!("Could not find parent device for mountpoint /sysroot");
anyhow::bail!("Could not find parent device of system root");
};

return Ok(parent);
Ok(parent)
}

pub fn type1_entry_conf_file_name(sort_key: impl std::fmt::Display) -> String {
Expand Down Expand Up @@ -211,7 +212,7 @@ fn compute_boot_digest(

let digest: &[u8] = &hasher.finish().context("Finishing digest")?;

return Ok(hex::encode(digest));
Ok(hex::encode(digest))
}

/// Given the SHA256 sum of current VMlinuz + Initrd combo, find boot entry with the same SHA256Sum
Expand Down Expand Up @@ -399,8 +400,8 @@ pub(crate) fn setup_composefs_bls_boot(
)
}

BootSetupType::Upgrade((fs, host)) => {
let sysroot_parent = get_sysroot_parent_dev()?;
BootSetupType::Upgrade((storage, fs, host)) => {
let sysroot_parent = get_sysroot_parent_dev(&storage.physical_root)?;
let bootloader = host.require_composefs_booted()?.bootloader.clone();

(
Expand Down Expand Up @@ -872,9 +873,9 @@ pub(crate) fn setup_composefs_uki_boot(
)
}

BootSetupType::Upgrade((_, host)) => {
let sysroot = Utf8PathBuf::from("/sysroot");
let sysroot_parent = get_sysroot_parent_dev()?;
BootSetupType::Upgrade((storage, _, host)) => {
let sysroot = Utf8PathBuf::from("/sysroot"); // Still needed for root_path
let sysroot_parent = get_sysroot_parent_dev(&storage.physical_root)?;
let bootloader = host.require_composefs_booted()?.bootloader.clone();

(
Expand Down
35 changes: 20 additions & 15 deletions crates/lib/src/bootc_composefs/delete.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use std::{collections::HashSet, io::Write, path::Path};

use anyhow::{Context, Result};
use cap_std_ext::{
cap_std::{ambient_authority, fs::Dir},
dirext::CapStdExtDirExt,
};
use cap_std_ext::{cap_std::fs::Dir, dirext::CapStdExtDirExt};
use composefs::fsverity::Sha512HashValue;
use composefs_boot::bootloader::{EFI_ADDON_DIR_EXT, EFI_EXT};

Expand All @@ -17,7 +14,7 @@ use crate::{
gc::composefs_gc,
repo::open_composefs_repo,
rollback::{composefs_rollback, rename_exchange_user_cfg},
status::{composefs_deployment_status, get_sorted_grub_uki_boot_entries},
status::{get_composefs_status, get_sorted_grub_uki_boot_entries},
},
composefs_consts::{
COMPOSEFS_STAGED_DEPLOYMENT_FNAME, COMPOSEFS_TRANSIENT_STATE_DIR, STATE_DIR_RELATIVE,
Expand All @@ -26,6 +23,7 @@ use crate::{
parsers::bls_config::{parse_bls_config, BLSConfigType},
spec::{BootEntry, Bootloader, DeploymentEntry},
status::Slot,
store::{BootedComposefs, Storage},
};

#[fn_error_context::context("Deleting Type1 Entry {}", depl.deployment.verity)]
Expand Down Expand Up @@ -215,17 +213,20 @@ fn remove_grub_menucfg_entry(id: &str, boot_dir: &Dir, deleting_staged: bool) ->
}

#[fn_error_context::context("Deleting boot entries for deployment {}", deployment.deployment.verity)]
fn delete_depl_boot_entries(deployment: &DeploymentEntry, deleting_staged: bool) -> Result<()> {
fn delete_depl_boot_entries(
deployment: &DeploymentEntry,
physical_root: &Dir,
deleting_staged: bool,
) -> Result<()> {
match deployment.deployment.bootloader {
Bootloader::Grub => {
let boot_dir = Dir::open_ambient_dir("/sysroot/boot", ambient_authority())
.context("Opening boot dir")?;
let boot_dir = physical_root.open_dir("boot").context("Opening boot dir")?;

match deployment.deployment.boot_type {
BootType::Bls => delete_type1_entry(deployment, &boot_dir, deleting_staged),

BootType::Uki => {
let device = get_sysroot_parent_dev()?;
let device = get_sysroot_parent_dev(physical_root)?;
let (esp_part, ..) = get_esp_partition(&device)?;
let esp_mount = mount_esp(&esp_part)?;

Expand All @@ -241,7 +242,7 @@ fn delete_depl_boot_entries(deployment: &DeploymentEntry, deleting_staged: bool)
}

Bootloader::Systemd => {
let device = get_sysroot_parent_dev()?;
let device = get_sysroot_parent_dev(physical_root)?;
let (esp_part, ..) = get_esp_partition(&device)?;

let esp_mount = mount_esp(&esp_part)?;
Expand Down Expand Up @@ -316,8 +317,12 @@ pub(crate) fn delete_staged(staged: &Option<BootEntry>) -> Result<()> {
}

#[fn_error_context::context("Deleting composefs deployment {}", deployment_id)]
pub(crate) async fn delete_composefs_deployment(deployment_id: &str) -> Result<()> {
let host = composefs_deployment_status().await?;
pub(crate) async fn delete_composefs_deployment(
deployment_id: &str,
storage: &Storage,
booted_cfs: &BootedComposefs,
) -> Result<()> {
let host = get_composefs_status(storage, booted_cfs).await?;

let booted = host.require_composefs_booted()?;

Expand All @@ -344,7 +349,7 @@ pub(crate) async fn delete_composefs_deployment(deployment_id: &str) -> Result<(

// Unqueue rollback. This makes it easier to delete boot entries later on
if matches!(depl_to_del.ty, Some(Slot::Rollback)) && host.status.rollback_queued {
composefs_rollback().await?;
composefs_rollback(storage, booted_cfs).await?;
}

let kind = if depl_to_del.pinned {
Expand All @@ -357,9 +362,9 @@ pub(crate) async fn delete_composefs_deployment(deployment_id: &str) -> Result<(

tracing::info!("Deleting {kind}deployment '{deployment_id}'");

delete_depl_boot_entries(&depl_to_del, deleting_staged)?;
delete_depl_boot_entries(&depl_to_del, &storage.physical_root, deleting_staged)?;

composefs_gc().await?;
composefs_gc(storage, booted_cfs).await?;

Ok(())
}
37 changes: 21 additions & 16 deletions crates/lib/src/bootc_composefs/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ use crate::bootc_composefs::boot::{
get_esp_partition, get_sysroot_parent_dev, mount_esp, BootType,
};
use crate::bootc_composefs::rollback::{rename_exchange_bls_entries, rename_exchange_user_cfg};
use crate::bootc_composefs::status::get_composefs_status;
use crate::composefs_consts::STATE_DIR_ABS;
use crate::spec::Bootloader;
use crate::{
bootc_composefs::status::composefs_deployment_status, composefs_consts::STATE_DIR_ABS,
};
use crate::store::{BootedComposefs, Storage};
use anyhow::{Context, Result};
use bootc_initramfs_setup::{mount_composefs_image, open_dir};
use bootc_initramfs_setup::mount_composefs_image;
use bootc_mount::tempmount::TempMount;
use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
use cap_std_ext::dirext::CapStdExtDirExt;
use etc_merge::{compute_diff, merge, print_diff, traverse_etc};
use rustix::fs::{fsync, renameat, CWD};
use rustix::fs::{fsync, renameat};
use rustix::path::Arg;

use fn_error_context::context;

pub(crate) async fn get_etc_diff() -> Result<()> {
let host = composefs_deployment_status().await?;
pub(crate) async fn get_etc_diff(storage: &Storage, booted_cfs: &BootedComposefs) -> Result<()> {
let host = get_composefs_status(storage, booted_cfs).await?;
let booted_composefs = host.require_composefs_booted()?;

// Mount the booted EROFS image to get pristine etc
let sysroot = open_dir(CWD, "/sysroot").context("Opening /sysroot")?;
let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;
let sysroot_fd = storage.physical_root.reopen_as_ownedfd()?;
let composefs_fd = mount_composefs_image(&sysroot_fd, &booted_composefs.verity, false)?;

let erofs_tmp_mnt = TempMount::mount_fd(&composefs_fd)?;

Expand All @@ -41,8 +41,11 @@ pub(crate) async fn get_etc_diff() -> Result<()> {
Ok(())
}

pub(crate) async fn composefs_backend_finalize() -> Result<()> {
let host = composefs_deployment_status().await?;
pub(crate) async fn composefs_backend_finalize(
storage: &Storage,
booted_cfs: &BootedComposefs,
) -> Result<()> {
let host = get_composefs_status(storage, booted_cfs).await?;

let booted_composefs = host.require_composefs_booted()?;

Expand All @@ -56,8 +59,8 @@ pub(crate) async fn composefs_backend_finalize() -> Result<()> {
))?;

// Mount the booted EROFS image to get pristine etc
let sysroot = open_dir(CWD, "/sysroot")?;
let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;
let sysroot_fd = storage.physical_root.reopen_as_ownedfd()?;
let composefs_fd = mount_composefs_image(&sysroot_fd, &booted_composefs.verity, false)?;

let erofs_tmp_mnt = TempMount::mount_fd(&composefs_fd)?;

Expand All @@ -83,13 +86,15 @@ pub(crate) async fn composefs_backend_finalize() -> Result<()> {
// Unmount EROFS
drop(erofs_tmp_mnt);

let sysroot_parent = get_sysroot_parent_dev()?;
let sysroot_parent = get_sysroot_parent_dev(&storage.physical_root)?;
// NOTE: Assumption here that ESP will always be present
let (esp_part, ..) = get_esp_partition(&sysroot_parent)?;

let esp_mount = mount_esp(&esp_part)?;
let boot_dir = Dir::open_ambient_dir("/sysroot/boot", ambient_authority())
.context("Opening sysroot/boot")?;
let boot_dir = storage
.physical_root
.open_dir("boot")
.context("Opening boot")?;

// NOTE: Assuming here we won't have two bootloaders at the same time
match booted_composefs.bootloader {
Expand Down
30 changes: 13 additions & 17 deletions crates/lib/src/bootc_composefs/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@
//! - We delete bootloader + image but fail to delete the state/unrefenced objects etc

use anyhow::{Context, Result};
use cap_std_ext::{
cap_std::{ambient_authority, fs::Dir},
dirext::CapStdExtDirExt,
};
use cap_std_ext::{cap_std::fs::Dir, dirext::CapStdExtDirExt};
use composefs::fsverity::{FsVerityHashValue, Sha512HashValue};

use crate::{
bootc_composefs::{
boot::{get_esp_partition, get_sysroot_parent_dev, mount_esp},
delete::{delete_image, delete_staged, delete_state_dir, get_image_objects},
status::{
composefs_deployment_status, get_bootloader, get_sorted_grub_uki_boot_entries,
get_bootloader, get_composefs_status, get_sorted_grub_uki_boot_entries,
get_sorted_type1_boot_entries,
},
},
composefs_consts::{STATE_DIR_RELATIVE, USER_CFG},
spec::Bootloader,
store::{BootedComposefs, Storage},
};

#[fn_error_context::context("Listing EROFS images")]
Expand All @@ -46,13 +44,12 @@ fn list_erofs_images(sysroot: &Dir) -> Result<Vec<String>> {
/// # Returns
/// The fsverity of EROFS images corresponding to boot entries
#[fn_error_context::context("Listing bootloader entries")]
fn list_bootloader_entries() -> Result<Vec<String>> {
fn list_bootloader_entries(physical_root: &Dir) -> Result<Vec<String>> {
let bootloader = get_bootloader()?;

let entries = match bootloader {
Bootloader::Grub => {
let boot_dir = Dir::open_ambient_dir("/sysroot/boot", ambient_authority())
.context("Opening boot dir")?;
let boot_dir = physical_root.open_dir("boot").context("Opening boot dir")?;

// Grub entries are always in boot
let grub_dir = boot_dir.open_dir("grub2").context("Opening grub dir")?;
Expand All @@ -78,7 +75,7 @@ fn list_bootloader_entries() -> Result<Vec<String>> {
}

Bootloader::Systemd => {
let device = get_sysroot_parent_dev()?;
let device = get_sysroot_parent_dev(physical_root)?;
let (esp_part, ..) = get_esp_partition(&device)?;
let esp_mount = mount_esp(&esp_part)?;

Expand Down Expand Up @@ -172,14 +169,13 @@ pub(crate) fn gc_objects(sysroot: &Dir) -> Result<()> {
/// Similarly if EROFS image B1 doesn't exist, but state dir does, then delete the state dir and
/// perform GC
#[fn_error_context::context("Running composefs garbage collection")]
pub(crate) async fn composefs_gc() -> Result<()> {
let host = composefs_deployment_status().await?;
let booted_cfs = host.require_composefs_booted()?;
pub(crate) async fn composefs_gc(storage: &Storage, booted_cfs: &BootedComposefs) -> Result<()> {
let host = get_composefs_status(storage, booted_cfs).await?;
let booted_cfs_status = host.require_composefs_booted()?;

let sysroot =
Dir::open_ambient_dir("/sysroot", ambient_authority()).context("Opening sysroot")?;
let sysroot = &storage.physical_root;

let bootloader_entries = list_bootloader_entries()?;
let bootloader_entries = list_bootloader_entries(&storage.physical_root)?;
let images = list_erofs_images(&sysroot)?;

// Collect the deployments that have an image but no bootloader entry
Expand All @@ -190,10 +186,10 @@ pub(crate) async fn composefs_gc() -> Result<()> {

let staged = &host.status.staged;

if img_bootloader_diff.contains(&&booted_cfs.verity) {
if img_bootloader_diff.contains(&&booted_cfs_status.verity) {
anyhow::bail!(
"Inconsistent state. Booted entry '{}' found for cleanup",
booted_cfs.verity
booted_cfs_status.verity
)
}

Expand Down
Loading