Skip to content

Commit 3acb674

Browse files
authored
Merge pull request #938 from HuijingHei/generate-metadata-from-usr
step1: Extend `generate-update-metadata()` to read from `/usr`
2 parents 1645ef3 + 3ac4491 commit 3acb674

File tree

2 files changed

+144
-25
lines changed

2 files changed

+144
-25
lines changed

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,36 @@ jobs:
106106
# Verify we injected static configs
107107
jq -re '.["static-configs"].version' /boot/bootupd-state.json
108108
[ $(sudo stat -c "%a" /boot/grub2/grub.cfg) == "600" ]
109+
110+
- name: bootupctl generate-update-metadata
111+
run: |
112+
set -xeuo pipefail
113+
# This will be helpful when we extend /usr
114+
sudo podman run --rm -it --privileged localhost/bootupd:latest \
115+
bash -c '
116+
updates=/usr/lib/bootupd/updates
117+
rm -fv ${updates}/{BIOS,EFI}.json
118+
cp -r ${updates}/EFI /usr/lib/ostree-boot/efi
119+
# prepare /usr/lib/efi/<grub2|shim>/<ver>
120+
if [ ! -d "/usr/lib/efi" ]; then
121+
arch="$(uname --machine)"
122+
if [[ "${arch}" == "x86_64" ]]; then
123+
surfix="x64"
124+
else
125+
# Assume aarch64 for now
126+
surfix="aa64"
127+
fi
128+
129+
grub_ver=$(rpm -qa grub2-efi-${surfix} --queryformat '%{VERSION}-%{RELEASE}')
130+
mkdir -p /usr/lib/efi/grub2/${grub_ver}/EFI/centos
131+
mv ${updates}/EFI/centos/grub${surfix}.efi /usr/lib/efi/grub2/${grub_ver}/EFI/centos/
132+
133+
shim_ver=$(rpm -qa shim-${surfix} --queryformat '%{VERSION}-%{RELEASE}')
134+
mkdir -p /usr/lib/efi/shim/${shim_ver}/EFI/
135+
mv ${updates}/EFI /usr/lib/efi/shim/${shim_ver}/
136+
else
137+
rm -rf ${updates}/EFI
138+
fi
139+
bootupctl backend generate-update-metadata -vvv
140+
cat ${updates}/EFI.json | jq
141+
'

src/efi.rs

Lines changed: 111 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ use std::process::Command;
1111

1212
use anyhow::{bail, Context, Result};
1313
use bootc_internal_utils::CommandRunExt;
14+
use camino::{Utf8Path, Utf8PathBuf};
1415
use cap_std::fs::Dir;
1516
use cap_std_ext::cap_std;
17+
use chrono::prelude::*;
1618
use fn_error_context::context;
1719
use openat_ext::OpenatDirExt;
1820
use os_release::OsRelease;
@@ -31,6 +33,9 @@ use crate::{component::*, packagesystem};
3133
/// Well-known paths to the ESP that may have been mounted external to us.
3234
pub(crate) const ESP_MOUNTS: &[&str] = &["boot/efi", "efi", "boot"];
3335

36+
/// New efi dir under usr/lib
37+
const EFILIB: &str = "usr/lib/efi";
38+
3439
/// The binary to change EFI boot ordering
3540
const EFIBOOTMGR: &str = "efibootmgr";
3641
#[cfg(target_arch = "aarch64")]
@@ -427,38 +432,77 @@ impl Component for Efi {
427432
})
428433
}
429434

