Skip to content

Commit e3276df

Browse files
committed
feat(extend_payload_to_esp):add payload to bootupd efi updates dir.
Bootup extend_payload_to_esp <path> will move the it to /usr This features gives user ability to stage payloads to be picked up by metadata generator.This will help in mounting fimwares and uboot binaries to esp as required by rpi4. example usage: `bootupctl backend extend_payload_to_esp /usr/lib/mydata` will move the content of the dir to `/usr/lib/efi/firmware/<name>/<version>/EFI/boot/efi/`
1 parent 5e99cf6 commit e3276df

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

src/bios.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,7 @@ impl Component for Bios {
267267
fn get_efi_vendor(&self, _: &openat::Dir) -> Result<Option<String>> {
268268
Ok(None)
269269
}
270+
fn extend_payload(&self, _: &str, _: &str) -> Result<Option<bool>> {
271+
Ok(None)
272+
}
270273
}

src/cli/bootupctl.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub enum CtlBackend {
7373
Generate(super::bootupd::GenerateOpts),
7474
#[clap(name = "install", hide = true)]
7575
Install(super::bootupd::InstallOpts),
76+
#[clap(name = "extend-payload-to-esp", hide = true)]
77+
ExtendPayload(super::bootupd::ExtendPayloadOpts),
7678
}
7779

7880
#[derive(Debug, Parser)]
@@ -109,6 +111,9 @@ impl CtlCommand {
109111
CtlVerb::Backend(CtlBackend::Install(opts)) => {
110112
super::bootupd::DCommand::run_install(opts)
111113
}
114+
CtlVerb::Backend(CtlBackend::ExtendPayload(opts)) => {
115+
super::bootupd::DCommand::run_extend_payload(opts)
116+
}
112117
CtlVerb::MigrateStaticGrubConfig => Self::run_migrate_static_grub_config(),
113118
}
114119
}

src/cli/bootupd.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ pub enum DVerb {
3535
GenerateUpdateMetadata(GenerateOpts),
3636
#[clap(name = "install", about = "Install components")]
3737
Install(InstallOpts),
38+
#[clap(
39+
name = "extend-payload-to-esp",
40+
about = "Extend bootloader payload with additional files"
41+
)]
42+
ExtendPayload(ExtendPayloadOpts),
3843
}
3944

4045
#[derive(Debug, Parser)]
@@ -82,12 +87,20 @@ pub struct GenerateOpts {
8287
sysroot: Option<String>,
8388
}
8489

90+
#[derive(Debug, Parser)]
91+
pub struct ExtendPayloadOpts {
92+
/// Source directory containing files to add
93+
#[clap(value_parser)]
94+
src_root: String,
95+
}
96+
8597
impl DCommand {
8698
/// Run CLI application.
8799
pub fn run(self) -> Result<()> {
88100
match self.cmd {
89101
DVerb::Install(opts) => Self::run_install(opts),
90102
DVerb::GenerateUpdateMetadata(opts) => Self::run_generate_meta(opts),
103+
DVerb::ExtendPayload(opts) => Self::run_extend_payload(opts),
91104
}
92105
}
93106

@@ -122,4 +135,17 @@ impl DCommand {
122135
.context("boot data installation failed")?;
123136
Ok(())
124137
}
138+
139+
pub(crate) fn run_extend_payload(opts: ExtendPayloadOpts) -> Result<()> {
140+
let components = crate::bootupd::get_components();
141+
let sysroot = "/";
142+
for component in components.values() {
143+
if let Some(updated) = component.extend_payload(sysroot, &opts.src_root)? {
144+
if updated {
145+
println!("Extended payload for {} successfully", component.name());
146+
}
147+
}
148+
}
149+
Ok(())
150+
}
125151
}

src/component.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub(crate) trait Component {
7979

8080
/// Locating efi vendor dir
8181
fn get_efi_vendor(&self, sysroot: &openat::Dir) -> Result<Option<String>>;
82+
83+
/// Extending payload from input dir
84+
fn extend_payload(&self, sysroot: &str, src_root: &str) -> Result<Option<bool>>;
8285
}
8386

8487
/// Given a component name, create an implementation.

