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
28 changes: 8 additions & 20 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ use crate::bootc_composefs::status::get_sorted_uki_boot_entries;
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::spec::ImageReference;
use crate::task::Task;
use crate::{bootc_composefs::repo::open_composefs_repo, store::ComposefsFilesystem};
use crate::{
Expand Down Expand Up @@ -369,14 +368,11 @@ pub(crate) fn setup_composefs_bls_boot(
// root_setup.kargs has [root=UUID=<UUID>, "rw"]
let mut cmdline_options = String::from(root_setup.kargs.join(" "));

match &state.composefs_options {
Some(opt) if opt.insecure => {
cmdline_options.push_str(&format!(" {COMPOSEFS_CMDLINE}=?{id_hex}"));
}
None | Some(..) => {
cmdline_options.push_str(&format!(" {COMPOSEFS_CMDLINE}={id_hex}"));
}
};
if state.composefs_options.insecure {
cmdline_options.push_str(&format!(" {COMPOSEFS_CMDLINE}=?{id_hex}"));
} else {
cmdline_options.push_str(&format!(" {COMPOSEFS_CMDLINE}={id_hex}"));
}

// Locate ESP partition device
let esp_part = esp_in(&root_setup.device_info)?;
Expand Down Expand Up @@ -835,18 +831,14 @@ pub(crate) fn setup_composefs_uki_boot(
}
}

let Some(cfs_opts) = &state.composefs_options else {
anyhow::bail!("ComposeFS options not found");
};

let esp_part = esp_in(&root_setup.device_info)?;

(
root_setup.physical_root_path.clone(),
esp_part.node.clone(),
state.detected_bootloader.clone(),
cfs_opts.insecure,
cfs_opts.uki_addon.as_ref(),
state.composefs_options.insecure,
state.composefs_options.uki_addon.as_ref(),
)
}

