Skip to content

Commit 9e102d7

Browse files
authored
Merge pull request #995 from HuijingHei/step2-ostree-boot
efi: transfer `usr/lib/ostree-boot` to `usr/lib/efi`
2 parents 2352767 + a3a3493 commit 9e102d7

File tree

10 files changed

+174
-111
lines changed

10 files changed

+174
-111
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ jobs:
9999
- name: bootupctl generate-update-metadata
100100
run: |
101101
set -xeuo pipefail
102-
sudo podman run --rm -v $PWD:/run/src -w /run/src --privileged localhost/bootupd:latest tests/tests/move-content-to-usr.sh
102+
sudo podman run --rm -v $PWD:/run/src -w /run/src --privileged localhost/bootupd:latest tests/tests/generate-update-metadata.sh

src/component.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub(crate) fn component_updatedirname(component: &dyn Component) -> PathBuf {
112112

113113
/// Returns the path to the payload directory for an available update for
114114
/// a component.
115+
#[allow(dead_code)]
115116
#[cfg(any(
116117
target_arch = "x86_64",
117118
target_arch = "aarch64",

src/efi.rs

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use bootc_internal_utils::CommandRunExt;
1414
use camino::{Utf8Path, Utf8PathBuf};
1515
use cap_std::fs::Dir;
1616
use cap_std_ext::cap_std;
17+
use cap_std_ext::dirext::CapStdExtDirExt;
1718
use chrono::prelude::*;
1819
use fn_error_context::context;
1920
use openat_ext::OpenatDirExt;
@@ -461,18 +462,34 @@ impl Component for Efi {
461462
}
462463

463464
fn generate_update_metadata(&self, sysroot: &str) -> Result<Option<ContentMetadata>> {
464-
let sysroot_path = Utf8Path::new(sysroot);
465+
let sysroot_path = Path::new(sysroot);
466+
let sysroot_dir = Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?;
467+
468+
if let Some(ostreeboot) = sysroot_dir
469+
.open_dir_optional(ostreeutil::BOOT_PREFIX)
470+
.context("Opening usr/lib/ostree-boot")?
471+
{
472+
let cruft = ["loader", "grub2"];
473+
for p in cruft.iter() {
474+
ostreeboot.remove_all_optional(p)?;
475+
}
476+
// Transfer ostree-boot EFI files to usr/lib/efi
477+
transfer_ostree_boot_to_usr(sysroot_path)?;
478+
479+
// Remove usr/lib/ostree-boot/efi/EFI dir (after transfer) or if it is empty
480+
ostreeboot.remove_all_optional("efi/EFI")?;
481+
}
482+
483+
// copy EFI files from usr/lib/efi to updates dir
484+
if let Some(efi_components) =
485+
get_efi_component_from_usr(Utf8Path::from_path(sysroot_path).unwrap(), EFILIB)?
486+
{
487+
// Confirm EFI has at least two components grub2 & shim
488+
// See https://github.com/coreos/bootupd/issues/994
489+
assert!(efi_components.len() > 1);
465490

466-
// copy EFI files to updates dir from usr/lib/efi
467-
let efilib_path = sysroot_path.join(EFILIB);
468-
let meta = if efilib_path.exists() {
469491
let mut packages = Vec::new();
470492
let mut modules_vec: Vec<Module> = vec![];
471-
let sysroot_dir = Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?;
472-
let efi_components = get_efi_component_from_usr(&sysroot_path, EFILIB)?;
473-
if efi_components.len() == 0 {
474-
bail!("Failed to find EFI components from {efilib_path}");
475-
}
476493
for efi in efi_components {
477494
Command::new("cp")
478495
.args(["-rp", "--reflink=auto"])
@@ -490,50 +507,16 @@ impl Component for Efi {
490507

491508
// change to now to workaround https://github.com/coreos/bootupd/issues/933
492509
let timestamp = std::time::SystemTime::now();
493-
ContentMetadata {
510+
let meta = ContentMetadata {
494511
timestamp: chrono::DateTime::<Utc>::from(timestamp),
495512
version: packages.join(","),
496513
versions: Some(modules_vec),
497-
}
514+
};
515+
write_update_metadata(sysroot, self, &meta)?;
516+
Ok(Some(meta))
498517
} else {
499-
let ostreebootdir = sysroot_path.join(ostreeutil::BOOT_PREFIX);
500-
501-
// move EFI files to updates dir from /usr/lib/ostree-boot
502-
if ostreebootdir.exists() {
503-
let cruft = ["loader", "grub2"];
504-
for p in cruft.iter() {
505-
let p = ostreebootdir.join(p);
506-
if p.exists() {
507-
std::fs::remove_dir_all(&p)?;
508-
}
509-
}
510-
511-
let efisrc = ostreebootdir.join("efi/EFI");
512-
if !efisrc.exists() {
513-
bail!("Failed to find {:?}", &efisrc);
514-
}
515-
516-
let dest_efidir = component_updatedir(sysroot, self);
517-
let dest_efidir =
518-
Utf8PathBuf::from_path_buf(dest_efidir).expect("Path is invalid UTF-8");
519-
// Fork off mv() because on overlayfs one can't rename() a lower level
520-
// directory today, and this will handle the copy fallback.
521-
Command::new("mv").args([&efisrc, &dest_efidir]).run()?;
522-
523-
let efidir = openat::Dir::open(dest_efidir.as_std_path())
524-
.with_context(|| format!("Opening {}", dest_efidir))?;
525-
let files = crate::util::filenames(&efidir)?.into_iter().map(|mut f| {
526-
f.insert_str(0, "/boot/efi/EFI/");
527-
f
528-
});
529-
query_files(sysroot, files)?
530-
} else {
531-
anyhow::bail!("Failed to find {ostreebootdir}");
532-
}
533-
};
534-
535-
write_update_metadata(sysroot, self, &meta)?;
536-
Ok(Some(meta))
518+
anyhow::bail!("Failed to find EFI components");
519+
}
537520
}
538521

539522
fn query_update(&self, sysroot: &openat::Dir) -> Result<Option<ContentMetadata>> {
@@ -730,7 +713,7 @@ pub struct EFIComponent {
730713
fn get_efi_component_from_usr<'a>(
731714
sysroot: &'a Utf8Path,
732715
usr_path: &'a str,
733-
) -> Result<Vec<EFIComponent>> {
716+
) -> Result<Option<Vec<EFIComponent>>> {
734717
let efilib_path = sysroot.join(usr_path);
735718
let skip_count = Utf8Path::new(usr_path).components().count();
736719

@@ -761,9 +744,65 @@ fn get_efi_component_from_usr<'a>(
761744
})
762745
.collect();
763746

747+
if components.len() == 0 {
748+
return Ok(None);
749+
}
764750
components.sort_by(|a, b| a.name.cmp(&b.name));
765751

766-
Ok(components)
752+
Ok(Some(components))
753+
}
754+
755+
/// Copy files from usr/lib/ostree-boot/efi/EFI to /usr/lib/efi/<component>/<evr>/
756+
fn transfer_ostree_boot_to_usr(sysroot: &Path) -> Result<()> {
757+
let ostreeboot_efi = Path::new(ostreeutil::BOOT_PREFIX).join("efi");
758+
let ostreeboot_efi_path = sysroot.join(&ostreeboot_efi);
759+
760+
let efi = ostreeboot_efi_path.join("EFI");
761+
if !efi.exists() {
762+
return Ok(());
763+
}
764+
for entry in WalkDir::new(&efi) {
765+
let entry = entry?;
766+
767+
if entry.file_type().is_file() {
768+
let entry_path = entry.path();
769+
770+
// get path EFI/{BOOT,<vendor>}/<file>
771+
let filepath = entry_path.strip_prefix(&ostreeboot_efi_path)?;
772+
// get path /boot/efi/EFI/{BOOT,<vendor>}/<file>
773+
let boot_filepath = Path::new("/boot/efi").join(filepath);
774+
775+
// Run `rpm -qf <filepath>`
776+
let pkg = crate::packagesystem::query_file(
777+
sysroot.to_str().unwrap(),
778+
boot_filepath.to_str().unwrap(),
779+
)?;
780+
781+
let (name, evr) = pkg.split_once(' ').unwrap();
782+
let component = name.split('-').next().unwrap_or("");
783+
// get path usr/lib/efi/<component>/<evr>
784+
let efilib_path = Path::new(EFILIB).join(component).join(evr);
785+
786+
let sysroot_dir = openat::Dir::open(sysroot)?;
787+
// Ensure dest parent directory exists
788+
if let Some(parent) = efilib_path.join(filepath).parent() {
789+
sysroot_dir.ensure_dir_all(parent, 0o755)?;
790+
}
791+
792+
// Source dir is usr/lib/ostree-boot/efi
793+
let src = sysroot_dir
794+
.sub_dir(&ostreeboot_efi)
795+
.context("Opening ostree-boot dir")?;
796+
// Dest dir is usr/lib/efi/<component>/<evr>
797+
let dest = sysroot_dir
798+
.sub_dir(&efilib_path)
799+
.context("Opening usr/lib/efi dir")?;
800+
// Copy file from ostree-boot to usr/lib/efi
801+
src.copy_file_at(filepath, &dest, filepath)
802+
.context("Copying file to usr/lib/efi")?;
803+
}
804+
}
805+
Ok(())
767806
}
768807

769808
#[cfg(test)]
@@ -900,7 +939,7 @@ Boot0003* test";
900939
let efi_comps = get_efi_component_from_usr(utf8_tpath, EFILIB)?;
901940
assert_eq!(
902941
efi_comps,
903-
vec![
942+
Some(vec![
904943
EFIComponent {
905944
name: "BAR".to_string(),
906945
version: "1.1".to_string(),
@@ -911,12 +950,12 @@ Boot0003* test";
911950
version: "1.1".to_string(),
912951
path: Utf8PathBuf::from("usr/lib/efi/FOO/1.1/EFI"),
913952
},
914-
]
953+
])
915954
);
916955
std::fs::remove_dir_all(efi_path.join("BAR/1.1/EFI"))?;
917956
std::fs::remove_dir_all(efi_path.join("FOO/1.1/EFI"))?;
918957
let efi_comps = get_efi_component_from_usr(utf8_tpath, EFILIB)?;
919-
assert_eq!(efi_comps, []);
958+
assert_eq!(efi_comps, None);
920959
Ok(())
921960
}
922961
}