src/efi.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::os::unix::io::AsRawFd;
99
use std::path::{Path, PathBuf};
1010
use std::process::Command;
1111

12-
use anyhow::{bail, Context, Result};
12+
use anyhow::{anyhow, bail, Context, Result};
1313
use bootc_utils::CommandRunExt;
1414
use cap_std::fs::Dir;
1515
use cap_std_ext::cap_std;
@@ -454,6 +454,93 @@ impl Component for Efi {
454454
Ok(meta)
455455
}
456456

457+
fn extend_payload(&self, sysroot_path: &str, src_input: &str) -> Result<Option<bool>> {
458+
let dest_efidir_base = Path::new(sysroot_path).join("usr/lib/efi").join("firmware");
459+
460+
// Fetch version and release from the source input using query_files
461+
let src_input_path = Path::new(src_input);
462+
let meta_from_src = packagesystem::query_files(sysroot_path, [src_input_path])
463+
.context(format!("Querying RPM metadata for {:?}", src_input_path))?;
464+
465+
let version_string_part = meta_from_src.version.splitn(2, ',').next()
466+
.ok_or_else(|| anyhow!(
467+
"RPM query returned an empty or malformed version string (no package name found in '{}').",
468+
meta_from_src.version
469+
))?;
470+
471+
let parts: Vec<&str> = version_string_part.split('-').collect();
472+
473+
let (pkg_name, version_release_str) = if parts.len() >= 3 {
474+
// Successfully extracted package name, version, and release
475+
let actual_pkg_name = parts[0];
476+
let version_part = parts[parts.len() - 2]; // version, e.g., "1.0"
477+
let release_part = parts[parts.len() - 1]
478+
.split('.')
479+
.next()
480+
.unwrap_or(parts[parts.len() - 1]); // release, e.g., "1" from "1.el8.noarch"
481+
(
482+
actual_pkg_name.to_string(),
483+
format!("{}-{}", version_part, release_part),
484+
)
485+
} else {
486+
return Err(anyhow!(
487+
"Unexpected RPM version string format: '{}'. Expected at least <pkg_name>-<version>-<release>.",
488+
version_string_part
489+
));
490+
};
491+
492+
let final_dest_efi_path = dest_efidir_base
493+
.join(&pkg_name) // add the package name as a directory
494+
.join(&version_release_str)
495+
.join("EFI/boot/efi");
496+
497+
// Ensure the destination directory exists
498+
std::fs::create_dir_all(&final_dest_efi_path).with_context(|| {
499+
format!(
500+
"Failed to create destination directory {:?}",
501+
&final_dest_efi_path
502+
)
503+
})?;
504+
505+
let src_metadata = std::fs::metadata(src_input_path)
506+
.with_context(|| format!("Failed to get metadata for {:?}", src_input_path))?;
507+
508+
if src_metadata.is_dir() {
509+
log::debug!(
510+
"Copying contents of directory {:?} to {:?}",
511+
src_input,
512+
&final_dest_efi_path
513+
);
514+
Command::new("cp")
515+
.args([
516+
"-rp",
517+
&format!("{}/.", src_input),
518+
final_dest_efi_path.to_str().unwrap(),
519+
])
520+
.run()
521+
.with_context(|| {
522+
format!(
523+
"Failed to copy contents of {:?} to {:?}",
524+
src_input, &final_dest_efi_path
525+
)
526+
})?;
527+
} else if src_metadata.is_file() {
528+
log::debug!("Copying file {:?} to {:?}", src_input, &final_dest_efi_path);
529+
Command::new("cp")
530+
.args(["-p", src_input, final_dest_efi_path.to_str().unwrap()])
531+
.run()
532+
.with_context(|| {
533+
format!(
534+
"Failed to copy file {:?} to {:?}",
535+
src_input, &final_dest_efi_path
536+
)
537+
})?;
538+
} else {
539+
anyhow::bail!("Unsupported src_input type: {:?}", src_input);
540+
}
541+
Ok(Some(true))
542+
}
543+
457544
fn query_update(&self, sysroot: &openat::Dir) -> Result<Option<ContentMetadata>> {
458545
get_component_update(sysroot, self)
459546
}

0 commit comments

Comments
 (0)