Expand Down Expand Up @@ -1001,11 +993,7 @@ pub(crate) fn setup_composefs_boot(
write_composefs_state(
&root_setup.physical_root_path,
id,
&ImageReference {
image: state.source.imageref.name.clone(),
transport: state.source.imageref.transport.to_string(),
signature: None,
},
&crate::spec::ImageReference::from(state.target_imgref.clone()),
false,
boot_type,
boot_digest,
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/bootc_composefs/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(crate) async fn get_etc_diff() -> Result<()> {
Ok(())
}

pub(crate) async fn composefs_native_finalize() -> Result<()> {
pub(crate) async fn composefs_backend_finalize() -> Result<()> {
let host = composefs_deployment_status().await?;

let booted_composefs = host.require_composefs_booted()?;
Expand Down
1 change: 1 addition & 0 deletions crates/lib/src/bootc_composefs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) mod boot;
pub(crate) mod finalize;
pub(crate) mod repo;
pub(crate) mod rollback;
pub(crate) mod service;
pub(crate) mod state;
pub(crate) mod status;
pub(crate) mod switch;
Expand Down
4 changes: 2 additions & 2 deletions crates/lib/src/bootc_composefs/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub(crate) async fn pull_composefs_repo(
)> {
let rootfs_dir = Dir::open_ambient_dir("/sysroot", ambient_authority())?;

let repo = open_composefs_repo(&rootfs_dir).context("Opening compoesfs repo")?;
let repo = open_composefs_repo(&rootfs_dir).context("Opening composefs repo")?;

let final_imgref = get_imgref(transport, image);

Expand All @@ -91,7 +91,7 @@ pub(crate) async fn pull_composefs_repo(
.await
.context("Pulling composefs repo")?;

tracing::info!("id: {}, verity: {}", hex::encode(id), verity.to_hex());
tracing::info!("ID: {}, Verity: {}", hex::encode(id), verity.to_hex());

let repo = open_composefs_repo(&rootfs_dir)?;
let mut fs: crate::store::ComposefsFilesystem =
Expand Down
22 changes: 22 additions & 0 deletions crates/lib/src/bootc_composefs/service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use anyhow::{Context, Result};
use fn_error_context::context;
use std::process::Command;

use crate::composefs_consts::BOOTC_FINALIZE_STAGED_SERVICE;

/// Starts the finaize staged service which will "unstage" the deployment
/// This is called before an upgrade or switch operation, as these create a staged
/// deployment
#[context("Starting finalize staged service")]
pub(crate) fn start_finalize_stated_svc() -> Result<()> {
let cmd_status = Command::new("systemctl")
.args(["start", "--quiet", BOOTC_FINALIZE_STAGED_SERVICE])
.status()
.context("Starting finalize service")?;

if !cmd_status.success() {
anyhow::bail!("systemctl exited with status {cmd_status}")
}

Ok(())
}
4 changes: 3 additions & 1 deletion crates/lib/src/bootc_composefs/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ pub(crate) fn write_composefs_state(
boot_type: BootType,
boot_digest: Option<String>,
) -> Result<()> {
let state_path = root_path.join(format!("{STATE_DIR_RELATIVE}/{}", deployment_id.to_hex()));
let state_path = root_path
.join(STATE_DIR_RELATIVE)
.join(deployment_id.to_hex());

create_dir_all(state_path.join("etc"))?;

Expand Down
3 changes: 3 additions & 0 deletions crates/lib/src/bootc_composefs/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
bootc_composefs::{
boot::{setup_composefs_bls_boot, setup_composefs_uki_boot, BootSetupType, BootType},
repo::pull_composefs_repo,
service::start_finalize_stated_svc,
state::write_composefs_state,
status::composefs_deployment_status,
},
Expand Down Expand Up @@ -36,6 +37,8 @@ pub(crate) async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
anyhow::bail!("Target image is undefined")
};

start_finalize_stated_svc()?;

let (repo, entries, id, fs) =
pull_composefs_repo(&target_imgref.transport, &target_imgref.image).await?;

Expand Down
5 changes: 3 additions & 2 deletions crates/lib/src/bootc_composefs/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
bootc_composefs::{
boot::{setup_composefs_bls_boot, setup_composefs_uki_boot, BootSetupType, BootType},
repo::pull_composefs_repo,
service::start_finalize_stated_svc,
state::write_composefs_state,
status::composefs_deployment_status,
},
Expand All @@ -14,12 +15,12 @@ use crate::{

#[context("Upgrading composefs")]
pub(crate) async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
// TODO: IMPORTANT Have all the checks here that `bootc upgrade` has for an ostree booted system

let host = composefs_deployment_status()
.await
.context("Getting composefs deployment status")?;

start_finalize_stated_svc()?;

// TODO: IMPORTANT We need to check if any deployment is staged and get the image from that
let imgref = host
.spec
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/bootloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use fn_error_context::context;

use bootc_blockdev::{Partition, PartitionTable};
use bootc_mount as mount;

#[cfg(any(feature = "composefs-backend", feature = "install-to-disk"))]
use bootc_mount::tempmount::TempMount;

use crate::utils;
Expand Down
4 changes: 2 additions & 2 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use tempfile::tempdir_in;

#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::{
finalize::{composefs_native_finalize, get_etc_diff},
finalize::{composefs_backend_finalize, get_etc_diff},
rollback::composefs_rollback,
state::composefs_usr_overlay,
status::composefs_booted,
Expand Down Expand Up @@ -1605,7 +1605,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
},

#[cfg(feature = "composefs-backend")]
Opt::ComposefsFinalizeStaged => composefs_native_finalize().await,
Opt::ComposefsFinalizeStaged => composefs_backend_finalize().await,

#[cfg(feature = "composefs-backend")]
Opt::ConfigDiff => get_etc_diff().await,
Expand Down
6 changes: 4 additions & 2 deletions crates/lib/src/composefs_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ pub(crate) const COMPOSEFS_TRANSIENT_STATE_DIR: &str = "/run/composefs";
/// File created in /run/composefs to record a staged-deployment
pub(crate) const COMPOSEFS_STAGED_DEPLOYMENT_FNAME: &str = "staged-deployment";

/// Absolute path to composefs-native state directory
/// Absolute path to composefs-backend state directory
pub(crate) const STATE_DIR_ABS: &str = "/sysroot/state/deploy";
/// Relative path to composefs-native state directory. Relative to /sysroot
/// Relative path to composefs-backend state directory. Relative to /sysroot
pub(crate) const STATE_DIR_RELATIVE: &str = "state/deploy";
/// Relative path to the shared 'var' directory. Relative to /sysroot
pub(crate) const SHARED_VAR_PATH: &str = "state/os/default/var";
Expand All @@ -36,3 +36,5 @@ pub(crate) const USER_CFG_STAGED: &str = "user.cfg.staged";
/// This is relative to the boot/efi directory
pub(crate) const TYPE1_ENT_PATH: &str = "loader/entries";
pub(crate) const TYPE1_ENT_PATH_STAGED: &str = "loader/entries.staged";

pub(crate) const BOOTC_FINALIZE_STAGED_SERVICE: &str = "bootc-finalize-staged.service";
72 changes: 42 additions & 30 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,17 @@ pub(crate) struct InstallConfigOpts {

#[derive(Debug, Default, Clone, clap::Parser, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct InstallComposefsOpts {
/// If true, composefs backend is used, else ostree backend is used
#[clap(long, default_value_t)]
#[serde(default)]
pub(crate) composefs_backend: bool,

/// Make fs-verity validation optional in case the filesystem doesn't support it
#[clap(long, default_value_t)]
#[serde(default)]
pub(crate) insecure: bool,

/// The bootloader to use.
#[clap(long)]
#[serde(default)]
pub(crate) bootloader: Option<Bootloader>,
Expand Down Expand Up @@ -286,11 +293,6 @@ pub(crate) struct InstallToDiskOpts {
#[serde(default)]
pub(crate) via_loopback: bool,

#[clap(long)]
#[serde(default)]
#[cfg(feature = "composefs-backend")]
pub(crate) composefs_native: bool,

#[clap(flatten)]
#[serde(flatten)]
#[cfg(feature = "composefs-backend")]
Expand Down Expand Up @@ -370,6 +372,10 @@ pub(crate) struct InstallToFilesystemOpts {

#[clap(flatten)]
pub(crate) config_opts: InstallConfigOpts,

#[cfg(feature = "composefs-backend")]
#[clap(flatten)]
pub(crate) composefs_opts: InstallComposefsOpts,
}

#[derive(Debug, Clone, clap::Parser, PartialEq, Eq)]
Expand Down Expand Up @@ -401,6 +407,10 @@ pub(crate) struct InstallToExistingRootOpts {
/// via e.g. `-v /:/target`.
#[clap(default_value = ALONGSIDE_ROOT_MOUNT)]
pub(crate) root_path: Utf8PathBuf,

#[cfg(feature = "composefs-backend")]
#[clap(flatten)]
pub(crate) composefs_opts: InstallComposefsOpts,
}

/// Global state captured from the container.
Expand Down Expand Up @@ -442,7 +452,7 @@ pub(crate) struct State {

// If Some, then --composefs_native is passed
#[cfg(feature = "composefs-backend")]
pub(crate) composefs_options: Option<InstallComposefsOpts>,
pub(crate) composefs_options: InstallComposefsOpts,

/// Detected bootloader type for the target system
pub(crate) detected_bootloader: crate::spec::Bootloader,
Expand Down Expand Up @@ -575,10 +585,10 @@ impl FromStr for MountSpec {
#[cfg(all(feature = "install-to-disk", feature = "composefs-backend"))]
impl InstallToDiskOpts {
pub(crate) fn validate(&self) -> Result<()> {
if !self.composefs_native {
// Reject using --insecure without --composefs
if !self.composefs_opts.composefs_backend {
// Reject using --insecure without --composefs-backend
if self.composefs_opts.insecure != false {
anyhow::bail!("--insecure must not be provided without --composefs");
anyhow::bail!("--insecure must not be provided without --composefs-backend");
}
}

Expand Down Expand Up @@ -1240,7 +1250,7 @@ async fn prepare_install(
config_opts: InstallConfigOpts,
source_opts: InstallSourceOpts,
target_opts: InstallTargetOpts,
composefs_options: Option<InstallComposefsOpts>,
#[cfg(feature = "composefs-backend")] mut composefs_options: InstallComposefsOpts,
) -> Result<Arc<State>> {
tracing::trace!("Preparing install");
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
Expand Down Expand Up @@ -1312,9 +1322,13 @@ async fn prepare_install(
} else {
false
};

tracing::debug!("Composefs required: {composefs_required}");
let composefs_options =
composefs_options.or_else(|| composefs_required.then_some(InstallComposefsOpts::default()));

#[cfg(feature = "composefs-backend")]
if composefs_required {
composefs_options.composefs_backend = true;
}

// We need to access devices that are set up by the host udev
bootc_mount::ensure_mirrored_host_mount("/dev")?;
Expand Down Expand Up @@ -1386,10 +1400,7 @@ async fn prepare_install(
// Priority: user-specified > bootupd availability > systemd-boot fallback
#[cfg(feature = "composefs-backend")]
let detected_bootloader = {
if let Some(bootloader) = composefs_options
.as_ref()
.and_then(|opts| opts.bootloader.clone())
{
if let Some(bootloader) = composefs_options.bootloader.clone() {
bootloader
} else {
if crate::bootloader::supports_bootupd(None)? {
Expand Down Expand Up @@ -1418,9 +1429,9 @@ async fn prepare_install(
tempdir,
host_is_container,
composefs_required,
detected_bootloader,
#[cfg(feature = "composefs-backend")]
composefs_options,
detected_bootloader,
});

Ok(state)
Expand Down Expand Up @@ -1592,7 +1603,7 @@ async fn install_to_filesystem_impl(
}

#[cfg(feature = "composefs-backend")]
if state.composefs_options.is_some() {
if state.composefs_options.composefs_backend {
// Load a fd for the mounted target physical root

let (id, verity) = initialize_composefs_repository(state, rootfs).await?;
Expand Down Expand Up @@ -1669,21 +1680,12 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
anyhow::bail!("Not a block device: {}", block_opts.device);
}

#[cfg(feature = "composefs-backend")]
let composefs_arg = if opts.composefs_native {
Some(opts.composefs_opts)
} else {
None
};

#[cfg(not(feature = "composefs-backend"))]
let composefs_arg = None;

let state = prepare_install(
opts.config_opts,
opts.source_opts,
opts.target_opts,
composefs_arg,
#[cfg(feature = "composefs-backend")]
opts.composefs_opts,
)
.await?;

Expand Down Expand Up @@ -1916,7 +1918,15 @@ pub(crate) async fn install_to_filesystem(
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
// IMPORTANT: In practice, we should only be gathering information before this point,
// IMPORTANT: and not performing any mutations at all.
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts, None).await?;
let state = prepare_install(
opts.config_opts,
opts.source_opts,
opts.target_opts,
#[cfg(feature = "composefs-backend")]
opts.composefs_opts,
)
.await?;

// And the last bit of state here is the fsopts, which we also destructure now.
let mut fsopts = opts.filesystem_opts;

Expand Down Expand Up @@ -2184,6 +2194,8 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
source_opts: opts.source_opts,
target_opts: opts.target_opts,
config_opts: opts.config_opts,
#[cfg(feature = "composefs-backend")]
composefs_opts: opts.composefs_opts,
};

install_to_filesystem(opts, true, cleanup).await
Expand Down
Loading