From 164d015a0241d697d936ecfefaf6d166523937e2 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Wed, 4 Jan 2023 18:53:08 -0500 Subject: [PATCH 01/33] Replace UefiBoot and BiosBoot with DiskImageBuilder --- build.rs | 1 + src/bios/mod.rs | 98 ------------------------ src/{uefi => }/gpt.rs | 0 src/lib.rs | 163 ++++++++++++++++++++++++++++++++++++++-- src/{bios => }/mbr.rs | 0 src/uefi/mod.rs | 106 -------------------------- src/uefi/pxe.rs | 50 ------------ tests/runner/src/lib.rs | 14 ++++ 8 files changed, 171 insertions(+), 261 deletions(-) delete mode 100644 src/bios/mod.rs rename src/{uefi => }/gpt.rs (100%) rename src/{bios => }/mbr.rs (100%) delete mode 100644 src/uefi/mod.rs delete mode 100644 src/uefi/pxe.rs diff --git a/build.rs b/build.rs index b5652bc6..275fbdea 100644 --- a/build.rs +++ b/build.rs @@ -266,6 +266,7 @@ async fn build_bios_stage_4(out_dir: &Path) -> PathBuf { convert_elf_to_bin(elf_path).await } +#[cfg(not(docsrs_dummy_build))] #[cfg(feature = "bios")] async fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf { let flat_binary_path = elf_path.with_extension("bin"); diff --git a/src/bios/mod.rs b/src/bios/mod.rs deleted file mode 100644 index ec0db402..00000000 --- a/src/bios/mod.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::fat; -use anyhow::Context; -use bootloader_boot_config::BootConfig; -use std::io::Write; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, -}; -use tempfile::NamedTempFile; - -mod mbr; - -const BIOS_STAGE_3: &str = "boot-stage-3"; -const BIOS_STAGE_4: &str = "boot-stage-4"; - -/// Create disk images for booting on legacy BIOS systems. -pub struct BiosBoot { - kernel: PathBuf, - ramdisk: Option, - config: Option, -} - -impl BiosBoot { - /// Start creating a disk image for the given bootloader ELF executable. - pub fn new(kernel_path: &Path) -> Self { - Self { - kernel: kernel_path.to_owned(), - ramdisk: None, - config: None, - } - } - - /// Add a ramdisk file to the image. - pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { - self.ramdisk = Some(ramdisk_path.to_owned()); - self - } - - /// Configures the runtime behavior of the bootloader. - pub fn set_boot_config(&mut self, config: &BootConfig) -> &mut Self { - self.config = Some(serde_json::to_string(&config).expect("failed to serialize BootConfig")); - self - } - - /// Create a bootable BIOS disk image at the given path. - pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { - let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH")); - let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH")); - - let fat_partition = self - .create_fat_partition() - .context("failed to create FAT partition")?; - - mbr::create_mbr_disk( - bootsector_path, - stage_2_path, - fat_partition.path(), - out_path, - ) - .context("failed to create BIOS MBR disk image")?; - - fat_partition - .close() - .context("failed to delete FAT partition after disk image creation")?; - - Ok(()) - } - - /// Creates an BIOS-bootable FAT partition with the kernel. - fn create_fat_partition(&self) -> anyhow::Result { - let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH")); - let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH")); - - let mut files = BTreeMap::new(); - files.insert(crate::KERNEL_FILE_NAME, self.kernel.as_path()); - files.insert(BIOS_STAGE_3, stage_3_path); - files.insert(BIOS_STAGE_4, stage_4_path); - if let Some(ramdisk_path) = &self.ramdisk { - files.insert(crate::RAMDISK_FILE_NAME, ramdisk_path); - } - - let mut config_file: NamedTempFile; - - if let Some(config_ser) = &self.config { - config_file = NamedTempFile::new() - .context("failed to create temp file") - .unwrap(); - writeln!(config_file, "{config_ser}")?; - files.insert(crate::CONFIG_FILE_NAME, config_file.path()); - } - - let out_file = NamedTempFile::new().context("failed to create temp file")?; - fat::create_fat_filesystem(files, out_file.path()) - .context("failed to create BIOS FAT filesystem")?; - - Ok(out_file) - } -} diff --git a/src/uefi/gpt.rs b/src/gpt.rs similarity index 100% rename from src/uefi/gpt.rs rename to src/gpt.rs diff --git a/src/lib.rs b/src/lib.rs index ff1894de..50aa328e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,20 +4,169 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems. #![warn(missing_docs)] +#[cfg(feature = "uefi")] +mod gpt; #[cfg(feature = "bios")] -mod bios; +mod mbr; + mod fat; -#[cfg(feature = "uefi")] -mod uefi; -#[cfg(feature = "bios")] -pub use bios::BiosBoot; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; -#[cfg(feature = "uefi")] -pub use uefi::UefiBoot; +use anyhow::Context; + +use tempfile::NamedTempFile; pub use bootloader_boot_config::BootConfig; const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; const CONFIG_FILE_NAME: &str = "boot.json"; + +struct DiskImageFile<'a> { + source: &'a PathBuf, + destination: &'a str, +} + +/// DiskImageBuilder helps create disk images for a specified set of files. +/// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images. +pub struct DiskImageBuilder<'a> { + files: Vec>, +} + +impl<'a> DiskImageBuilder<'a> { + /// Create a new instance of DiskImageBuilder, with the specified kernel. + pub fn new(kernel: &'a PathBuf) -> Self { + let mut obj = Self::empty(); + obj.set_kernel(kernel); + obj + } + + /// Create a new, empty instance of DiskImageBuilder + pub fn empty() -> Self { + Self { files: Vec::new() } + } + + /// Add or replace a kernel to be included in the final image. + pub fn set_kernel(&mut self, path: &'a PathBuf) -> &mut Self { + self.add_or_replace_file(path, KERNEL_FILE_NAME) + } + + /// Add or replace a ramdisk to be included in the final image. + pub fn set_ramdisk(&mut self, path: &'a PathBuf) -> &mut Self { + self.add_or_replace_file(&path, RAMDISK_FILE_NAME) + } + + /// Add or replace arbitrary files. + /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI + /// This can be useful in situations where you want to generate an image, but not use the provided bootloader. + pub fn add_or_replace_file(&mut self, path: &'a PathBuf, target: &'a str) -> &mut Self { + self.files.insert( + 0, + DiskImageFile::<'a> { + source: &path, + destination: &target, + }, + ); + self + } + fn create_fat_filesystem_image( + &self, + internal_files: BTreeMap<&'a str, &'a Path>, + ) -> anyhow::Result { + let mut local_map = BTreeMap::new(); + + for k in internal_files { + local_map.insert(k.0, k.1); + } + + for f in self.files.as_slice() { + local_map.insert(f.destination, &f.source.as_path()); + } + + let out_file = NamedTempFile::new().context("failed to create temp file")?; + fat::create_fat_filesystem(local_map, out_file.path()) + .context("failed to create BIOS FAT filesystem")?; + + Ok(out_file) + } + #[cfg(feature = "bios")] + /// Create an MBR disk image for booting on BIOS systems. + pub fn create_bios_image(&self, image_filename: &Path) -> anyhow::Result<()> { + const BIOS_STAGE_3: &str = "boot-stage-3"; + const BIOS_STAGE_4: &str = "boot-stage-4"; + let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH")); + let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH")); + let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH")); + let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH")); + let mut internal_files = BTreeMap::new(); + internal_files.insert(BIOS_STAGE_3, stage_3_path); + internal_files.insert(BIOS_STAGE_4, stage_4_path); + + let fat_partition = self + .create_fat_filesystem_image(internal_files) + .context("failed to create FAT partition")?; + mbr::create_mbr_disk( + bootsector_path, + stage_2_path, + fat_partition.path(), + image_filename, + ) + .context("failed to create BIOS MBR disk image")?; + + fat_partition + .close() + .context("failed to delete FAT partition after disk image creation")?; + Ok(()) + } + + #[cfg(feature = "uefi")] + /// Create a GPT disk image for booting on UEFI systems. + pub fn create_uefi_image(&self, image_filename: &Path) -> anyhow::Result<()> { + const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi"; + let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); + let mut internal_files = BTreeMap::new(); + internal_files.insert(UEFI_BOOT_FILENAME, bootloader_path); + let fat_partition = self + .create_fat_filesystem_image(internal_files) + .context("failed to create FAT partition")?; + gpt::create_gpt_disk(fat_partition.path(), image_filename) + .context("failed to create UEFI GPT disk image")?; + fat_partition + .close() + .context("failed to delete FAT partition after disk image creation")?; + + Ok(()) + } + + #[cfg(feature = "uefi")] + /// Create a folder containing the needed files for UEFI TFTP/PXE booting. + pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> { + const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader"; + let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); + std::fs::create_dir_all(tftp_path) + .with_context(|| format!("failed to create out dir at {}", tftp_path.display()))?; + + let to = tftp_path.join(UEFI_TFTP_BOOT_FILENAME); + std::fs::copy(bootloader_path, &to).with_context(|| { + format!( + "failed to copy bootloader from {} to {}", + bootloader_path.display(), + to.display() + ) + })?; + + for f in self.files.as_slice() { + let to = tftp_path.join(f.destination); + let result = std::fs::copy(f.source, to); + if result.is_err() { + return Err(anyhow::Error::from(result.unwrap_err())); + } + } + + Ok(()) + } +} diff --git a/src/bios/mbr.rs b/src/mbr.rs similarity index 100% rename from src/bios/mbr.rs rename to src/mbr.rs diff --git a/src/uefi/mod.rs b/src/uefi/mod.rs deleted file mode 100644 index 030ea9bb..00000000 --- a/src/uefi/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::fat; -use anyhow::Context; -use bootloader_boot_config::BootConfig; -use std::io::Write; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, -}; -use tempfile::NamedTempFile; - -mod gpt; -mod pxe; - -/// Create disk images for booting on UEFI systems. -pub struct UefiBoot { - kernel: PathBuf, - ramdisk: Option, - config: Option, -} - -impl UefiBoot { - /// Start creating a disk image for the given bootloader ELF executable. - pub fn new(kernel_path: &Path) -> Self { - Self { - kernel: kernel_path.to_owned(), - ramdisk: None, - config: None, - } - } - - /// Add a ramdisk file to the disk image. - pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { - self.ramdisk = Some(ramdisk_path.to_owned()); - self - } - - /// Configures the runtime behavior of the bootloader. - pub fn set_boot_config(&mut self, config: &BootConfig) -> &mut Self { - self.config = Some(serde_json::to_string(&config).expect("failed to serialize BootConfig")); - self - } - - /// Create a bootable UEFI disk image at the given path. - pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { - let fat_partition = self - .create_fat_partition() - .context("failed to create FAT partition")?; - - gpt::create_gpt_disk(fat_partition.path(), out_path) - .context("failed to create UEFI GPT disk image")?; - - fat_partition - .close() - .context("failed to delete FAT partition after disk image creation")?; - - Ok(()) - } - - /// Prepare a folder for use with booting over UEFI_PXE. - /// - /// This places the bootloader executable under the path "bootloader". The - /// DHCP server should set the filename option to that path, otherwise the - /// bootloader won't be found. - pub fn create_pxe_tftp_folder(&self, out_path: &Path) -> anyhow::Result<()> { - let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - - pxe::create_uefi_tftp_folder( - bootloader_path, - self.kernel.as_path(), - self.ramdisk.as_deref(), - self.config.as_deref(), - out_path, - ) - .context("failed to create UEFI PXE tftp folder")?; - - Ok(()) - } - - /// Creates an UEFI-bootable FAT partition with the kernel. - fn create_fat_partition(&self) -> anyhow::Result { - let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - - let mut files = BTreeMap::new(); - files.insert("efi/boot/bootx64.efi", bootloader_path); - files.insert(crate::KERNEL_FILE_NAME, self.kernel.as_path()); - if let Some(ramdisk_path) = &self.ramdisk { - files.insert(crate::RAMDISK_FILE_NAME, ramdisk_path); - } - - let mut config_file: NamedTempFile; - - if let Some(config_ser) = &self.config { - config_file = NamedTempFile::new() - .context("failed to create temp file") - .unwrap(); - writeln!(config_file, "{config_ser}")?; - files.insert(crate::CONFIG_FILE_NAME, config_file.path()); - } - - let out_file = NamedTempFile::new().context("failed to create temp file")?; - fat::create_fat_filesystem(files, out_file.path()) - .context("failed to create UEFI FAT filesystem")?; - - Ok(out_file) - } -} diff --git a/src/uefi/pxe.rs b/src/uefi/pxe.rs deleted file mode 100644 index 4eac7f8c..00000000 --- a/src/uefi/pxe.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::path::Path; - -use anyhow::Context; -use bootloader_boot_config::BootConfig; - -pub fn create_uefi_tftp_folder( - bootloader_path: &Path, - kernel_binary: &Path, - ramdisk_path: Option<&Path>, - boot_config: Option<&str>, - out_path: &Path, -) -> anyhow::Result<()> { - std::fs::create_dir_all(out_path) - .with_context(|| format!("failed to create out dir at {}", out_path.display()))?; - - let to = out_path.join("bootloader"); - std::fs::copy(bootloader_path, &to).with_context(|| { - format!( - "failed to copy bootloader from {} to {}", - bootloader_path.display(), - to.display() - ) - })?; - - let to = out_path.join("kernel-x86_64"); - std::fs::copy(kernel_binary, &to).with_context(|| { - format!( - "failed to copy kernel from {} to {}", - kernel_binary.display(), - to.display() - ) - })?; - let to = out_path.join("ramdisk"); - if let Some(rp) = ramdisk_path { - std::fs::copy(rp, &to).with_context(|| { - format!( - "failed to copy ramdisk from {} to {}", - rp.display(), - to.display() - ) - })?; - } - - if let Some(config) = boot_config { - let to = out_path.join("boot.json"); - std::fs::write(to, config).context("failed to write boot.json")?; - } - - Ok(()) -} diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index a25932ed..8faf096f 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -71,6 +71,20 @@ pub fn run_test_kernel_internal( run_test_kernel_on_bios(&mbr_path); } + + #[cfg(feature = "uefi")] + { + let gpt_path = kernel_path_buf.with_extension("gpt"); + let tftp_path = kernel_path_buf.with_extension("tftp"); + image_builder.create_uefi_image(&gpt_path).unwrap(); + image_builder.create_uefi_tftp_folder(&tftp_path).unwrap(); + run_test_kernel_on_uefi(&gpt_path); + run_test_kernel_on_uefi_pxe(&tftp_path); + } +} + +pub fn run_test_kernel(kernel_binary_path: &str) { + run_test_kernel_with_ramdisk(kernel_binary_path, None); } #[cfg(feature = "uefi")] From 85779d3dae4c0c7b0aafd14684d0a739f718934f Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 20:00:09 -0500 Subject: [PATCH 02/33] Update DiskImageBuilder to remove lifetime requirement by cloning, and convert PathBuf parameters to Path --- src/lib.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 50aa328e..63795cba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,20 +26,21 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; const CONFIG_FILE_NAME: &str = "boot.json"; -struct DiskImageFile<'a> { - source: &'a PathBuf, - destination: &'a str, +#[derive(Clone)] +struct DiskImageFile { + source: PathBuf, + destination: String, } /// DiskImageBuilder helps create disk images for a specified set of files. /// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images. -pub struct DiskImageBuilder<'a> { - files: Vec>, +pub struct DiskImageBuilder { + files: Vec, } -impl<'a> DiskImageBuilder<'a> { +impl DiskImageBuilder { /// Create a new instance of DiskImageBuilder, with the specified kernel. - pub fn new(kernel: &'a PathBuf) -> Self { + pub fn new(kernel: &Path) -> Self { let mut obj = Self::empty(); obj.set_kernel(kernel); obj @@ -51,31 +52,31 @@ impl<'a> DiskImageBuilder<'a> { } /// Add or replace a kernel to be included in the final image. - pub fn set_kernel(&mut self, path: &'a PathBuf) -> &mut Self { + pub fn set_kernel(&mut self, path: &Path) -> &mut Self { self.add_or_replace_file(path, KERNEL_FILE_NAME) } /// Add or replace a ramdisk to be included in the final image. - pub fn set_ramdisk(&mut self, path: &'a PathBuf) -> &mut Self { + pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { self.add_or_replace_file(&path, RAMDISK_FILE_NAME) } /// Add or replace arbitrary files. /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI /// This can be useful in situations where you want to generate an image, but not use the provided bootloader. - pub fn add_or_replace_file(&mut self, path: &'a PathBuf, target: &'a str) -> &mut Self { + pub fn add_or_replace_file(&mut self, path: &Path, target: &str) -> &mut Self { self.files.insert( 0, - DiskImageFile::<'a> { - source: &path, - destination: &target, + DiskImageFile { + source: path.clone().to_path_buf(), + destination: target.to_string(), }, ); self } fn create_fat_filesystem_image( &self, - internal_files: BTreeMap<&'a str, &'a Path>, + internal_files: BTreeMap<&str, &Path>, ) -> anyhow::Result { let mut local_map = BTreeMap::new(); @@ -84,7 +85,7 @@ impl<'a> DiskImageBuilder<'a> { } for f in self.files.as_slice() { - local_map.insert(f.destination, &f.source.as_path()); + local_map.insert(&f.destination, &f.source.as_path()); } let out_file = NamedTempFile::new().context("failed to create temp file")?; @@ -160,8 +161,8 @@ impl<'a> DiskImageBuilder<'a> { })?; for f in self.files.as_slice() { - let to = tftp_path.join(f.destination); - let result = std::fs::copy(f.source, to); + let to = tftp_path.join(f.destination.clone()); + let result = std::fs::copy(f.source.clone(), to); if result.is_err() { return Err(anyhow::Error::from(result.unwrap_err())); } From 5786d9b02087634efe9241c7e4d7ec648ed03d93 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 20:00:35 -0500 Subject: [PATCH 03/33] Reimplement BiosBoot and UefiBoot as wrappers around DiskImageBuilder --- src/bios/mod.rs | 31 +++++++++++++++++++++++++++++++ src/lib.rs | 4 ++++ src/uefi/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/bios/mod.rs create mode 100644 src/uefi/mod.rs diff --git a/src/bios/mod.rs b/src/bios/mod.rs new file mode 100644 index 00000000..69d698e7 --- /dev/null +++ b/src/bios/mod.rs @@ -0,0 +1,31 @@ +use std::path::Path; + +use crate::DiskImageBuilder; + +const BIOS_STAGE_3: &str = "boot-stage-3"; +const BIOS_STAGE_4: &str = "boot-stage-4"; + +/// Create disk images for booting on legacy BIOS systems. +pub struct BiosBoot { + image_builder: DiskImageBuilder +} + +impl BiosBoot { + /// Start creating a disk image for the given bootloader ELF executable. + pub fn new(kernel_path: &Path) -> Self { + Self { + image_builder: DiskImageBuilder::new(kernel_path) + } + } + + /// Add a ramdisk file to the image + pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { + self.image_builder.set_ramdisk(ramdisk_path); + self + } + + /// Create a bootable BIOS disk image at the given path. + pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { + self.image_builder.create_bios_image(out_path) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 63795cba..df1a7818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,10 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems. mod gpt; #[cfg(feature = "bios")] mod mbr; +#[cfg(feature = "bios")] +mod bios; +#[cfg(feature = "uefi")] +mod uefi; mod fat; diff --git a/src/uefi/mod.rs b/src/uefi/mod.rs new file mode 100644 index 00000000..c085a763 --- /dev/null +++ b/src/uefi/mod.rs @@ -0,0 +1,40 @@ +use std::path::Path; + +use crate::DiskImageBuilder; + +const BIOS_STAGE_3: &str = "boot-stage-3"; +const BIOS_STAGE_4: &str = "boot-stage-4"; + +/// Create disk images for booting on legacy BIOS systems. +pub struct UefiBoot { + image_builder: DiskImageBuilder +} + +impl UefiBoot { + /// Start creating a disk image for the given bootloader ELF executable. + pub fn new(kernel_path: &Path) -> Self { + Self { + image_builder: DiskImageBuilder::new(kernel_path) + } + } + + /// Add a ramdisk file to the image + pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { + self.image_builder.set_ramdisk(ramdisk_path); + self + } + + /// Create a bootable UEFI disk image at the given path. + pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { + self.image_builder.create_uefi_image(out_path) + } + + /// Prepare a folder for use with booting over UEFI_PXE. + /// + /// This places the bootloader executable under the path "bootloader". The + /// DHCP server should set the filename option to that path, otherwise the + /// bootloader won't be found. + pub fn create_pxe_tftp_folder(&self, out_path: &Path) -> anyhow::Result<()> { + self.image_builder.create_uefi_tftp_folder(out_path) + } +} \ No newline at end of file From 131130b5ae44caffd282c1988016ca8d1abcf77e Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 20:04:33 -0500 Subject: [PATCH 04/33] cargo fmt --- src/bios/mod.rs | 6 +++--- src/lib.rs | 4 ++-- src/uefi/mod.rs | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bios/mod.rs b/src/bios/mod.rs index 69d698e7..384ada2a 100644 --- a/src/bios/mod.rs +++ b/src/bios/mod.rs @@ -7,14 +7,14 @@ const BIOS_STAGE_4: &str = "boot-stage-4"; /// Create disk images for booting on legacy BIOS systems. pub struct BiosBoot { - image_builder: DiskImageBuilder + image_builder: DiskImageBuilder, } impl BiosBoot { /// Start creating a disk image for the given bootloader ELF executable. pub fn new(kernel_path: &Path) -> Self { Self { - image_builder: DiskImageBuilder::new(kernel_path) + image_builder: DiskImageBuilder::new(kernel_path), } } @@ -28,4 +28,4 @@ impl BiosBoot { pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { self.image_builder.create_bios_image(out_path) } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index df1a7818..9febe793 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems. #![warn(missing_docs)] +#[cfg(feature = "bios")] +mod bios; #[cfg(feature = "uefi")] mod gpt; #[cfg(feature = "bios")] mod mbr; -#[cfg(feature = "bios")] -mod bios; #[cfg(feature = "uefi")] mod uefi; diff --git a/src/uefi/mod.rs b/src/uefi/mod.rs index c085a763..25fc86a4 100644 --- a/src/uefi/mod.rs +++ b/src/uefi/mod.rs @@ -7,14 +7,14 @@ const BIOS_STAGE_4: &str = "boot-stage-4"; /// Create disk images for booting on legacy BIOS systems. pub struct UefiBoot { - image_builder: DiskImageBuilder + image_builder: DiskImageBuilder, } impl UefiBoot { /// Start creating a disk image for the given bootloader ELF executable. pub fn new(kernel_path: &Path) -> Self { Self { - image_builder: DiskImageBuilder::new(kernel_path) + image_builder: DiskImageBuilder::new(kernel_path), } } @@ -28,7 +28,7 @@ impl UefiBoot { pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { self.image_builder.create_uefi_image(out_path) } - + /// Prepare a folder for use with booting over UEFI_PXE. /// /// This places the bootloader executable under the path "bootloader". The @@ -37,4 +37,4 @@ impl UefiBoot { pub fn create_pxe_tftp_folder(&self, out_path: &Path) -> anyhow::Result<()> { self.image_builder.create_uefi_tftp_folder(out_path) } -} \ No newline at end of file +} From 2a7a25c060f1b950dc50c0737725a7e79def4655 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 20:26:25 -0500 Subject: [PATCH 05/33] Add abi_efiapi feature --- uefi/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/uefi/src/main.rs b/uefi/src/main.rs index d0cee33b..f3a615e1 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![deny(unsafe_op_in_unsafe_fn)] +#![feature(abi_efiapi)] use crate::memory_descriptor::UefiMemoryDescriptor; use bootloader_api::info::FrameBufferInfo; From 25536d2c0d3801273ef16ef46980549dd4544d6d Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 21:52:17 -0500 Subject: [PATCH 06/33] Fix build errors due to rebase --- src/bios/mod.rs | 13 +++++++---- src/lib.rs | 36 +++++++++++++++++++++++++++++- src/uefi/mod.rs | 11 ++++++--- tests/runner/src/lib.rs | 49 +++++++++-------------------------------- 4 files changed, 63 insertions(+), 46 deletions(-) diff --git a/src/bios/mod.rs b/src/bios/mod.rs index 384ada2a..f619098a 100644 --- a/src/bios/mod.rs +++ b/src/bios/mod.rs @@ -1,9 +1,8 @@ use std::path::Path; -use crate::DiskImageBuilder; +use bootloader_boot_config::BootConfig; -const BIOS_STAGE_3: &str = "boot-stage-3"; -const BIOS_STAGE_4: &str = "boot-stage-4"; +use crate::DiskImageBuilder; /// Create disk images for booting on legacy BIOS systems. pub struct BiosBoot { @@ -18,12 +17,18 @@ impl BiosBoot { } } - /// Add a ramdisk file to the image + /// Add a ramdisk file to the image. pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { self.image_builder.set_ramdisk(ramdisk_path); self } + /// Creates a configuration file (boot.json) that configures the runtime behavior of the bootloader. + pub fn set_boot_config(&mut self, config: &BootConfig) -> &mut Self { + self.image_builder.set_boot_config(config); + self + } + /// Create a bootable BIOS disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { self.image_builder.create_bios_image(out_path) diff --git a/src/lib.rs b/src/lib.rs index 9febe793..22deb881 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,16 +13,25 @@ mod mbr; #[cfg(feature = "uefi")] mod uefi; +#[cfg(feature = "uefi")] +pub use uefi::UefiBoot; + +#[cfg(feature = "bios")] +pub use bios::BiosBoot; + mod fat; use std::{ collections::BTreeMap, + env::temp_dir, + fs, + io::Write, path::{Path, PathBuf}, }; use anyhow::Context; -use tempfile::NamedTempFile; +use tempfile::{tempfile, NamedTempFile}; pub use bootloader_boot_config::BootConfig; @@ -65,6 +74,31 @@ impl DiskImageBuilder { self.add_or_replace_file(&path, RAMDISK_FILE_NAME) } + /// Configures the runtime behavior of the bootloader. + pub fn set_boot_config(&mut self, boot_config: &BootConfig) -> &mut Self { + let json = + serde_json::to_string_pretty(boot_config).expect("failed to serialize BootConfig"); + let bytes = json.as_bytes(); + self.add_or_replace_file_byte_content(bytes, CONFIG_FILE_NAME) + } + + /// Add or replace a file from a byte array + pub fn add_or_replace_file_byte_content(&mut self, data: &[u8], target: &str) -> &mut Self { + let temp_path = temp_dir(); + let file_name = temp_path.join("bytes.tmp"); + fs::create_dir_all(temp_path).expect("Failed to create temp directory"); + let mut temp_file = + fs::File::create(file_name.clone()).expect("Failed to create temp file"); + temp_file + .write_all(data) + .expect("Failed to write data to temp file"); + temp_file + .sync_all() + .expect("Failed to flush temp file to disk"); + + self.add_or_replace_file(&file_name, target) + } + /// Add or replace arbitrary files. /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI /// This can be useful in situations where you want to generate an image, but not use the provided bootloader. diff --git a/src/uefi/mod.rs b/src/uefi/mod.rs index 25fc86a4..c07e8d28 100644 --- a/src/uefi/mod.rs +++ b/src/uefi/mod.rs @@ -1,9 +1,8 @@ use std::path::Path; -use crate::DiskImageBuilder; +use bootloader_boot_config::BootConfig; -const BIOS_STAGE_3: &str = "boot-stage-3"; -const BIOS_STAGE_4: &str = "boot-stage-4"; +use crate::DiskImageBuilder; /// Create disk images for booting on legacy BIOS systems. pub struct UefiBoot { @@ -24,6 +23,12 @@ impl UefiBoot { self } + /// Creates a configuration file (boot.json) that configures the runtime behavior of the bootloader. + pub fn set_boot_config(&mut self, config: &BootConfig) -> &mut Self { + self.image_builder.set_boot_config(config); + self + } + /// Create a bootable UEFI disk image at the given path. pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> { self.image_builder.create_uefi_image(out_path) diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index 8faf096f..706c4b79 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -1,4 +1,5 @@ use bootloader::BootConfig; +use bootloader::DiskImageBuilder; use std::{io::Read, path::Path, process::Command}; const QEMU_ARGS: &[&str] = &[ @@ -31,26 +32,20 @@ pub fn run_test_kernel_internal( config_file_path: Option<&BootConfig>, ) { let kernel_path = Path::new(kernel_binary_path); + let mut image_builder = DiskImageBuilder::new(kernel_path); + if let Some(rdp) = ramdisk_path { + image_builder.set_ramdisk(rdp); + } + if let Some(cfp) = config_file_path { + image_builder.set_boot_config(cfp); + } #[cfg(feature = "uefi")] { - // create a GPT disk image for UEFI booting let gpt_path = kernel_path.with_extension("gpt"); - let mut uefi_builder = bootloader::UefiBoot::new(kernel_path); - // Set ramdisk for test, if supplied. - if let Some(rdp) = ramdisk_path { - uefi_builder.set_ramdisk(rdp); - } - if let Some(cfp) = config_file_path { - uefi_builder.set_boot_config(cfp); - } - uefi_builder.create_disk_image(&gpt_path).unwrap(); - - // create a TFTP folder with the kernel executable and UEFI bootloader for - // UEFI PXE booting let tftp_path = kernel_path.with_extension("tftp"); - uefi_builder.create_pxe_tftp_folder(&tftp_path).unwrap(); - + image_builder.create_uefi_image(&gpt_path).unwrap(); + image_builder.create_uefi_tftp_folder(&tftp_path).unwrap(); run_test_kernel_on_uefi(&gpt_path); run_test_kernel_on_uefi_pxe(&tftp_path); } @@ -59,32 +54,10 @@ pub fn run_test_kernel_internal( { // create an MBR disk image for legacy BIOS booting let mbr_path = kernel_path.with_extension("mbr"); - let mut bios_builder = bootloader::BiosBoot::new(kernel_path); - // Set ramdisk for test, if supplied. - if let Some(rdp) = ramdisk_path { - bios_builder.set_ramdisk(rdp); - } - if let Some(cfp) = config_file_path { - bios_builder.set_boot_config(cfp); - } - bios_builder.create_disk_image(&mbr_path).unwrap(); + image_builder.create_bios_image(mbr_path.as_path()).unwrap(); run_test_kernel_on_bios(&mbr_path); } - - #[cfg(feature = "uefi")] - { - let gpt_path = kernel_path_buf.with_extension("gpt"); - let tftp_path = kernel_path_buf.with_extension("tftp"); - image_builder.create_uefi_image(&gpt_path).unwrap(); - image_builder.create_uefi_tftp_folder(&tftp_path).unwrap(); - run_test_kernel_on_uefi(&gpt_path); - run_test_kernel_on_uefi_pxe(&tftp_path); - } -} - -pub fn run_test_kernel(kernel_binary_path: &str) { - run_test_kernel_with_ramdisk(kernel_binary_path, None); } #[cfg(feature = "uefi")] From b472297f8e15302aa0341d7acb29394ed8d3fa8f Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 28 Jan 2023 22:01:55 -0500 Subject: [PATCH 07/33] Fix clippy failures --- src/lib.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 22deb881..46d39b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ use std::{ use anyhow::Context; -use tempfile::{tempfile, NamedTempFile}; +use tempfile::NamedTempFile; pub use bootloader_boot_config::BootConfig; @@ -71,7 +71,7 @@ impl DiskImageBuilder { /// Add or replace a ramdisk to be included in the final image. pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { - self.add_or_replace_file(&path, RAMDISK_FILE_NAME) + self.add_or_replace_file(path, RAMDISK_FILE_NAME) } /// Configures the runtime behavior of the bootloader. @@ -106,7 +106,7 @@ impl DiskImageBuilder { self.files.insert( 0, DiskImageFile { - source: path.clone().to_path_buf(), + source: path.to_path_buf(), destination: target.to_string(), }, ); @@ -123,7 +123,7 @@ impl DiskImageBuilder { } for f in self.files.as_slice() { - local_map.insert(&f.destination, &f.source.as_path()); + local_map.insert(&f.destination, f.source.as_path()); } let out_file = NamedTempFile::new().context("failed to create temp file")?; @@ -200,10 +200,7 @@ impl DiskImageBuilder { for f in self.files.as_slice() { let to = tftp_path.join(f.destination.clone()); - let result = std::fs::copy(f.source.clone(), to); - if result.is_err() { - return Err(anyhow::Error::from(result.unwrap_err())); - } + std::fs::copy(f.source.clone(), to)?; } Ok(()) From d1a239124e0ef3bbc40695ca5782ff1a95628222 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sun, 29 Jan 2023 14:37:16 -0500 Subject: [PATCH 08/33] Remove unnecessary feature abi_efiapi --- uefi/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/uefi/src/main.rs b/uefi/src/main.rs index f3a615e1..d0cee33b 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![deny(unsafe_op_in_unsafe_fn)] -#![feature(abi_efiapi)] use crate::memory_descriptor::UefiMemoryDescriptor; use bootloader_api::info::FrameBufferInfo; From 37c5539c540a80149ac21123a06e4bef5e433c62 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:03:09 -0500 Subject: [PATCH 09/33] Rename image_filename to image_path --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 46d39b14..7cd2eaf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,7 @@ impl DiskImageBuilder { } #[cfg(feature = "bios")] /// Create an MBR disk image for booting on BIOS systems. - pub fn create_bios_image(&self, image_filename: &Path) -> anyhow::Result<()> { + pub fn create_bios_image(&self, image_path: &Path) -> anyhow::Result<()> { const BIOS_STAGE_3: &str = "boot-stage-3"; const BIOS_STAGE_4: &str = "boot-stage-4"; let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH")); @@ -152,7 +152,7 @@ impl DiskImageBuilder { bootsector_path, stage_2_path, fat_partition.path(), - image_filename, + image_path, ) .context("failed to create BIOS MBR disk image")?; @@ -164,7 +164,7 @@ impl DiskImageBuilder { #[cfg(feature = "uefi")] /// Create a GPT disk image for booting on UEFI systems. - pub fn create_uefi_image(&self, image_filename: &Path) -> anyhow::Result<()> { + pub fn create_uefi_image(&self, image_path: &Path) -> anyhow::Result<()> { const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); let mut internal_files = BTreeMap::new(); @@ -172,7 +172,7 @@ impl DiskImageBuilder { let fat_partition = self .create_fat_filesystem_image(internal_files) .context("failed to create FAT partition")?; - gpt::create_gpt_disk(fat_partition.path(), image_filename) + gpt::create_gpt_disk(fat_partition.path(), image_path) .context("failed to create UEFI GPT disk image")?; fat_partition .close() From 2af185f443cbfe8ebce4963b2b683aff4df64b01 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:06:57 -0500 Subject: [PATCH 10/33] Create enum to hold different sources of file data --- src/file_data_source.rs | 56 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ 2 files changed, 59 insertions(+) create mode 100644 src/file_data_source.rs diff --git a/src/file_data_source.rs b/src/file_data_source.rs new file mode 100644 index 00000000..3455035e --- /dev/null +++ b/src/file_data_source.rs @@ -0,0 +1,56 @@ +use std::path::{PathBuf}; +use alloc::vec::Vec; +use core::fmt::{Debug, Formatter}; +use std::{fs, io}; +use std::fs::File; +use std::io::Cursor; +use anyhow::Context; + +#[derive(Clone)] +pub enum FileDataSource { + File(PathBuf), + Data(Vec) +} + +impl Debug for FileDataSource { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + FileDataSource::File(file) => f.write_fmt(format_args!("data source: File {}", file.display())), + FileDataSource::Data(d) => f.write_fmt(format_args!("data source: {} raw bytes ", d.len())) + } + } +} + +impl FileDataSource { + pub fn len(&self) -> anyhow::Result { + Ok(match self { + FileDataSource::File(path) => { + fs::metadata(path) + .with_context(|| format!("failed to read metadata of file `{}`", path.display()))? + .len() + } + FileDataSource::Data(v) => v.len() as u64 + }) + } + + pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> { + match self { + FileDataSource::File(file_path) => { + io::copy( + &mut fs::File::open(file_path) + .with_context(|| format!("failed to open `{}` for copying", file_path.display()))?, + target, + )?; + }, + FileDataSource::Data(contents) => { + let mut cursor = Cursor::new(contents); + io::copy( + &mut cursor, + target, + )?; + } + }; + + Ok(()) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 7cd2eaf3..9ce8b78c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems. #![warn(missing_docs)] +extern crate alloc; + #[cfg(feature = "bios")] mod bios; #[cfg(feature = "uefi")] @@ -20,6 +22,7 @@ pub use uefi::UefiBoot; pub use bios::BiosBoot; mod fat; +mod file_data_source; use std::{ collections::BTreeMap, From 81e9d8037cbcad9f023b2baec35c53004ac10ef7 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:08:37 -0500 Subject: [PATCH 11/33] Update code to use FileDataSource --- src/fat.rs | 38 +++++++++++++++++-------------- src/lib.rs | 67 ++++++++++++++++++++++++------------------------------ 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/src/fat.rs b/src/fat.rs index d94a57b6..782905e4 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -1,21 +1,21 @@ use anyhow::Context; use std::{collections::BTreeMap, fs, io, path::Path}; +use std::fs::File; +use fatfs::{Dir, FileSystem}; +use crate::file_data_source::FileDataSource; use crate::KERNEL_FILE_NAME; pub fn create_fat_filesystem( - files: BTreeMap<&str, &Path>, + files: BTreeMap<&str, FileDataSource>, out_fat_path: &Path, ) -> anyhow::Result<()> { const MB: u64 = 1024 * 1024; // calculate needed size let mut needed_size = 0; - for path in files.values() { - let file_size = fs::metadata(path) - .with_context(|| format!("failed to read metadata of file `{}`", path.display()))? - .len(); - needed_size += file_size; + for source in files.values() { + needed_size += source.len()?; } // create new filesystem image file at the given path and set its length @@ -31,7 +31,9 @@ pub fn create_fat_filesystem( // choose a file system label let mut label = *b"MY_RUST_OS!"; - if let Some(path) = files.get(KERNEL_FILE_NAME) { + + // This __should__ always be a file, but maybe not. Should we allow the caller to set the volume label instead? + if let Some(FileDataSource::File(path)) = files.get(KERNEL_FILE_NAME) { if let Some(name) = path.file_stem() { let converted = name.to_string_lossy(); let name = converted.as_bytes(); @@ -48,10 +50,14 @@ pub fn create_fat_filesystem( fatfs::format_volume(&fat_file, format_options).context("Failed to format FAT file")?; let filesystem = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) .context("Failed to open FAT file system of UEFI FAT file")?; - + let mut root_dir = filesystem.root_dir(); + // copy files to file system - let root_dir = filesystem.root_dir(); - for (target_path_raw, file_path) in files { + add_files_to_image(&root_dir, files) +} + +pub fn add_files_to_image(root_dir: &Dir<&File>, files: BTreeMap<&str, FileDataSource>) -> anyhow::Result<()> { + for (target_path_raw, source) in files { let target_path = Path::new(target_path_raw); // create parent directories let ancestors: Vec<_> = target_path.ancestors().skip(1).collect(); @@ -70,13 +76,11 @@ pub fn create_fat_filesystem( .create_file(target_path_raw) .with_context(|| format!("failed to create file at `{}`", target_path.display()))?; new_file.truncate().unwrap(); - io::copy( - &mut fs::File::open(file_path) - .with_context(|| format!("failed to open `{}` for copying", file_path.display()))?, - &mut new_file, - ) - .with_context(|| format!("failed to copy `{}` to FAT filesystem", file_path.display()))?; + + + source.copy_to(&mut new_file) + .with_context(|| format!("failed to copy source data `{:?}` to file at `{}`", source, target_path.display()))?; } - + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 9ce8b78c..029ad4c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ use anyhow::Context; use tempfile::NamedTempFile; pub use bootloader_boot_config::BootConfig; +use crate::file_data_source::FileDataSource; const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; @@ -44,7 +45,7 @@ const CONFIG_FILE_NAME: &str = "boot.json"; #[derive(Clone)] struct DiskImageFile { - source: PathBuf, + source: FileDataSource, destination: String, } @@ -69,12 +70,12 @@ impl DiskImageBuilder { /// Add or replace a kernel to be included in the final image. pub fn set_kernel(&mut self, path: &Path) -> &mut Self { - self.add_or_replace_file(path, KERNEL_FILE_NAME) + self.add_or_replace_file(FileDataSource::File(path.to_path_buf()), KERNEL_FILE_NAME) } /// Add or replace a ramdisk to be included in the final image. pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { - self.add_or_replace_file(path, RAMDISK_FILE_NAME) + self.add_or_replace_file(FileDataSource::File(path.to_path_buf()), RAMDISK_FILE_NAME) } /// Configures the runtime behavior of the bootloader. @@ -82,34 +83,15 @@ impl DiskImageBuilder { let json = serde_json::to_string_pretty(boot_config).expect("failed to serialize BootConfig"); let bytes = json.as_bytes(); - self.add_or_replace_file_byte_content(bytes, CONFIG_FILE_NAME) + self.add_or_replace_file(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) } - - /// Add or replace a file from a byte array - pub fn add_or_replace_file_byte_content(&mut self, data: &[u8], target: &str) -> &mut Self { - let temp_path = temp_dir(); - let file_name = temp_path.join("bytes.tmp"); - fs::create_dir_all(temp_path).expect("Failed to create temp directory"); - let mut temp_file = - fs::File::create(file_name.clone()).expect("Failed to create temp file"); - temp_file - .write_all(data) - .expect("Failed to write data to temp file"); - temp_file - .sync_all() - .expect("Failed to flush temp file to disk"); - - self.add_or_replace_file(&file_name, target) - } - + /// Add or replace arbitrary files. - /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI - /// This can be useful in situations where you want to generate an image, but not use the provided bootloader. - pub fn add_or_replace_file(&mut self, path: &Path, target: &str) -> &mut Self { + pub fn add_or_replace_file(&mut self, file_data_source: FileDataSource, target: &str) -> &mut Self { self.files.insert( 0, DiskImageFile { - source: path.to_path_buf(), + source: file_data_source, destination: target.to_string(), }, ); @@ -117,16 +99,19 @@ impl DiskImageBuilder { } fn create_fat_filesystem_image( &self, - internal_files: BTreeMap<&str, &Path>, + internal_files: BTreeMap<&str, FileDataSource>, ) -> anyhow::Result { let mut local_map = BTreeMap::new(); - for k in internal_files { - local_map.insert(k.0, k.1); + for f in self.files.as_slice() { + local_map.insert(f.destination.as_str(), f.source.clone()); } - for f in self.files.as_slice() { - local_map.insert(&f.destination, f.source.as_path()); + + for k in internal_files { + if let Some(_) = local_map.insert(k.0, k.1) { + return Err(anyhow::Error::msg(format!("Attempted to overwrite internal file: {}", k.0))); + } } let out_file = NamedTempFile::new().context("failed to create temp file")?; @@ -145,8 +130,8 @@ impl DiskImageBuilder { let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH")); let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH")); let mut internal_files = BTreeMap::new(); - internal_files.insert(BIOS_STAGE_3, stage_3_path); - internal_files.insert(BIOS_STAGE_4, stage_4_path); + internal_files.insert(BIOS_STAGE_3, FileDataSource::File(stage_3_path.to_path_buf())); + internal_files.insert(BIOS_STAGE_4, FileDataSource::File(stage_4_path.to_path_buf())); let fat_partition = self .create_fat_filesystem_image(internal_files) @@ -171,7 +156,7 @@ impl DiskImageBuilder { const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); let mut internal_files = BTreeMap::new(); - internal_files.insert(UEFI_BOOT_FILENAME, bootloader_path); + internal_files.insert(UEFI_BOOT_FILENAME, FileDataSource::File(bootloader_path.to_path_buf())); let fat_partition = self .create_fat_filesystem_image(internal_files) .context("failed to create FAT partition")?; @@ -189,11 +174,11 @@ impl DiskImageBuilder { pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> { const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); - std::fs::create_dir_all(tftp_path) + fs::create_dir_all(tftp_path) .with_context(|| format!("failed to create out dir at {}", tftp_path.display()))?; let to = tftp_path.join(UEFI_TFTP_BOOT_FILENAME); - std::fs::copy(bootloader_path, &to).with_context(|| { + fs::copy(bootloader_path, &to).with_context(|| { format!( "failed to copy bootloader from {} to {}", bootloader_path.display(), @@ -203,7 +188,15 @@ impl DiskImageBuilder { for f in self.files.as_slice() { let to = tftp_path.join(f.destination.clone()); - std::fs::copy(f.source.clone(), to)?; + + let mut new_file = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(to)?; + + f.source.copy_to(&mut new_file)?; } Ok(()) From c0fab3785283e67a82e45170978df7336a5a04d3 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:09:03 -0500 Subject: [PATCH 12/33] Format code --- src/fat.rs | 29 ++++++++++++++++----------- src/file_data_source.rs | 44 ++++++++++++++++++++--------------------- src/lib.rs | 33 ++++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 42 deletions(-) diff --git a/src/fat.rs b/src/fat.rs index 782905e4..f8690286 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -1,8 +1,8 @@ +use crate::file_data_source::FileDataSource; use anyhow::Context; -use std::{collections::BTreeMap, fs, io, path::Path}; -use std::fs::File; use fatfs::{Dir, FileSystem}; -use crate::file_data_source::FileDataSource; +use std::fs::File; +use std::{collections::BTreeMap, fs, io, path::Path}; use crate::KERNEL_FILE_NAME; @@ -31,7 +31,7 @@ pub fn create_fat_filesystem( // choose a file system label let mut label = *b"MY_RUST_OS!"; - + // This __should__ always be a file, but maybe not. Should we allow the caller to set the volume label instead? if let Some(FileDataSource::File(path)) = files.get(KERNEL_FILE_NAME) { if let Some(name) = path.file_stem() { @@ -51,12 +51,15 @@ pub fn create_fat_filesystem( let filesystem = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) .context("Failed to open FAT file system of UEFI FAT file")?; let mut root_dir = filesystem.root_dir(); - + // copy files to file system add_files_to_image(&root_dir, files) } -pub fn add_files_to_image(root_dir: &Dir<&File>, files: BTreeMap<&str, FileDataSource>) -> anyhow::Result<()> { +pub fn add_files_to_image( + root_dir: &Dir<&File>, + files: BTreeMap<&str, FileDataSource>, +) -> anyhow::Result<()> { for (target_path_raw, source) in files { let target_path = Path::new(target_path_raw); // create parent directories @@ -76,11 +79,15 @@ pub fn add_files_to_image(root_dir: &Dir<&File>, files: BTreeMap<&str, FileDataS .create_file(target_path_raw) .with_context(|| format!("failed to create file at `{}`", target_path.display()))?; new_file.truncate().unwrap(); - - - source.copy_to(&mut new_file) - .with_context(|| format!("failed to copy source data `{:?}` to file at `{}`", source, target_path.display()))?; + + source.copy_to(&mut new_file).with_context(|| { + format!( + "failed to copy source data `{:?}` to file at `{}`", + source, + target_path.display() + ) + })?; } - + Ok(()) } diff --git a/src/file_data_source.rs b/src/file_data_source.rs index 3455035e..daecb3a7 100644 --- a/src/file_data_source.rs +++ b/src/file_data_source.rs @@ -1,22 +1,26 @@ -use std::path::{PathBuf}; use alloc::vec::Vec; +use anyhow::Context; use core::fmt::{Debug, Formatter}; -use std::{fs, io}; use std::fs::File; use std::io::Cursor; -use anyhow::Context; +use std::path::PathBuf; +use std::{fs, io}; #[derive(Clone)] pub enum FileDataSource { File(PathBuf), - Data(Vec) + Data(Vec), } impl Debug for FileDataSource { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { - FileDataSource::File(file) => f.write_fmt(format_args!("data source: File {}", file.display())), - FileDataSource::Data(d) => f.write_fmt(format_args!("data source: {} raw bytes ", d.len())) + FileDataSource::File(file) => { + f.write_fmt(format_args!("data source: File {}", file.display())) + } + FileDataSource::Data(d) => { + f.write_fmt(format_args!("data source: {} raw bytes ", d.len())) + } } } } @@ -24,33 +28,29 @@ impl Debug for FileDataSource { impl FileDataSource { pub fn len(&self) -> anyhow::Result { Ok(match self { - FileDataSource::File(path) => { - fs::metadata(path) - .with_context(|| format!("failed to read metadata of file `{}`", path.display()))? - .len() - } - FileDataSource::Data(v) => v.len() as u64 + FileDataSource::File(path) => fs::metadata(path) + .with_context(|| format!("failed to read metadata of file `{}`", path.display()))? + .len(), + FileDataSource::Data(v) => v.len() as u64, }) } - + pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> { match self { FileDataSource::File(file_path) => { io::copy( - &mut fs::File::open(file_path) - .with_context(|| format!("failed to open `{}` for copying", file_path.display()))?, + &mut fs::File::open(file_path).with_context(|| { + format!("failed to open `{}` for copying", file_path.display()) + })?, target, )?; - }, + } FileDataSource::Data(contents) => { let mut cursor = Cursor::new(contents); - io::copy( - &mut cursor, - target, - )?; + io::copy(&mut cursor, target)?; } }; - + Ok(()) } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 029ad4c5..181aa512 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,8 +36,8 @@ use anyhow::Context; use tempfile::NamedTempFile; -pub use bootloader_boot_config::BootConfig; use crate::file_data_source::FileDataSource; +pub use bootloader_boot_config::BootConfig; const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; @@ -85,9 +85,13 @@ impl DiskImageBuilder { let bytes = json.as_bytes(); self.add_or_replace_file(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) } - + /// Add or replace arbitrary files. - pub fn add_or_replace_file(&mut self, file_data_source: FileDataSource, target: &str) -> &mut Self { + pub fn add_or_replace_file( + &mut self, + file_data_source: FileDataSource, + target: &str, + ) -> &mut Self { self.files.insert( 0, DiskImageFile { @@ -107,10 +111,12 @@ impl DiskImageBuilder { local_map.insert(f.destination.as_str(), f.source.clone()); } - for k in internal_files { if let Some(_) = local_map.insert(k.0, k.1) { - return Err(anyhow::Error::msg(format!("Attempted to overwrite internal file: {}", k.0))); + return Err(anyhow::Error::msg(format!( + "Attempted to overwrite internal file: {}", + k.0 + ))); } } @@ -130,8 +136,14 @@ impl DiskImageBuilder { let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH")); let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH")); let mut internal_files = BTreeMap::new(); - internal_files.insert(BIOS_STAGE_3, FileDataSource::File(stage_3_path.to_path_buf())); - internal_files.insert(BIOS_STAGE_4, FileDataSource::File(stage_4_path.to_path_buf())); + internal_files.insert( + BIOS_STAGE_3, + FileDataSource::File(stage_3_path.to_path_buf()), + ); + internal_files.insert( + BIOS_STAGE_4, + FileDataSource::File(stage_4_path.to_path_buf()), + ); let fat_partition = self .create_fat_filesystem_image(internal_files) @@ -156,7 +168,10 @@ impl DiskImageBuilder { const UEFI_BOOT_FILENAME: &str = "efi/boot/bootx64.efi"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); let mut internal_files = BTreeMap::new(); - internal_files.insert(UEFI_BOOT_FILENAME, FileDataSource::File(bootloader_path.to_path_buf())); + internal_files.insert( + UEFI_BOOT_FILENAME, + FileDataSource::File(bootloader_path.to_path_buf()), + ); let fat_partition = self .create_fat_filesystem_image(internal_files) .context("failed to create FAT partition")?; @@ -195,7 +210,7 @@ impl DiskImageBuilder { .create(true) .truncate(true) .open(to)?; - + f.source.copy_to(&mut new_file)?; } From c193f9f95f13d6dc23260e7ee5a273021499dc41 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:22:24 -0500 Subject: [PATCH 13/33] Add public method to set source directly This also renames the methods to add files to set_. --- src/lib.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 181aa512..f8538599 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,12 +70,12 @@ impl DiskImageBuilder { /// Add or replace a kernel to be included in the final image. pub fn set_kernel(&mut self, path: &Path) -> &mut Self { - self.add_or_replace_file(FileDataSource::File(path.to_path_buf()), KERNEL_FILE_NAME) + self.set_file_source(FileDataSource::File(path.to_path_buf()), KERNEL_FILE_NAME) } /// Add or replace a ramdisk to be included in the final image. pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { - self.add_or_replace_file(FileDataSource::File(path.to_path_buf()), RAMDISK_FILE_NAME) + self.set_file_source(FileDataSource::File(path.to_path_buf()), RAMDISK_FILE_NAME) } /// Configures the runtime behavior of the bootloader. @@ -83,24 +83,27 @@ impl DiskImageBuilder { let json = serde_json::to_string_pretty(boot_config).expect("failed to serialize BootConfig"); let bytes = json.as_bytes(); - self.add_or_replace_file(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) + self.set_file_source(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) + } + + pub fn set_file_source(&mut self, source: FileDataSource, destination: &str,) -> &mut Self { + let destination = destination.to_string(); + self.files.insert(0, DiskImageFile { source, destination }); + self + } + + pub fn set_file_contents(&mut self, data: &[u8], destination: &str,) -> &mut Self { + self.set_file_source(FileDataSource::Data(data.to_vec()), destination) } - /// Add or replace arbitrary files. - pub fn add_or_replace_file( + pub fn set_file( &mut self, - file_data_source: FileDataSource, - target: &str, + file_path: &Path, + destination: &str, ) -> &mut Self { - self.files.insert( - 0, - DiskImageFile { - source: file_data_source, - destination: target.to_string(), - }, - ); - self + self.set_file_source(FileDataSource::File(file_path.to_path_buf()),destination) } + fn create_fat_filesystem_image( &self, internal_files: BTreeMap<&str, FileDataSource>, From d4e9a5fbc1ebd442227af5964bba8bf7b20f3711 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:24:09 -0500 Subject: [PATCH 14/33] Format files after changes --- src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f8538599..d6f7057f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,25 +85,27 @@ impl DiskImageBuilder { let bytes = json.as_bytes(); self.set_file_source(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) } - - pub fn set_file_source(&mut self, source: FileDataSource, destination: &str,) -> &mut Self { + + pub fn set_file_source(&mut self, source: FileDataSource, destination: &str) -> &mut Self { let destination = destination.to_string(); - self.files.insert(0, DiskImageFile { source, destination }); + self.files.insert( + 0, + DiskImageFile { + source, + destination, + }, + ); self } - pub fn set_file_contents(&mut self, data: &[u8], destination: &str,) -> &mut Self { + pub fn set_file_contents(&mut self, data: &[u8], destination: &str) -> &mut Self { self.set_file_source(FileDataSource::Data(data.to_vec()), destination) } - pub fn set_file( - &mut self, - file_path: &Path, - destination: &str, - ) -> &mut Self { - self.set_file_source(FileDataSource::File(file_path.to_path_buf()),destination) + pub fn set_file(&mut self, file_path: &Path, destination: &str) -> &mut Self { + self.set_file_source(FileDataSource::File(file_path.to_path_buf()), destination) } - + fn create_fat_filesystem_image( &self, internal_files: BTreeMap<&str, FileDataSource>, From 42b58959a5782017c2718d7cda160fb8e39425f9 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:26:12 -0500 Subject: [PATCH 15/33] Refactor set methods to take destination first --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6f7057f..153e1751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,12 +70,12 @@ impl DiskImageBuilder { /// Add or replace a kernel to be included in the final image. pub fn set_kernel(&mut self, path: &Path) -> &mut Self { - self.set_file_source(FileDataSource::File(path.to_path_buf()), KERNEL_FILE_NAME) + self.set_file_source(KERNEL_FILE_NAME, FileDataSource::File(path.to_path_buf())) } /// Add or replace a ramdisk to be included in the final image. pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { - self.set_file_source(FileDataSource::File(path.to_path_buf()), RAMDISK_FILE_NAME) + self.set_file_source(RAMDISK_FILE_NAME, FileDataSource::File(path.to_path_buf())) } /// Configures the runtime behavior of the bootloader. @@ -83,10 +83,10 @@ impl DiskImageBuilder { let json = serde_json::to_string_pretty(boot_config).expect("failed to serialize BootConfig"); let bytes = json.as_bytes(); - self.set_file_source(FileDataSource::Data(bytes.to_vec()), CONFIG_FILE_NAME) + self.set_file_source(CONFIG_FILE_NAME, FileDataSource::Data(bytes.to_vec())) } - pub fn set_file_source(&mut self, source: FileDataSource, destination: &str) -> &mut Self { + pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); self.files.insert( 0, @@ -98,12 +98,12 @@ impl DiskImageBuilder { self } - pub fn set_file_contents(&mut self, data: &[u8], destination: &str) -> &mut Self { - self.set_file_source(FileDataSource::Data(data.to_vec()), destination) + pub fn set_file_contents(&mut self, destination: &str, data: &[u8]) -> &mut Self { + self.set_file_source(destination, FileDataSource::Data(data.to_vec())) } - pub fn set_file(&mut self, file_path: &Path, destination: &str) -> &mut Self { - self.set_file_source(FileDataSource::File(file_path.to_path_buf()), destination) + pub fn set_file(&mut self, destination: &str, file_path: &Path) -> &mut Self { + self.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) } fn create_fat_filesystem_image( From dd87c0049f191e8f572f32d15d404be2174ec4d9 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:27:18 -0500 Subject: [PATCH 16/33] Fix warnings with --- src/fat.rs | 6 +++--- src/file_data_source.rs | 2 +- src/lib.rs | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fat.rs b/src/fat.rs index f8690286..5ef321a0 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -1,8 +1,8 @@ use crate::file_data_source::FileDataSource; use anyhow::Context; -use fatfs::{Dir, FileSystem}; +use fatfs::{Dir}; use std::fs::File; -use std::{collections::BTreeMap, fs, io, path::Path}; +use std::{collections::BTreeMap, fs, path::Path}; use crate::KERNEL_FILE_NAME; @@ -50,7 +50,7 @@ pub fn create_fat_filesystem( fatfs::format_volume(&fat_file, format_options).context("Failed to format FAT file")?; let filesystem = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) .context("Failed to open FAT file system of UEFI FAT file")?; - let mut root_dir = filesystem.root_dir(); + let root_dir = filesystem.root_dir(); // copy files to file system add_files_to_image(&root_dir, files) diff --git a/src/file_data_source.rs b/src/file_data_source.rs index daecb3a7..edf2103b 100644 --- a/src/file_data_source.rs +++ b/src/file_data_source.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use anyhow::Context; use core::fmt::{Debug, Formatter}; -use std::fs::File; + use std::io::Cursor; use std::path::PathBuf; use std::{fs, io}; diff --git a/src/lib.rs b/src/lib.rs index 153e1751..ef55077f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,10 +26,9 @@ mod file_data_source; use std::{ collections::BTreeMap, - env::temp_dir, fs, io::Write, - path::{Path, PathBuf}, + path::{Path}, }; use anyhow::Context; From 3a7fcf7849ea353b0dc3da2d4488a15c836c021f Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:33:50 -0500 Subject: [PATCH 17/33] Add documentation comments to set_file_* methods --- src/lib.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ef55077f..2c60c6b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,13 @@ impl DiskImageBuilder { let bytes = json.as_bytes(); self.set_file_source(CONFIG_FILE_NAME, FileDataSource::Data(bytes.to_vec())) } - + + /// Add a file source to the disk image + /// Example: + /// ``` + /// image_builder.set_file_source("/extra/README.md", FileDataSource::File(file_path.to_path_buf())) + /// .set_file_source("/config/config.json", FileDataSource::Data(serialzed_configuration.to_vec())); + /// ``` pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); self.files.insert( @@ -97,10 +103,20 @@ impl DiskImageBuilder { self } + /// Add a file with the specified bytes to the disk image + /// NOTE: This is just a convenience wrapper around set_file_source + /// ``` + /// image_builder.set_file_source(destination, FileDataSource::Data(data.to_vec())) + /// ``` pub fn set_file_contents(&mut self, destination: &str, data: &[u8]) -> &mut Self { self.set_file_source(destination, FileDataSource::Data(data.to_vec())) } + /// Add a file with the specified source file to the disk image + /// NOTE: This is just a convenience wrapper around set_file_source + /// ``` + /// image_builder.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) + /// ``` pub fn set_file(&mut self, destination: &str, file_path: &Path) -> &mut Self { self.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) } From 502967babbc7eead2463682f8b699db5c0812e92 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:37:02 -0500 Subject: [PATCH 18/33] Add documentation comments to FileDataSource and it's public methods. --- src/file_data_source.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/file_data_source.rs b/src/file_data_source.rs index edf2103b..05b67618 100644 --- a/src/file_data_source.rs +++ b/src/file_data_source.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use std::{fs, io}; #[derive(Clone)] +/// Defines a data source, either a source `std::path::PathBuf`, or a vector of bytes. pub enum FileDataSource { File(PathBuf), Data(Vec), @@ -26,6 +27,7 @@ impl Debug for FileDataSource { } impl FileDataSource { + /// Get the length of the inner data source pub fn len(&self) -> anyhow::Result { Ok(match self { FileDataSource::File(path) => fs::metadata(path) @@ -34,7 +36,18 @@ impl FileDataSource { FileDataSource::Data(v) => v.len() as u64, }) } - + /// Copy this data source to the specified target that implements io::Write + /// Example: + /// ``` + /// let mut new_file = std::io::fs::OpenOptions::new() + /// .read(true) + /// .write(true) + /// .create(true) + /// .truncate(true) + /// .open(to)?; + /// + /// f.source.copy_to(&mut new_file)?; + ///``` pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> { match self { FileDataSource::File(file_path) => { From ad345102a772e3074cffc6a4c7fe7627e165c29b Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:42:25 -0500 Subject: [PATCH 19/33] Remove code comments to pass doc test. --- src/file_data_source.rs | 11 ----------- src/lib.rs | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/src/file_data_source.rs b/src/file_data_source.rs index 05b67618..3e1128b3 100644 --- a/src/file_data_source.rs +++ b/src/file_data_source.rs @@ -37,17 +37,6 @@ impl FileDataSource { }) } /// Copy this data source to the specified target that implements io::Write - /// Example: - /// ``` - /// let mut new_file = std::io::fs::OpenOptions::new() - /// .read(true) - /// .write(true) - /// .create(true) - /// .truncate(true) - /// .open(to)?; - /// - /// f.source.copy_to(&mut new_file)?; - ///``` pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> { match self { FileDataSource::File(file_path) => { diff --git a/src/lib.rs b/src/lib.rs index 2c60c6b8..2d006dc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,11 +86,6 @@ impl DiskImageBuilder { } /// Add a file source to the disk image - /// Example: - /// ``` - /// image_builder.set_file_source("/extra/README.md", FileDataSource::File(file_path.to_path_buf())) - /// .set_file_source("/config/config.json", FileDataSource::Data(serialzed_configuration.to_vec())); - /// ``` pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); self.files.insert( @@ -105,18 +100,12 @@ impl DiskImageBuilder { /// Add a file with the specified bytes to the disk image /// NOTE: This is just a convenience wrapper around set_file_source - /// ``` - /// image_builder.set_file_source(destination, FileDataSource::Data(data.to_vec())) - /// ``` pub fn set_file_contents(&mut self, destination: &str, data: &[u8]) -> &mut Self { self.set_file_source(destination, FileDataSource::Data(data.to_vec())) } /// Add a file with the specified source file to the disk image /// NOTE: This is just a convenience wrapper around set_file_source - /// ``` - /// image_builder.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) - /// ``` pub fn set_file(&mut self, destination: &str, file_path: &Path) -> &mut Self { self.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) } From a93ce32043f514b9feceecd7a50d8dffc642f8c4 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 18:48:54 -0500 Subject: [PATCH 20/33] Final formatting pass --- src/fat.rs | 2 +- src/lib.rs | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/fat.rs b/src/fat.rs index 5ef321a0..4907a79e 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -1,6 +1,6 @@ use crate::file_data_source::FileDataSource; use anyhow::Context; -use fatfs::{Dir}; +use fatfs::Dir; use std::fs::File; use std::{collections::BTreeMap, fs, path::Path}; diff --git a/src/lib.rs b/src/lib.rs index 2d006dc4..fda50554 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,12 +24,7 @@ pub use bios::BiosBoot; mod fat; mod file_data_source; -use std::{ - collections::BTreeMap, - fs, - io::Write, - path::{Path}, -}; +use std::{collections::BTreeMap, fs, io::Write, path::Path}; use anyhow::Context; @@ -84,7 +79,7 @@ impl DiskImageBuilder { let bytes = json.as_bytes(); self.set_file_source(CONFIG_FILE_NAME, FileDataSource::Data(bytes.to_vec())) } - + /// Add a file source to the disk image pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); From 3ce95982e345f5b097a4fa8f7b8cfdcdf2eaf018 Mon Sep 17 00:00:00 2001 From: Jason Couture Date: Sat, 4 Mar 2023 19:02:57 -0500 Subject: [PATCH 21/33] Remove DiskImageFile in favor of a BTreeMap --- src/lib.rs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fda50554..6cf83e43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ pub use bios::BiosBoot; mod fat; mod file_data_source; -use std::{collections::BTreeMap, fs, io::Write, path::Path}; +use std::{collections::BTreeMap, fs, path::Path}; use anyhow::Context; @@ -37,16 +37,10 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; const CONFIG_FILE_NAME: &str = "boot.json"; -#[derive(Clone)] -struct DiskImageFile { - source: FileDataSource, - destination: String, -} - /// DiskImageBuilder helps create disk images for a specified set of files. /// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images. pub struct DiskImageBuilder { - files: Vec, + files: BTreeMap, } impl DiskImageBuilder { @@ -59,7 +53,9 @@ impl DiskImageBuilder { /// Create a new, empty instance of DiskImageBuilder pub fn empty() -> Self { - Self { files: Vec::new() } + Self { + files: BTreeMap::new(), + } } /// Add or replace a kernel to be included in the final image. @@ -83,13 +79,7 @@ impl DiskImageBuilder { /// Add a file source to the disk image pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); - self.files.insert( - 0, - DiskImageFile { - source, - destination, - }, - ); + self.files.insert(destination, source); self } @@ -111,8 +101,8 @@ impl DiskImageBuilder { ) -> anyhow::Result { let mut local_map = BTreeMap::new(); - for f in self.files.as_slice() { - local_map.insert(f.destination.as_str(), f.source.clone()); + for f in &self.files { + local_map.insert(f.0.as_str(), f.1.clone()); } for k in internal_files { @@ -205,8 +195,8 @@ impl DiskImageBuilder { ) })?; - for f in self.files.as_slice() { - let to = tftp_path.join(f.destination.clone()); + for f in &self.files { + let to = tftp_path.join(f.0.clone()); let mut new_file = fs::OpenOptions::new() .read(true) @@ -215,7 +205,7 @@ impl DiskImageBuilder { .truncate(true) .open(to)?; - f.source.copy_to(&mut new_file)?; + f.1.copy_to(&mut new_file)?; } Ok(()) From 6b8d970d3182304de430f50cbf8cc236db485830 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 13:57:58 +0100 Subject: [PATCH 22/33] Improve docs --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6cf83e43..15bd8af0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,8 +37,9 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64"; const RAMDISK_FILE_NAME: &str = "ramdisk"; const CONFIG_FILE_NAME: &str = "boot.json"; -/// DiskImageBuilder helps create disk images for a specified set of files. -/// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images. +/// Allows creating disk images for a specified set of files. +/// +/// It can currently create `MBR` (BIOS), `GPT` (UEFI), and `TFTP` (UEFI) images. pub struct DiskImageBuilder { files: BTreeMap, } From 5cabc0cde837b9e6532fbde7df03809efcf43efa Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 13:59:08 +0100 Subject: [PATCH 23/33] Make `set_file_source` private We don't want to expose the `FileDataSource` enum for now. --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 15bd8af0..67d16b0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,20 +78,18 @@ impl DiskImageBuilder { } /// Add a file source to the disk image - pub fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { + fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { let destination = destination.to_string(); self.files.insert(destination, source); self } /// Add a file with the specified bytes to the disk image - /// NOTE: This is just a convenience wrapper around set_file_source pub fn set_file_contents(&mut self, destination: &str, data: &[u8]) -> &mut Self { self.set_file_source(destination, FileDataSource::Data(data.to_vec())) } /// Add a file with the specified source file to the disk image - /// NOTE: This is just a convenience wrapper around set_file_source pub fn set_file(&mut self, destination: &str, file_path: &Path) -> &mut Self { self.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) } From 2923ad83f8b03d60435b0237d6a174bbf5aa4cdf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 13:59:41 +0100 Subject: [PATCH 24/33] Take arguments as owned values in `set_file` and `set_file_contents` --- src/lib.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67d16b0e..077ca033 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,11 @@ pub use bios::BiosBoot; mod fat; mod file_data_source; -use std::{collections::BTreeMap, fs, path::Path}; +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, +}; use anyhow::Context; @@ -85,13 +89,13 @@ impl DiskImageBuilder { } /// Add a file with the specified bytes to the disk image - pub fn set_file_contents(&mut self, destination: &str, data: &[u8]) -> &mut Self { - self.set_file_source(destination, FileDataSource::Data(data.to_vec())) + pub fn set_file_contents(&mut self, destination: &str, data: Vec) -> &mut Self { + self.set_file_source(destination, FileDataSource::Data(data)) } /// Add a file with the specified source file to the disk image - pub fn set_file(&mut self, destination: &str, file_path: &Path) -> &mut Self { - self.set_file_source(destination, FileDataSource::File(file_path.to_path_buf())) + pub fn set_file(&mut self, destination: &str, file_path: PathBuf) -> &mut Self { + self.set_file_source(destination, FileDataSource::File(file_path)) } fn create_fat_filesystem_image( From 1a5cdf6d6c059b3336ec035ed211f55b1d75d293 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:00:55 +0100 Subject: [PATCH 25/33] Reorder methods (public first) --- src/lib.rs | 65 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 077ca033..5b1f9c4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,13 +81,6 @@ impl DiskImageBuilder { self.set_file_source(CONFIG_FILE_NAME, FileDataSource::Data(bytes.to_vec())) } - /// Add a file source to the disk image - fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { - let destination = destination.to_string(); - self.files.insert(destination, source); - self - } - /// Add a file with the specified bytes to the disk image pub fn set_file_contents(&mut self, destination: &str, data: Vec) -> &mut Self { self.set_file_source(destination, FileDataSource::Data(data)) @@ -98,31 +91,6 @@ impl DiskImageBuilder { self.set_file_source(destination, FileDataSource::File(file_path)) } - fn create_fat_filesystem_image( - &self, - internal_files: BTreeMap<&str, FileDataSource>, - ) -> anyhow::Result { - let mut local_map = BTreeMap::new(); - - for f in &self.files { - local_map.insert(f.0.as_str(), f.1.clone()); - } - - for k in internal_files { - if let Some(_) = local_map.insert(k.0, k.1) { - return Err(anyhow::Error::msg(format!( - "Attempted to overwrite internal file: {}", - k.0 - ))); - } - } - - let out_file = NamedTempFile::new().context("failed to create temp file")?; - fat::create_fat_filesystem(local_map, out_file.path()) - .context("failed to create BIOS FAT filesystem")?; - - Ok(out_file) - } #[cfg(feature = "bios")] /// Create an MBR disk image for booting on BIOS systems. pub fn create_bios_image(&self, image_path: &Path) -> anyhow::Result<()> { @@ -213,4 +181,37 @@ impl DiskImageBuilder { Ok(()) } + + /// Add a file source to the disk image + fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { + let destination = destination.to_string(); + self.files.insert(destination, source); + self + } + + fn create_fat_filesystem_image( + &self, + internal_files: BTreeMap<&str, FileDataSource>, + ) -> anyhow::Result { + let mut local_map = BTreeMap::new(); + + for f in &self.files { + local_map.insert(f.0.as_str(), f.1.clone()); + } + + for k in internal_files { + if let Some(_) = local_map.insert(k.0, k.1) { + return Err(anyhow::Error::msg(format!( + "Attempted to overwrite internal file: {}", + k.0 + ))); + } + } + + let out_file = NamedTempFile::new().context("failed to create temp file")?; + fat::create_fat_filesystem(local_map, out_file.path()) + .context("failed to create BIOS FAT filesystem")?; + + Ok(out_file) + } } From 6e9b6a323a5b26ce404b4515c165bfe1907f957d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:02:25 +0100 Subject: [PATCH 26/33] Document that only the kernel and ramdisk are loaded into memory --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5b1f9c4d..edfe7e8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,11 +82,17 @@ impl DiskImageBuilder { } /// Add a file with the specified bytes to the disk image + /// + /// Note that the bootloader only loads the kernel and ramdisk files into memory on boot. + /// Other files need to be loaded manually by the kernel. pub fn set_file_contents(&mut self, destination: &str, data: Vec) -> &mut Self { self.set_file_source(destination, FileDataSource::Data(data)) } /// Add a file with the specified source file to the disk image + /// + /// Note that the bootloader only loads the kernel and ramdisk files into memory on boot. + /// Other files need to be loaded manually by the kernel. pub fn set_file(&mut self, destination: &str, file_path: PathBuf) -> &mut Self { self.set_file_source(destination, FileDataSource::File(file_path)) } From c4714ce5097afa1f1c0fe1a656ac331d69e8dd9c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:03:16 +0100 Subject: [PATCH 27/33] Apply clippy suggestion --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index edfe7e8c..262c33bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,7 +206,7 @@ impl DiskImageBuilder { } for k in internal_files { - if let Some(_) = local_map.insert(k.0, k.1) { + if local_map.insert(k.0, k.1).is_some() { return Err(anyhow::Error::msg(format!( "Attempted to overwrite internal file: {}", k.0 From 075f22d3c5ff52f727a2d29f039d3da33236cb99 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:09:59 +0100 Subject: [PATCH 28/33] Take `FileDataSource` by reference in `create_fat_filesystem` --- src/fat.rs | 4 ++-- src/lib.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fat.rs b/src/fat.rs index 4907a79e..aa2a450b 100644 --- a/src/fat.rs +++ b/src/fat.rs @@ -7,7 +7,7 @@ use std::{collections::BTreeMap, fs, path::Path}; use crate::KERNEL_FILE_NAME; pub fn create_fat_filesystem( - files: BTreeMap<&str, FileDataSource>, + files: BTreeMap<&str, &FileDataSource>, out_fat_path: &Path, ) -> anyhow::Result<()> { const MB: u64 = 1024 * 1024; @@ -58,7 +58,7 @@ pub fn create_fat_filesystem( pub fn add_files_to_image( root_dir: &Dir<&File>, - files: BTreeMap<&str, FileDataSource>, + files: BTreeMap<&str, &FileDataSource>, ) -> anyhow::Result<()> { for (target_path_raw, source) in files { let target_path = Path::new(target_path_raw); diff --git a/src/lib.rs b/src/lib.rs index 262c33bb..5eb1d363 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,14 +199,14 @@ impl DiskImageBuilder { &self, internal_files: BTreeMap<&str, FileDataSource>, ) -> anyhow::Result { - let mut local_map = BTreeMap::new(); + let mut local_map: BTreeMap<&str, _> = BTreeMap::new(); - for f in &self.files { - local_map.insert(f.0.as_str(), f.1.clone()); + for (name, source) in &self.files { + local_map.insert(&name, source); } for k in internal_files { - if local_map.insert(k.0, k.1).is_some() { + if local_map.insert(k.0, &k.1).is_some() { return Err(anyhow::Error::msg(format!( "Attempted to overwrite internal file: {}", k.0 From 692d39f44e303b57c5d88b3b65f276ccd346f221 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:11:25 +0100 Subject: [PATCH 29/33] Serialize boot config to `Vec` directly --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5eb1d363..bb3ca4f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,10 +75,8 @@ impl DiskImageBuilder { /// Configures the runtime behavior of the bootloader. pub fn set_boot_config(&mut self, boot_config: &BootConfig) -> &mut Self { - let json = - serde_json::to_string_pretty(boot_config).expect("failed to serialize BootConfig"); - let bytes = json.as_bytes(); - self.set_file_source(CONFIG_FILE_NAME, FileDataSource::Data(bytes.to_vec())) + let json = serde_json::to_vec_pretty(boot_config).expect("failed to serialize BootConfig"); + self.set_file_source(CONFIG_FILE_NAME.into(), FileDataSource::Data(json)) } /// Add a file with the specified bytes to the disk image From bd5047c21ce98308c53b47554a3b1ddf55b3e1c8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:13:09 +0100 Subject: [PATCH 30/33] Take file paths by value to avoid internal cloning Also: Store file paths as `Cow<'static, str>` internally to avoid cloning for const paths. --- src/lib.rs | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bb3ca4f8..94d73262 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ mod fat; mod file_data_source; use std::{ + borrow::Cow, collections::BTreeMap, fs, path::{Path, PathBuf}, @@ -45,12 +46,12 @@ const CONFIG_FILE_NAME: &str = "boot.json"; /// /// It can currently create `MBR` (BIOS), `GPT` (UEFI), and `TFTP` (UEFI) images. pub struct DiskImageBuilder { - files: BTreeMap, + files: BTreeMap, FileDataSource>, } impl DiskImageBuilder { /// Create a new instance of DiskImageBuilder, with the specified kernel. - pub fn new(kernel: &Path) -> Self { + pub fn new(kernel: PathBuf) -> Self { let mut obj = Self::empty(); obj.set_kernel(kernel); obj @@ -64,13 +65,19 @@ impl DiskImageBuilder { } /// Add or replace a kernel to be included in the final image. - pub fn set_kernel(&mut self, path: &Path) -> &mut Self { - self.set_file_source(KERNEL_FILE_NAME, FileDataSource::File(path.to_path_buf())) + pub fn set_kernel(&mut self, path: PathBuf) -> &mut Self { + self.set_file_source( + KERNEL_FILE_NAME.into(), + FileDataSource::File(path.to_path_buf()), + ) } /// Add or replace a ramdisk to be included in the final image. - pub fn set_ramdisk(&mut self, path: &Path) -> &mut Self { - self.set_file_source(RAMDISK_FILE_NAME, FileDataSource::File(path.to_path_buf())) + pub fn set_ramdisk(&mut self, path: PathBuf) -> &mut Self { + self.set_file_source( + RAMDISK_FILE_NAME.into(), + FileDataSource::File(path.to_path_buf()), + ) } /// Configures the runtime behavior of the bootloader. @@ -83,8 +90,8 @@ impl DiskImageBuilder { /// /// Note that the bootloader only loads the kernel and ramdisk files into memory on boot. /// Other files need to be loaded manually by the kernel. - pub fn set_file_contents(&mut self, destination: &str, data: Vec) -> &mut Self { - self.set_file_source(destination, FileDataSource::Data(data)) + pub fn set_file_contents(&mut self, destination: String, data: Vec) -> &mut Self { + self.set_file_source(destination.into(), FileDataSource::Data(data)) } /// Add a file with the specified source file to the disk image @@ -92,7 +99,7 @@ impl DiskImageBuilder { /// Note that the bootloader only loads the kernel and ramdisk files into memory on boot. /// Other files need to be loaded manually by the kernel. pub fn set_file(&mut self, destination: &str, file_path: PathBuf) -> &mut Self { - self.set_file_source(destination, FileDataSource::File(file_path)) + self.set_file_source(destination.into(), FileDataSource::File(file_path)) } #[cfg(feature = "bios")] @@ -156,6 +163,8 @@ impl DiskImageBuilder { #[cfg(feature = "uefi")] /// Create a folder containing the needed files for UEFI TFTP/PXE booting. pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> { + use std::ops::Deref; + const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH")); fs::create_dir_all(tftp_path) @@ -171,7 +180,7 @@ impl DiskImageBuilder { })?; for f in &self.files { - let to = tftp_path.join(f.0.clone()); + let to = tftp_path.join(f.0.deref()); let mut new_file = fs::OpenOptions::new() .read(true) @@ -187,8 +196,11 @@ impl DiskImageBuilder { } /// Add a file source to the disk image - fn set_file_source(&mut self, destination: &str, source: FileDataSource) -> &mut Self { - let destination = destination.to_string(); + fn set_file_source( + &mut self, + destination: Cow<'static, str>, + source: FileDataSource, + ) -> &mut Self { self.files.insert(destination, source); self } From f0328e63f099fd39b5625560df7262142cda2670 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:42:55 +0100 Subject: [PATCH 31/33] Follow-up fixes for by-value arguments --- src/bios/mod.rs | 4 ++-- src/lib.rs | 18 ++++++------------ src/uefi/mod.rs | 4 ++-- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/bios/mod.rs b/src/bios/mod.rs index f619098a..6000adcc 100644 --- a/src/bios/mod.rs +++ b/src/bios/mod.rs @@ -13,13 +13,13 @@ impl BiosBoot { /// Start creating a disk image for the given bootloader ELF executable. pub fn new(kernel_path: &Path) -> Self { Self { - image_builder: DiskImageBuilder::new(kernel_path), + image_builder: DiskImageBuilder::new(kernel_path.to_owned()), } } /// Add a ramdisk file to the image. pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { - self.image_builder.set_ramdisk(ramdisk_path); + self.image_builder.set_ramdisk(ramdisk_path.to_owned()); self } diff --git a/src/lib.rs b/src/lib.rs index 94d73262..85865cf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,18 +66,12 @@ impl DiskImageBuilder { /// Add or replace a kernel to be included in the final image. pub fn set_kernel(&mut self, path: PathBuf) -> &mut Self { - self.set_file_source( - KERNEL_FILE_NAME.into(), - FileDataSource::File(path.to_path_buf()), - ) + self.set_file_source(KERNEL_FILE_NAME.into(), FileDataSource::File(path)) } /// Add or replace a ramdisk to be included in the final image. pub fn set_ramdisk(&mut self, path: PathBuf) -> &mut Self { - self.set_file_source( - RAMDISK_FILE_NAME.into(), - FileDataSource::File(path.to_path_buf()), - ) + self.set_file_source(RAMDISK_FILE_NAME.into(), FileDataSource::File(path)) } /// Configures the runtime behavior of the bootloader. @@ -98,7 +92,7 @@ impl DiskImageBuilder { /// /// Note that the bootloader only loads the kernel and ramdisk files into memory on boot. /// Other files need to be loaded manually by the kernel. - pub fn set_file(&mut self, destination: &str, file_path: PathBuf) -> &mut Self { + pub fn set_file(&mut self, destination: String, file_path: PathBuf) -> &mut Self { self.set_file_source(destination.into(), FileDataSource::File(file_path)) } @@ -212,11 +206,11 @@ impl DiskImageBuilder { let mut local_map: BTreeMap<&str, _> = BTreeMap::new(); for (name, source) in &self.files { - local_map.insert(&name, source); + local_map.insert(name, source); } - for k in internal_files { - if local_map.insert(k.0, &k.1).is_some() { + for k in &internal_files { + if local_map.insert(k.0, k.1).is_some() { return Err(anyhow::Error::msg(format!( "Attempted to overwrite internal file: {}", k.0 diff --git a/src/uefi/mod.rs b/src/uefi/mod.rs index c07e8d28..7a27e728 100644 --- a/src/uefi/mod.rs +++ b/src/uefi/mod.rs @@ -13,13 +13,13 @@ impl UefiBoot { /// Start creating a disk image for the given bootloader ELF executable. pub fn new(kernel_path: &Path) -> Self { Self { - image_builder: DiskImageBuilder::new(kernel_path), + image_builder: DiskImageBuilder::new(kernel_path.to_owned()), } } /// Add a ramdisk file to the image pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self { - self.image_builder.set_ramdisk(ramdisk_path); + self.image_builder.set_ramdisk(ramdisk_path.to_owned()); self } From ac934c4070632de14b6173a3888b5902e5e7c1d1 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 14:43:17 +0100 Subject: [PATCH 32/33] Adjust test runner for new owned arguments --- tests/runner/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/runner/src/lib.rs b/tests/runner/src/lib.rs index 706c4b79..7d293c1e 100644 --- a/tests/runner/src/lib.rs +++ b/tests/runner/src/lib.rs @@ -32,9 +32,9 @@ pub fn run_test_kernel_internal( config_file_path: Option<&BootConfig>, ) { let kernel_path = Path::new(kernel_binary_path); - let mut image_builder = DiskImageBuilder::new(kernel_path); + let mut image_builder = DiskImageBuilder::new(kernel_path.to_owned()); if let Some(rdp) = ramdisk_path { - image_builder.set_ramdisk(rdp); + image_builder.set_ramdisk(rdp.to_owned()); } if let Some(cfp) = config_file_path { image_builder.set_boot_config(cfp); From e3dd6fc6c7c60a78d4e9d75207e7c9404b990555 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 12 Mar 2023 15:06:10 +0100 Subject: [PATCH 33/33] Fix unused import --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 85865cf7..179f1d10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ mod file_data_source; use std::{ borrow::Cow, collections::BTreeMap, - fs, path::{Path, PathBuf}, }; @@ -157,7 +156,7 @@ impl DiskImageBuilder { #[cfg(feature = "uefi")] /// Create a folder containing the needed files for UEFI TFTP/PXE booting. pub fn create_uefi_tftp_folder(&self, tftp_path: &Path) -> anyhow::Result<()> { - use std::ops::Deref; + use std::{fs, ops::Deref}; const UEFI_TFTP_BOOT_FILENAME: &str = "bootloader"; let bootloader_path = Path::new(env!("UEFI_BOOTLOADER_PATH"));