Skip to content

Commit e994131

Browse files
Merge pull request #1089 from Johan-Liebert1/usr-lib-efi-backwards-compat
efi: Choose update layout depending on /usr/lib/efi
2 parents d7a054e + fbb5911 commit e994131

5 files changed

Lines changed: 172 additions & 115 deletions

File tree

src/efi.rs

Lines changed: 89 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -568,50 +568,49 @@ impl Component for Efi {
568568
}
569569

570570
fn generate_update_metadata(&self, sysroot: &str) -> Result<Option<ContentMetadata>> {
571-
let sysroot_path = Path::new(sysroot);
571+
let sysroot_path = Utf8Path::new(sysroot);
572572
let sysroot_dir = Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?;
573573

574-
if let Some(ostreeboot) = sysroot_dir
574+
let ostreeboot = sysroot_dir
575575
.open_dir_optional(ostreeutil::BOOT_PREFIX)
576-
.context("Opening usr/lib/ostree-boot")?
577-
{
576+
.context("Opening usr/lib/ostree-boot")?;
577+
578+
// Newer images >= F44 have boot entries in /usr/lib/efi, but older images
579+
// have them in /usr/lib/ostree-boot, which we move to /usr/lib/bootupd/updates/EFI
580+
let metadata = if sysroot_path.join(EFILIB).exists() {
581+
println!("Generating metadata from {EFILIB}");
582+
generate_meta_from_usr_efi(sysroot_path)?
583+
} else {
584+
match &ostreeboot {
585+
Some(..) => {
586+
println!(
587+
"Moving {}/efi/EFI to {BOOTUPD_UPDATES_DIR}/EFI",
588+
ostreeutil::BOOT_PREFIX
589+
);
590+
// Transfer usr/lib/ostree-boot/EFI files to usr/lib/bootupd/updates/EFI
591+
transfer_ostree_boot_to_bootupd_updates(sysroot_path, self)?
592+
}
593+
594+
// If /usr/lib/ostree-boot doesn't exist, assume /usr/lib/efi will
595+
None => anyhow::bail!("/usr/lib/ostree-boot and /usr/lib/efi not found"),
596+
}
597+
};
598+
599+
if let Some(ostreeboot) = ostreeboot {
578600
let cruft = ["loader", "grub2"];
579601
for p in cruft.iter() {
602+
println!("Removing {p} from {}", ostreeutil::BOOT_PREFIX);
580603
ostreeboot.remove_all_optional(p)?;
581604
}
582-
// Transfer ostree-boot EFI files to usr/lib/efi
583-
transfer_ostree_boot_to_usr(sysroot_path)?;
584605

606+
println!("Removing efi/EFI from {}", ostreeutil::BOOT_PREFIX);
585607
// Remove usr/lib/ostree-boot/efi/EFI dir (after transfer) or if it is empty
586608
ostreeboot.remove_all_optional("efi/EFI")?;
587-
}
609+
};
588610

589-
if let Some(efi_components) =
590-
get_efi_component_from_usr(Utf8Path::from_path(sysroot_path).unwrap(), EFILIB)?
591-
{
592-
let mut packages = Vec::new();
593-
let mut modules_vec: Vec<Module> = vec![];
594-
for efi in efi_components {
595-
packages.push(format!("{}-{}", efi.name, efi.version));
596-
modules_vec.push(Module {
597-
name: efi.name,
598-
rpm_evr: efi.version,
599-
});
600-
}
601-
modules_vec.sort_unstable();
602-
603-
// change to now to workaround https://github.com/coreos/bootupd/issues/933
604-
let timestamp = std::time::SystemTime::now();
605-
let meta = ContentMetadata {
606-
timestamp: chrono::DateTime::<Utc>::from(timestamp),
607-
version: packages.join(","),
608-
versions: Some(modules_vec),
609-
};
610-
write_update_metadata(sysroot, self, &meta)?;
611-
Ok(Some(meta))
612-
} else {
613-
anyhow::bail!("Failed to find EFI components");
614-
}
611+
write_update_metadata(sysroot_path.as_str(), self, &metadata)?;
612+
613+
Ok(Some(metadata))
615614
}
616615

617616
fn query_update(&self, sysroot: &openat::Dir) -> Result<Option<ContentMetadata>> {
@@ -801,6 +800,34 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
801800
Ok(result)
802801
}
803802

