Skip to content

Commit 7a10221

Browse files
Johan-Liebert1cgwalters
authored andcommitted
composefs/rollback: Handle UKI rollback
We parse the grub menuentries, get the rollback deployment then perform the rollback, which basically consists of writing a new .staged menuentry file then atomically swapping the staged and the current menuentry. Rollback while there is a staged deployment is still to be handled. Signed-off-by: Johan-Liebert1 <[email protected]>
1 parent dc862a2 commit 7a10221

File tree

2 files changed

+83
-71
lines changed

2 files changed

+83
-71
lines changed

crates/lib/src/deploy.rs

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
//! Create a merged filesystem tree with the image and mounted configmaps.
44
55
use std::collections::HashSet;
6+
use std::fmt::Write as _;
67
use std::fs::create_dir_all;
7-
use std::io::{BufRead, Read as _, Write};
8+
use std::io::{BufRead, Read, Write};
89
use std::path::PathBuf;
910

1011
use anyhow::Ok;
@@ -25,12 +26,11 @@ use ostree_ext::tokio_util::spawn_blocking_cancellable_flatten;
2526
use rustix::fs::{fsync, renameat_with, AtFlags, RenameFlags};
2627

2728
use crate::bls_config::{parse_bls_config, BLSConfig};
28-
#[allow(unused_imports)]
29-
use crate::install::{get_efi_uuid_source, get_user_config, BootType};
29+
use crate::install::{get_efi_uuid_source, BootType};
3030
use crate::parsers::grub_menuconfig::{parse_grub_menuentry_file, MenuEntry};
3131
use crate::progress_jsonl::{Event, ProgressWriter, SubTaskBytes, SubTaskStep};
3232
use crate::spec::ImageReference;
33-
use crate::spec::{BootEntry, BootOrder, HostSpec};
33+
use crate::spec::{BootOrder, HostSpec};
3434
use crate::status::{composefs_deployment_status, labels_of_config};
3535
use crate::store::Storage;
3636
use crate::utils::async_task_with_spinner;
@@ -744,42 +744,61 @@ pub(crate) async fn stage(
744744
Ok(())
745745
}
746746