430-
fn generate_update_metadata(&self, sysroot_path: &str) -> Result<ContentMetadata> {
431-
let ostreebootdir = Path::new(sysroot_path).join(ostreeutil::BOOT_PREFIX);
432-
let dest_efidir = component_updatedir(sysroot_path, self);
433-
434-
if ostreebootdir.exists() {
435-
let cruft = ["loader", "grub2"];
436-
for p in cruft.iter() {
437-
let p = ostreebootdir.join(p);
438-
if p.exists() {
439-
std::fs::remove_dir_all(&p)?;
435+
fn generate_update_metadata(&self, sysroot: &str) -> Result<ContentMetadata> {
436+
let sysroot_path = Utf8Path::new(sysroot);
437+
438+
// copy EFI files to updates dir from usr/lib/efi
439+
let meta = if sysroot_path.join(EFILIB).exists() {
440+
let mut packages = Vec::new();
441+
for efi_dir in get_efi_path_from_usr(&sysroot_path)? {
442+
let efi_dir = efi_dir.context("Invalid EFI directory path")?;
443+
444+
let sysroot_dir =
445+
Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?;
446+
Command::new("cp")
447+
.args(["-rp", "--reflink=auto"])
448+
.arg(&efi_dir)
449+
.arg(crate::model::BOOTUPD_UPDATES_DIR)
450+
.current_dir(format!("/proc/self/fd/{}", sysroot_dir.as_raw_fd()))
451+
.run()?;
452+
453+
if let Ok(relative) = efi_dir.strip_prefix(EFILIB) {
454+
let mut components = relative.components();
455+
456+
if let (Some(pkg), Some(ver)) = (components.next(), components.next()) {
457+
packages.push(format!("{}-{}", pkg.as_str(), ver.as_str()));
458+
}
440459
}
441460
}
442461

443-
let efisrc = ostreebootdir.join("efi/EFI");
444-
if !efisrc.exists() {
445-
bail!("Failed to find {:?}", &efisrc);
462+
// change to now to workaround https://github.com/coreos/bootupd/issues/933
463+
let timestamp = std::time::SystemTime::now();
464+
ContentMetadata {
465+
timestamp: chrono::DateTime::<Utc>::from(timestamp),
466+
version: packages.join(","),
446467
}
468+
} else {
469+
let ostreebootdir = sysroot_path.join(ostreeutil::BOOT_PREFIX);
470+
471+
// move EFI files to updates dir from /usr/lib/ostree-boot
472+
if ostreebootdir.exists() {
473+
let cruft = ["loader", "grub2"];
474+
for p in cruft.iter() {
475+
let p = ostreebootdir.join(p);
476+
if p.exists() {
477+
std::fs::remove_dir_all(&p)?;
478+
}
479+
}
447480

448-
// Fork off mv() because on overlayfs one can't rename() a lower level
449-
// directory today, and this will handle the copy fallback.
450-
Command::new("mv").args([&efisrc, &dest_efidir]).run()?;
451-
}
481+
let efisrc = ostreebootdir.join("efi/EFI");
482+
if !efisrc.exists() {
483+
bail!("Failed to find {:?}", &efisrc);
484+
}
452485

453-
let efidir = openat::Dir::open(&dest_efidir)
454-
.with_context(|| format!("Opening {}", dest_efidir.display()))?;
455-
let files = crate::util::filenames(&efidir)?.into_iter().map(|mut f| {
456-
f.insert_str(0, "/boot/efi/EFI/");
457-
f
458-
});
486+
let dest_efidir = component_updatedir(sysroot, self);
487+
let dest_efidir =
488+
Utf8PathBuf::from_path_buf(dest_efidir).expect("Path is invalid UTF-8");
489+
// Fork off mv() because on overlayfs one can't rename() a lower level
490+
// directory today, and this will handle the copy fallback.
491+
Command::new("mv").args([&efisrc, &dest_efidir]).run()?;
492+
493+
let efidir = openat::Dir::open(dest_efidir.as_std_path())
494+
.with_context(|| format!("Opening {}", dest_efidir))?;
495+
let files = crate::util::filenames(&efidir)?.into_iter().map(|mut f| {
496+
f.insert_str(0, "/boot/efi/EFI/");
497+
f
498+
});
499+
packagesystem::query_files(sysroot, files)?
500+
} else {
501+
anyhow::bail!("Failed to find {ostreebootdir}");
502+
}
503+
};
459504

460-
let meta = packagesystem::query_files(sysroot_path, files)?;
461-
write_update_metadata(sysroot_path, self, &meta)?;
505+
write_update_metadata(sysroot, self, &meta)?;
462506
Ok(meta)
463507
}
464508

@@ -654,6 +698,29 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
654698
Ok(result)
655699
}
656700

701+
/// Get EFI path under usr/lib/efi, eg. usr/lib/efi/<package>/<version>/EFI
702+
fn get_efi_path_from_usr(
703+
sysroot: &Utf8Path,
704+
) -> Result<impl Iterator<Item = Result<Utf8PathBuf>> + use<'_>> {
705+
let efilib_path = sysroot.join(EFILIB);
706+
707+
Ok(WalkDir::new(efilib_path)
708+
.min_depth(3)
709+
.max_depth(3)
710+
.into_iter()
711+
.filter_entry(|e| e.file_type().is_dir() && e.file_name() == "EFI")
712+
.map(move |entry| {
713+
let abs_path = entry.context("Failed to read directory entry")?.into_path();
714+
715+
let rel_path = abs_path
716+
.strip_prefix(sysroot)
717+
.context("Failed to strip sysroot prefix")?;
718+
719+
Utf8PathBuf::from_path_buf(rel_path.to_path_buf())
720+
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 path : {}", e.display()))
721+
}))
722+
}
723+
657724
#[cfg(test)]
658725
mod tests {
659726
use cap_std_ext::dirext::CapStdExtDirExt;
@@ -774,4 +841,23 @@ Boot0003* test";
774841
}
775842
Ok(())
776843
}
844+
845+
#[test]
846+
fn test_get_efi_path_from_usr() -> Result<()> {
847+
let tmpdir: &tempfile::TempDir = &tempfile::tempdir()?;
848+
let tpath = tmpdir.path();
849+
let efi_path = tpath.join("usr/lib/efi");
850+
std::fs::create_dir_all(efi_path.join("FOO/1.1/EFI"))?;
851+
std::fs::create_dir_all(efi_path.join("BAR/1.1/EFI"))?;
852+
std::fs::create_dir_all(efi_path.join("FOOBAR/1.1/test"))?;
853+
let utf8_tpath =
854+
Utf8Path::from_path(tpath).ok_or_else(|| anyhow::anyhow!("Path is not valid UTF-8"))?;
855+
let efi_path_iter = get_efi_path_from_usr(utf8_tpath)?;
856+
let paths: Vec<Utf8PathBuf> = efi_path_iter.filter_map(Result::ok).collect();
857+
assert_eq!(
858+
paths,
859+
["usr/lib/efi/FOO/1.1/EFI", "usr/lib/efi/BAR/1.1/EFI"]
860+
);
861+
Ok(())
862+
}
777863
}

0 commit comments

Comments
 (0)