803+
#[context("Generating metadata from usr/lib/efi")]
804+
fn generate_meta_from_usr_efi(sysroot_path: &Utf8Path) -> Result<ContentMetadata> {
805+
let Some(efi_components) = get_efi_component_from_usr(sysroot_path, EFILIB)? else {
806+
anyhow::bail!("Failed to find EFI components");
807+
};
808+
809+
let mut packages = Vec::new();
810+
let mut modules_vec: Vec<Module> = vec![];
811+
for efi in efi_components {
812+
packages.push(format!("{}-{}", efi.name, efi.version));
813+
modules_vec.push(Module {
814+
name: efi.name,
815+
rpm_evr: efi.version,
816+
});
817+
}
818+
modules_vec.sort_unstable();
819+
820+
// change to now to workaround https://github.com/coreos/bootupd/issues/933
821+
let timestamp = std::time::SystemTime::now();
822+
let meta = ContentMetadata {
823+
timestamp: chrono::DateTime::<Utc>::from(timestamp),
824+
version: packages.join(","),
825+
versions: Some(modules_vec),
826+
};
827+
828+
Ok(meta)
829+
}
830+
804831
#[derive(Debug, PartialEq, Eq)]
805832
pub struct EFIComponent {
806833
pub name: String,
@@ -851,57 +878,43 @@ fn get_efi_component_from_usr<'a>(
851878
Ok(Some(components))
852879
}
853880

