From 7773ce48cb078a9c4d86817cfa42dfd8e0aceccf Mon Sep 17 00:00:00 2001 From: iTrooz Date: Fri, 11 Aug 2023 11:51:49 +0200 Subject: [PATCH] feat(efiboot): add `efiboot dump` and `efiboot import` (#64) --- Cargo.lock | 1 + efiboot/Cargo.toml | 1 + efiboot/src/cli.rs | 4 ++++ efiboot/src/cli/dump.rs | 39 +++++++++++++++++++++++++++++++ efiboot/src/cli/import.rs | 49 +++++++++++++++++++++++++++++++++++++++ efiboot/src/main.rs | 42 +++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+) create mode 100644 efiboot/src/cli/dump.rs create mode 100644 efiboot/src/cli/import.rs diff --git a/Cargo.lock b/Cargo.lock index 9c1382bb..c70b0348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,7 @@ dependencies = [ name = "efiboot" version = "1.3.0" dependencies = [ + "byteorder", "efivar", "itertools", "paw", diff --git a/efiboot/Cargo.toml b/efiboot/Cargo.toml index 25ecd4e0..acb1e836 100644 --- a/efiboot/Cargo.toml +++ b/efiboot/Cargo.toml @@ -13,6 +13,7 @@ description = "EFI boot manager variable editor written in Rust" edition = "2018" [dependencies] +byteorder = "1.4.3" efivar = { version = "1.3.0", path = "../efivar", features = ["store"] } itertools = "0.11.0" paw = "1.0.0" diff --git a/efiboot/src/cli.rs b/efiboot/src/cli.rs index 7fefe7df..c8dc9065 100644 --- a/efiboot/src/cli.rs +++ b/efiboot/src/cli.rs @@ -1,10 +1,14 @@ mod boot; mod delete; +mod dump; +mod import; mod list; mod read; pub use self::boot::get_entries as get_boot_entries; pub use self::boot::get_order as get_boot_order; pub use self::delete::run as delete; +pub use self::dump::run as dump; +pub use self::import::run as import; pub use self::list::run as list; pub use self::read::run as read; diff --git a/efiboot/src/cli/dump.rs b/efiboot/src/cli/dump.rs new file mode 100644 index 00000000..b2c5f8bd --- /dev/null +++ b/efiboot/src/cli/dump.rs @@ -0,0 +1,39 @@ +use std::{fs::File, io::Write, path::Path}; + +use uuid::Uuid; + +use efivar::{ + efi::{VariableFlags, VariableName, VariableVendor}, + VarManager, +}; + +fn dump(output_path: &Path, flags: VariableFlags, data: &[u8]) -> Result<(), std::io::Error> { + let mut file = File::create(output_path)?; + file.write_all(&flags.bits().to_le_bytes())?; + file.write_all(data)?; + + Ok(()) +} + +pub fn run(reader: Box, name: &str, namespace: Option, output_path: &Path) { + let var = VariableName::new_with_vendor( + name, + namespace.map_or(VariableVendor::Efi, VariableVendor::Custom), + ); + + let mut buf = vec![0u8; 512]; + match reader.read(&var, &mut buf) { + Ok((size, flags)) => { + buf.resize(size, 0); + match dump(output_path, flags, &buf) { + Ok(_) => println!( + "Dumped variable {} to file {}", + var, + output_path.canonicalize().unwrap().display() + ), + Err(err) => eprintln!("Failed to write to file: {}", err), + } + } + Err(err) => eprintln!("Failed to read variable: {}", err), + } +} diff --git a/efiboot/src/cli/import.rs b/efiboot/src/cli/import.rs new file mode 100644 index 00000000..cf6daec5 --- /dev/null +++ b/efiboot/src/cli/import.rs @@ -0,0 +1,49 @@ +use std::{fs::File, io::Read, path::Path}; + +use uuid::Uuid; + +use byteorder::{LittleEndian, ReadBytesExt}; + +use efivar::{ + efi::{VariableFlags, VariableName, VariableVendor}, + VarManager, +}; + +fn read_var_from_file(input_path: &Path) -> Result<(VariableFlags, Vec), std::io::Error> { + let mut file = File::open(input_path)?; + + let flags = VariableFlags::from_bits(file.read_u32::()?).unwrap(); + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + + Ok((flags, data)) +} + +pub fn run( + mut manager: Box, + input_path: &Path, + name: &str, + namespace: Option, +) { + let var = VariableName::new_with_vendor( + name, + namespace.map_or(VariableVendor::Efi, VariableVendor::Custom), + ); + + let (flags, data) = match read_var_from_file(input_path) { + Ok(inner) => inner, + Err(err) => { + eprintln!( + "Failed to read variable from file {}: {}", + input_path.display(), + err + ); + return; + } + }; + + match manager.write(&var, flags, &data) { + Ok(()) => println!("Imported variable {} with success", var), + Err(err) => eprintln!("Failed to write variable {}: {}", var, err), + } +} diff --git a/efiboot/src/main.rs b/efiboot/src/main.rs index e337a13f..f6d54b3b 100644 --- a/efiboot/src/main.rs +++ b/efiboot/src/main.rs @@ -52,6 +52,34 @@ enum Command { /// Manage boot-related variables Boot(BootCommand), + /// Dump a variable to file + Dump { + /// Name of the variable to dump + #[structopt(value_name = "VARIABLE")] + name: String, + + /// GUID of the namespace. Default: EFI standard namespace + #[structopt(short, long, value_name = "NAMESPACE")] + namespace: Option, + + /// Output file + #[structopt(value_name = "OUTPUT_FILE")] + output_file: PathBuf, + }, + /// Import a variable from a file + Import { + /// Input file + #[structopt(value_name = "OUTPUT_FILE")] + input_file: PathBuf, + + /// Name of the variable to create + #[structopt(value_name = "VARIABLE")] + name: String, + + /// GUID of the namespace. Default: EFI standard namespace + #[structopt(short, long, value_name = "NAMESPACE")] + namespace: Option, + }, } #[derive(StructOpt)] @@ -101,5 +129,19 @@ fn main(opts: Opt) { cli::get_boot_entries(manager, verbose); } }, + Command::Dump { + name, + namespace, + output_file, + } => { + cli::dump(manager, &name, namespace, &output_file); + } + Command::Import { + input_file, + name, + namespace, + } => { + cli::import(manager, &input_file, &name, namespace); + } } }