747+
/// Filename for `loader/entries`
748+
pub(crate) const USER_CFG: &str = "user.cfg";
749+
pub(crate) const USER_CFG_STAGED: &str = "user.cfg.staged";
750+
pub(crate) const USER_CFG_ROLLBACK: &str = USER_CFG_STAGED;
751+
747752
#[context("Rolling back UKI")]
748-
pub(crate) fn rollback_composefs_uki(_current: &BootEntry, _rollback: &BootEntry) -> Result<()> {
749-
unimplemented!()
750-
// let user_cfg_name = "grub2/user.cfg.staged";
751-
// let user_cfg_path = PathBuf::from("boot").join(user_cfg_name);
752-
// let sysroot = &Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority())?;
753-
754-
// let efi_uuid_source = get_efi_uuid_source();
755-
756-
// let rollback_verity = if let Some(composefs) = &rollback.composefs {
757-
// composefs.verity.clone()
758-
// } else {
759-
// // Shouldn't really happen
760-
// anyhow::bail!("Verity not found for rollback deployment")
761-
// };
762-
// let rollback_config = get_user_config(todo!(), &rollback_verity).as_bytes();
763-
764-
// let current_verity = if let Some(composefs) = &current.composefs {
765-
// composefs.verity.clone()
766-
// } else {
767-
// // Shouldn't really happen
768-
// anyhow::bail!("Verity not found for booted deployment")
769-
// };
770-
// let current_config = get_user_config(todo!(), &current_verity).as_bytes();
771-
772-
// // TODO: Need to check if user.cfg.staged exists
773-
// sysroot
774-
// .atomic_replace_with(user_cfg_path, |w| {
775-
// write!(w, "{efi_uuid_source}")?;
776-
// w.write_all(rollback_config)?;
777-
// w.write_all(current_config)?;
778-
// Ok(())
779-
// })
780-
// .with_context(|| format!("Writing {user_cfg_name}"))?;
781-
782-
// Ok(())
753+
pub(crate) fn rollback_composefs_uki() -> Result<()> {
754+
let user_cfg_path = PathBuf::from("/sysroot/boot/grub2");
755+
756+
let mut str = String::new();
757+
let mut menuentries =
758+
get_sorted_uki_boot_entries(&mut str).context("Getting UKI boot entries")?;
759+
760+
// TODO(Johan-Liebert): Currently assuming there are only two deployments
761+
assert!(menuentries.len() == 2);
762+
763+
let (first, second) = menuentries.split_at_mut(1);
764+
std::mem::swap(&mut first[0], &mut second[0]);
765+
766+
let mut buffer = get_efi_uuid_source();
767+
768+
for entry in menuentries {
769+
write!(buffer, "{entry}")?;
770+
}
771+
772+
let entries_dir =
773+
cap_std::fs::Dir::open_ambient_dir(&user_cfg_path, cap_std::ambient_authority())
774+
.with_context(|| format!("Opening {user_cfg_path:?}"))?;
775+
776+
entries_dir
777+
.atomic_write(USER_CFG_ROLLBACK, buffer)
778+
.with_context(|| format!("Writing to {USER_CFG_ROLLBACK}"))?;
779+
780+
tracing::debug!("Atomically exchanging for {USER_CFG_ROLLBACK} and {USER_CFG}");
781+
renameat_with(
782+
&entries_dir,
783+
USER_CFG_ROLLBACK,
784+
&entries_dir,
785+
USER_CFG,
786+
RenameFlags::EXCHANGE,
787+
)
788+
.context("renameat")?;
789+
790+
tracing::debug!("Removing {USER_CFG_ROLLBACK}");
791+
rustix::fs::unlinkat(&entries_dir, USER_CFG_ROLLBACK, AtFlags::empty()).context("unlinkat")?;
792+
793+
tracing::debug!("Syncing to disk");
794+
fsync(
795+
entries_dir
796+
.reopen_as_ownedfd()
797+
.with_context(|| format!("Reopening {user_cfg_path:?} as owned fd"))?,
798+
)
799+
.with_context(|| format!("fsync {user_cfg_path:?}"))?;
800+
801+
Ok(())
783802
}
784803

785804
/// Filename for `loader/entries`
@@ -837,6 +856,7 @@ pub(crate) fn rollback_composefs_bls() -> Result<()> {
837856
cfg.version = idx as u32;
838857
}
839858

859+
// TODO(Johan-Liebert): Currently assuming there are only two deployments
840860
assert!(all_configs.len() == 2);
841861

842862
// Write these
@@ -852,7 +872,7 @@ pub(crate) fn rollback_composefs_bls() -> Result<()> {
852872
let file_name = format!("bootc-composefs-{}.conf", cfg.version);
853873

854874
rollback_entries_dir
855-
.atomic_write(&file_name, cfg.to_string().as_bytes())
875+
.atomic_write(&file_name, cfg.to_string())
856876
.with_context(|| format!("Writing to {file_name}"))?;
857877
}
858878

@@ -879,7 +899,7 @@ pub(crate) fn rollback_composefs_bls() -> Result<()> {
879899
.context("renameat")?;
880900

881901
tracing::debug!("Removing {ROLLBACK_ENTRIES}");
882-
rustix::fs::unlinkat(&dir, ROLLBACK_ENTRIES, AtFlags::REMOVEDIR).context("unlinkat")?;
902+
rustix::fs::unlinkat(&dir, ROLLBACK_ENTRIES, AtFlags::empty()).context("unlinkat")?;
883903

884904
tracing::debug!("Syncing to disk");
885905
fsync(
@@ -923,7 +943,7 @@ pub(crate) async fn composefs_rollback() -> Result<()> {
923943

924944
match rollback_composefs_entry.boot_type {
925945
BootType::Bls => rollback_composefs_bls(),
926-
BootType::Uki => rollback_composefs_uki(&host.status.booted.unwrap(), &rollback_status),
946+
BootType::Uki => rollback_composefs_uki(),
927947
}?;
928948

929949
Ok(())

crates/lib/src/install.rs

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,13 @@ use self::baseline::InstallBlockDeviceOpts;
7575
use crate::bls_config::{parse_bls_config, BLSConfig};
7676
use crate::boundimage::{BoundImage, ResolvedBoundImage};
7777
use crate::containerenv::ContainerExecutionInfo;
78-
use crate::deploy::{prepare_for_pull, pull_from_prepared, PreparedImportMeta, PreparedPullResult};
78+
use crate::deploy::{
79+
get_sorted_uki_boot_entries, prepare_for_pull, pull_from_prepared, PreparedImportMeta,
80+
PreparedPullResult, USER_CFG, USER_CFG_STAGED,
81+
};
7982
use crate::kernel_cmdline::Cmdline;
8083
use crate::lsm;
84+
use crate::parsers::grub_menuconfig::MenuEntry;
8185
use crate::progress_jsonl::ProgressWriter;
8286
use crate::spec::ImageReference;
8387
use crate::store::Storage;
@@ -1743,22 +1747,6 @@ pub fn get_esp_partition(device: &str) -> Result<(String, Option<String>)> {
17431747
Ok((esp.node, esp.uuid))
17441748
}
17451749

1746-
pub(crate) fn get_user_config(boot_label: &String, uki_id: &str) -> String {
1747-
// TODO: Full EFI path here
1748-
let s = format!(
1749-
r#"
1750-
menuentry "{boot_label}: ({uki_id})" {{
1751-
insmod fat
1752-
insmod chain
1753-
search --no-floppy --set=root --fs-uuid "${{EFI_PART_UUID}}"
1754-
chainloader /EFI/Linux/{uki_id}.efi
1755-
}}
1756-
"#
1757-
);
1758-
1759-
return s;
1760-
}
1761-
17621750
/// Contains the EFP's filesystem UUID. Used by grub
17631751
pub(crate) const EFI_UUID_FILE: &str = "efiuuid.cfg";
17641752

@@ -1907,9 +1895,9 @@ pub(crate) fn setup_composefs_uki_boot(
19071895
let efi_uuid_source = get_efi_uuid_source();
19081896

19091897
let user_cfg_name = if is_upgrade {
1910-
"user.cfg.staged"
1898+
USER_CFG_STAGED
19111899
} else {
1912-
"user.cfg"
1900+
USER_CFG
19131901
};
19141902

19151903
let grub_dir =
@@ -1924,17 +1912,17 @@ pub(crate) fn setup_composefs_uki_boot(
19241912

19251913
// Shouldn't really fail so no context here
19261914
buffer.write_all(efi_uuid_source.as_bytes())?;
1927-
buffer.write_all(get_user_config(&boot_label, &id.to_hex()).as_bytes())?;
1928-
1929-
// root_path here will be /sysroot
1930-
for entry in std::fs::read_dir(root_path.join(STATE_DIR_RELATIVE))? {
1931-
let entry = entry?;
1915+
buffer.write_all(
1916+
MenuEntry::new(&boot_label, &id.to_hex())
1917+
.to_string()
1918+
.as_bytes(),
1919+
)?;
19321920

1933-
let depl_file_name = entry.file_name();
1934-
// SAFETY: Deployment file name shouldn't containg non UTF-8 chars
1935-
let depl_file_name = depl_file_name.to_string_lossy();
1921+
let mut str_buf = String::new();
1922+
let entries = get_sorted_uki_boot_entries(&mut str_buf)?;
19361923

1937-
buffer.write_all(get_user_config(&boot_label, &depl_file_name).as_bytes())?;
1924+
for entry in entries {
1925+
buffer.write_all(entry.to_string().as_bytes())?;
19381926
}
19391927

19401928
grub_dir
@@ -1962,7 +1950,11 @@ pub(crate) fn setup_composefs_uki_boot(
19621950

19631951
// Shouldn't really fail so no context here
19641952
buffer.write_all(efi_uuid_source.as_bytes())?;
1965-
buffer.write_all(get_user_config(&boot_label, &id.to_hex()).as_bytes())?;
1953+
buffer.write_all(
1954+
MenuEntry::new(&boot_label, &id.to_hex())
1955+
.to_string()
1956+
.as_bytes(),
1957+
)?;
19661958

19671959
grub_dir
19681960
.atomic_write(user_cfg_name, buffer)

0 commit comments

Comments
 (0)