854-
/// Copy files from usr/lib/ostree-boot/efi/EFI to /usr/lib/efi/<component>/<evr>/
855-
fn transfer_ostree_boot_to_usr(sysroot: &Path) -> Result<()> {
856-
let ostreeboot_efi = Path::new(ostreeutil::BOOT_PREFIX).join("efi");
857-
let ostreeboot_efi_path = sysroot.join(&ostreeboot_efi);
881+
/// Copies usr/lib/ostree-boot/EFI to usr/lib/bootupd/updates
882+
fn transfer_ostree_boot_to_bootupd_updates(
883+
sysroot: &Utf8Path,
884+
component: &Efi,
885+
) -> Result<ContentMetadata> {
886+
let ostreebootdir = sysroot.join(ostreeutil::BOOT_PREFIX);
858887

859-
let efi = ostreeboot_efi_path.join("EFI");
860-
if !efi.exists() {
861-
return Ok(());
888+
// move EFI files to updates dir from /usr/lib/ostree-boot
889+
if !ostreebootdir.exists() {
890+
anyhow::bail!("Failed to find {ostreebootdir}");
862891
}
863-
for entry in WalkDir::new(&efi) {
864-
let entry = entry?;
865892

866-
if entry.file_type().is_file() {
867-
let entry_path = entry.path();
893+
let efisrc = ostreebootdir.join("efi/EFI");
894+
if !efisrc.exists() {
895+
bail!("Failed to find {:?}", &efisrc);
896+
}
868897

869-
// get path EFI/{BOOT,<vendor>}/<file>
870-
let filepath = entry_path.strip_prefix(&ostreeboot_efi_path)?;
871-
// get path /boot/efi/EFI/{BOOT,<vendor>}/<file>
872-
let boot_filepath = Path::new("/boot/efi").join(filepath);
898+
let dest_efidir = component_updatedir(sysroot.as_str(), component);
899+
let dest_efidir = Utf8PathBuf::from_path_buf(dest_efidir).expect("Path is invalid UTF-8");
873900

874-
// Run `rpm -qf <filepath>`
875-
let pkg = crate::packagesystem::query_file(
876-
sysroot.to_str().unwrap(),
877-
boot_filepath.to_str().unwrap(),
878-
)?;
901+
// Fork off mv() because on overlayfs one can't rename() a lower level
902+
// directory today, and this will handle the copy fallback.
903+
Command::new("mv")
904+
.args([&efisrc, &dest_efidir])
905+
.run_capture_stderr()?;
879906

880-
let (name, evr) = pkg.split_once(' ').unwrap();
881-
let component = name.split('-').next().unwrap_or("");
882-
// get path usr/lib/efi/<component>/<evr>
883-
let efilib_path = Path::new(EFILIB).join(component).join(evr);
907+
let efidir = openat::Dir::open(dest_efidir.as_std_path())
908+
.with_context(|| format!("Opening {}", dest_efidir))?;
884909

885-
let sysroot_dir = openat::Dir::open(sysroot)?;
886-
// Ensure dest parent directory exists
887-
if let Some(parent) = efilib_path.join(filepath).parent() {
888-
sysroot_dir.ensure_dir_all(parent, 0o755)?;
889-
}
910+
let files = crate::util::filenames(&efidir)?.into_iter().map(|mut f| {
911+
f.insert_str(0, "/boot/efi/EFI");
912+
f
913+
});
890914

891-
// Source dir is usr/lib/ostree-boot/efi
892-
let src = sysroot_dir
893-
.sub_dir(&ostreeboot_efi)
894-
.context("Opening ostree-boot dir")?;
895-
// Dest dir is usr/lib/efi/<component>/<evr>
896-
let dest = sysroot_dir
897-
.sub_dir(&efilib_path)
898-
.context("Opening usr/lib/efi dir")?;
899-
// Copy file from ostree-boot to usr/lib/efi
900-
src.copy_file_at(filepath, &dest, filepath)
901-
.context("Copying file to usr/lib/efi")?;
902-
}
903-
}
904-
Ok(())
915+
let files = files.collect::<Vec<_>>();
916+
917+
query_files(sysroot.as_str(), files)
905918
}
906919

907920
#[cfg(test)]

src/packagesystem.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,21 +115,6 @@ fn split_name_version(input: &str) -> Option<(String, String)> {
115115
Some((name.to_string(), format!("{version}-{release}")))
116116
}
117117

118-
/// Query the rpm database and get package "<name> <evr>"
119-
pub(crate) fn query_file(sysroot_path: &str, path: &str) -> Result<String> {
120-
let mut c = ostreeutil::rpm_cmd(sysroot_path)?;
121-
c.args(["-q", "--queryformat", "%{NAME} %{EVR}", "-f", path]);
122-
123-
let rpmout = c.output()?;
124-
if !rpmout.status.success() {
125-
std::io::stderr().write_all(&rpmout.stderr)?;
126-
bail!("Failed to invoke rpm -qf");
127-
}
128-
129-
let output = String::from_utf8(rpmout.stdout)?;
130-
Ok(output.trim().to_string())
131-
}
132-
133118
fn parse_evr(pkg: &str) -> Module {
134119
// assume it is "grub2-1:2.12-28.fc42" (from usr/lib/efi)
135120
if !pkg.ends_with(std::env::consts::ARCH) {

tests/e2e-update/e2e-update-in-vm.sh

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,21 @@ ok validate
5353

5454
bootupctl status | tee out.txt
5555
assert_file_has_content_literal out.txt 'Component EFI'
56-
assert_file_has_content_literal out.txt ' Installed: grub2-1:'
57-
assert_not_file_has_content out.txt ' Installed:.*test_bootupd_payload'
58-
assert_not_file_has_content out.txt ' Installed:.*'"${TARGET_GRUB_EVR}"
59-
assert_file_has_content out.txt 'Update: Available:.*'"${TARGET_GRUB_EVR}"
60-
assert_file_has_content out.txt 'Update: Available:.*test_bootupd_payload-1.0'
56+
57+
if [[ -d /usr/lib/efi ]]; then
58+
assert_file_has_content_literal out.txt ' Installed: grub2-1:'
59+
assert_not_file_has_content out.txt ' Installed:.*test_bootupd_payload'
60+
assert_not_file_has_content out.txt ' Installed:.*'"${TARGET_GRUB_EVR}"
61+
assert_file_has_content out.txt 'Update: Available:.*'"${TARGET_GRUB_EVR}"
62+
assert_file_has_content out.txt 'Update: Available:.*test_bootupd_payload-1.0'
63+
else
64+
assert_file_has_content_literal out.txt ' Installed: grub2-efi-x64-'
65+
assert_not_file_has_content out.txt ' Installed:.*test_bootupd_payload'
66+
assert_not_file_has_content out.txt ' Installed:.*'"${TARGET_GRUB_PKG}"
67+
assert_file_has_content out.txt 'Update: Available:.*'"${TARGET_GRUB_PKG}"
68+
assert_file_has_content out.txt 'Update: Available:.*test_bootupd_payload-1.0'
69+
fi
70+
6171
bootupctl status --print-if-available > out.txt
6272
assert_file_has_content_literal 'out.txt' 'Updates available: BIOS EFI'
6373
ok update avail

tests/e2e-update/e2e-update.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ case $(arch) in
7777
*) fatal "Unhandled arch $(arch)";;
7878
esac
7979
target_grub_name=grub2-efi-${grubarch}
80+
target_grub_pkg=$(rpm -qp --queryformat='%{nevra}\n' ${overrides}/rpm/${target_grub_name}-2*.rpm)
8081
target_grub_evr=$(rpm -qp --queryformat='%{evr}\n' ${overrides}/rpm/${target_grub_name}-2*.rpm)
8182
target_commit=$(cosa meta --get-value ostree-commit)
8283
echo "Target commit: ${target_commit}"
@@ -97,6 +98,7 @@ systemd:
9798
RemainAfterExit=yes
9899
Environment=TARGET_COMMIT=${target_commit}
99100
Environment=TARGET_GRUB_NAME=${target_grub_name}
101+
Environment=TARGET_GRUB_PKG=${target_grub_pkg}
100102
Environment=TARGET_GRUB_EVR=${target_grub_evr}
101103
Environment=SRCDIR=/run/bootupd-source
102104
# Run via shell because selinux denies systemd writing to 9p apparently

0 commit comments

Comments
 (0)