src/packagesystem.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ 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+
118133
fn parse_evr(pkg: &str) -> Module {
119134
// assume it is "grub2-1:2.12-28.fc42" (from usr/lib/efi)
120135
if !pkg.ends_with(std::env::consts::ARCH) {

src/util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub(crate) fn getenv_utf8(n: &str) -> Result<Option<String>> {
1919
}
2020
}
2121

22+
#[allow(dead_code)]
2223
pub(crate) fn filenames(dir: &openat::Dir) -> Result<HashSet<String>> {
2324
let mut ret = HashSet::new();
2425
for entry in dir.list_dir(".")? {

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ if ! test -f ${stampfile}; then
1818
fatal "already at ${TARGET_COMMIT}"
1919
fi
2020

21-
current_grub=$(rpm -q --queryformat='%{nevra}\n' ${TARGET_GRUB_NAME})
22-
if test "${current_grub}" == "${TARGET_GRUB_PKG}"; then
23-
fatal "Current grub ${current_grub} is same as target ${TARGET_GRUB_PKG}"
21+
current_grub=$(rpm -q --queryformat='%{evr}\n' ${TARGET_GRUB_NAME})
22+
if test "${current_grub}" == "${TARGET_GRUB_EVR}"; then
23+
fatal "Current grub ${current_grub} is same as target ${TARGET_GRUB_EVR}"
2424
fi
2525

2626
# FIXME
@@ -44,20 +44,20 @@ if test "$semode" != Enforcing; then
4444
fatal "SELinux mode is ${semode}"
4545
fi
4646

47-
if ! test -n "${TARGET_GRUB_PKG}"; then
48-
fatal "Missing TARGET_GRUB_PKG"
47+
if ! test -n "${TARGET_GRUB_EVR}"; then
48+
fatal "Missing TARGET_GRUB_EVR"
4949
fi
5050

5151
bootupctl validate
5252
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-efi-x64-'
57-
assert_not_file_has_content out.txt ' Installed:.*test-bootupd-payload'
58-
assert_not_file_has_content out.txt ' Installed:.*'"${TARGET_GRUB_PKG}"
59-
assert_file_has_content out.txt 'Update: Available:.*'"${TARGET_GRUB_PKG}"
60-
assert_file_has_content out.txt 'Update: Available:.*test-bootupd-payload-1.0'
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'
6161
bootupctl status --print-if-available > out.txt
6262
assert_file_has_content_literal 'out.txt' 'Updates available: BIOS EFI'
6363
ok update avail
@@ -74,7 +74,7 @@ assert_file_has_content err.txt "error: .*synthetic failpoint"
7474

7575
bootupctl update -vvv | tee out.txt
7676
assert_file_has_content out.txt "Previous EFI: .*"
77-
assert_file_has_content out.txt "Updated EFI: ${TARGET_GRUB_PKG}.*,test-bootupd-payload-1.0"
77+
assert_file_has_content out.txt "Updated EFI: .*${TARGET_GRUB_EVR}.*,test_bootupd_payload-1.0"
7878

7979
assert_file_has_content ${tmpefimount}/EFI/fedora/test-bootupd.efi test-payload
8080

tests/e2e-update/e2e-update.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export test_tmpdir=${testtmp}
2525
# This is new content for our update
2626
test_bootupd_payload_file=/boot/efi/EFI/fedora/test-bootupd.efi
2727
test_bootupd_payload_file1=/boot/efi/EFI/BOOT/test-bootupd1.efi
28-
build_rpm test-bootupd-payload \
28+
build_rpm test_bootupd_payload \
2929
files "${test_bootupd_payload_file}
3030
${test_bootupd_payload_file1}" \
3131
install "mkdir -p %{buildroot}/$(dirname ${test_bootupd_payload_file})
@@ -56,9 +56,9 @@ if test -z "${e2e_skip_build:-}"; then
5656
runv cosa build
5757
runv cosa osbuild qemu
5858
prev_image=$(runv cosa meta --image-path qemu)
59-
# Modify manifest to include `test-bootupd-payload` RPM
59+
# Modify manifest to include `test_bootupd_ayload` RPM
6060
runv git -C src/config checkout manifest.yaml # first make sure it's clean
61-
echo "packages: [test-bootupd-payload]" >> src/config/manifest.yaml
61+
echo "packages: [test_bootupd_payload]" >> src/config/manifest.yaml
6262
rm -f ${overrides}/rpm/*.rpm
6363
echo "Building update ostree"
6464
# Latest (current) version in F42
@@ -77,7 +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)
80+
target_grub_evr=$(rpm -qp --queryformat='%{evr}\n' ${overrides}/rpm/${target_grub_name}-2*.rpm)
8181
target_commit=$(cosa meta --get-value ostree-commit)
8282
echo "Target commit: ${target_commit}"
8383
# For some reason 9p can't write to tmpfs
@@ -97,7 +97,7 @@ systemd:
9797
RemainAfterExit=yes
9898
Environment=TARGET_COMMIT=${target_commit}
9999
Environment=TARGET_GRUB_NAME=${target_grub_name}
100-
Environment=TARGET_GRUB_PKG=${target_grub_pkg}
100+
Environment=TARGET_GRUB_EVR=${target_grub_evr}
101101
Environment=SRCDIR=/run/bootupd-source
102102
# Run via shell because selinux denies systemd writing to 9p apparently
103103
ExecStart=/bin/sh -c '/run/bootupd-source/${testprefix}/e2e-update-in-vm.sh &>>/run/testtmp/out.txt; test -f /run/rebooting || poweroff -ff'

0 commit comments

Comments
 (0)