Skip to content

Commit 8d2d53c

Browse files
authored
Merge pull request #320 from jasoncouture/simplified_disk_builder
Simplified disk builder
2 parents 5a24373 + e3dd6fc commit 8d2d53c

File tree

10 files changed

+324
-237
lines changed

10 files changed

+324
-237
lines changed

build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ async fn build_bios_stage_4(out_dir: &Path) -> PathBuf {
266266
convert_elf_to_bin(elf_path).await
267267
}
268268

269+
#[cfg(not(docsrs_dummy_build))]
269270
#[cfg(feature = "bios")]
270271
async fn convert_elf_to_bin(elf_path: PathBuf) -> PathBuf {
271272
let flat_binary_path = elf_path.with_extension("bin");

src/bios/mod.rs

+9-71
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,36 @@
1-
use crate::fat;
2-
use anyhow::Context;
3-
use bootloader_boot_config::BootConfig;
4-
use std::io::Write;
5-
use std::{
6-
collections::BTreeMap,
7-
path::{Path, PathBuf},
8-
};
9-
use tempfile::NamedTempFile;
1+
use std::path::Path;
102

11-
mod mbr;
3+
use bootloader_boot_config::BootConfig;
124

13-
const BIOS_STAGE_3: &str = "boot-stage-3";
14-
const BIOS_STAGE_4: &str = "boot-stage-4";
5+
use crate::DiskImageBuilder;
156

167
/// Create disk images for booting on legacy BIOS systems.
178
pub struct BiosBoot {
18-
kernel: PathBuf,
19-
ramdisk: Option<PathBuf>,
20-
config: Option<String>,
9+
image_builder: DiskImageBuilder,
2110
}
2211

2312
impl BiosBoot {
2413
/// Start creating a disk image for the given bootloader ELF executable.
2514
pub fn new(kernel_path: &Path) -> Self {
2615
Self {
27-
kernel: kernel_path.to_owned(),
28-
ramdisk: None,
29-
config: None,
16+
image_builder: DiskImageBuilder::new(kernel_path.to_owned()),
3017
}
3118
}
3219

3320
/// Add a ramdisk file to the image.
3421
pub fn set_ramdisk(&mut self, ramdisk_path: &Path) -> &mut Self {
35-
self.ramdisk = Some(ramdisk_path.to_owned());
22+
self.image_builder.set_ramdisk(ramdisk_path.to_owned());
3623
self
3724
}
3825

39-
/// Configures the runtime behavior of the bootloader.
26+
/// Creates a configuration file (boot.json) that configures the runtime behavior of the bootloader.
4027
pub fn set_boot_config(&mut self, config: &BootConfig) -> &mut Self {
41-
self.config = Some(serde_json::to_string(&config).expect("failed to serialize BootConfig"));
28+
self.image_builder.set_boot_config(config);
4229
self
4330
}
4431

4532
/// Create a bootable BIOS disk image at the given path.
4633
pub fn create_disk_image(&self, out_path: &Path) -> anyhow::Result<()> {
47-
let bootsector_path = Path::new(env!("BIOS_BOOT_SECTOR_PATH"));
48-
let stage_2_path = Path::new(env!("BIOS_STAGE_2_PATH"));
49-
50-
let fat_partition = self
51-
.create_fat_partition()
52-
.context("failed to create FAT partition")?;
53-
54-
mbr::create_mbr_disk(
55-
bootsector_path,
56-
stage_2_path,
57-
fat_partition.path(),
58-
out_path,
59-
)
60-
.context("failed to create BIOS MBR disk image")?;
61-
62-
fat_partition
63-
.close()
64-
.context("failed to delete FAT partition after disk image creation")?;
65-
66-
Ok(())
67-
}
68-
69-
/// Creates an BIOS-bootable FAT partition with the kernel.
70-
fn create_fat_partition(&self) -> anyhow::Result<NamedTempFile> {
71-
let stage_3_path = Path::new(env!("BIOS_STAGE_3_PATH"));
72-
let stage_4_path = Path::new(env!("BIOS_STAGE_4_PATH"));
73-
74-
let mut files = BTreeMap::new();
75-
files.insert(crate::KERNEL_FILE_NAME, self.kernel.as_path());
76-
files.insert(BIOS_STAGE_3, stage_3_path);
77-
files.insert(BIOS_STAGE_4, stage_4_path);
78-
if let Some(ramdisk_path) = &self.ramdisk {
79-
files.insert(crate::RAMDISK_FILE_NAME, ramdisk_path);
80-
}
81-
82-
let mut config_file: NamedTempFile;
83-
84-
if let Some(config_ser) = &self.config {
85-
config_file = NamedTempFile::new()
86-
.context("failed to create temp file")
87-
.unwrap();
88-
writeln!(config_file, "{config_ser}")?;
89-
files.insert(crate::CONFIG_FILE_NAME, config_file.path());
90-
}
91-
92-
let out_file = NamedTempFile::new().context("failed to create temp file")?;
93-
fat::create_fat_filesystem(files, out_file.path())
94-
.context("failed to create BIOS FAT filesystem")?;
95-
96-
Ok(out_file)
34+
self.image_builder.create_bios_image(out_path)
9735
}
9836
}

src/fat.rs

+27-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1+
use crate::file_data_source::FileDataSource;
12
use anyhow::Context;
2-
use std::{collections::BTreeMap, fs, io, path::Path};
3+
use fatfs::Dir;
4+
use std::fs::File;
5+
use std::{collections::BTreeMap, fs, path::Path};
36

47
use crate::KERNEL_FILE_NAME;
58

69
pub fn create_fat_filesystem(
7-
files: BTreeMap<&str, &Path>,
10+
files: BTreeMap<&str, &FileDataSource>,
811
out_fat_path: &Path,
912
) -> anyhow::Result<()> {
1013
const MB: u64 = 1024 * 1024;
1114

1215
// calculate needed size
1316
let mut needed_size = 0;
14-
for path in files.values() {
15-
let file_size = fs::metadata(path)
16-
.with_context(|| format!("failed to read metadata of file `{}`", path.display()))?
17-
.len();
18-
needed_size += file_size;
17+
for source in files.values() {
18+
needed_size += source.len()?;
1919
}
2020

2121
// create new filesystem image file at the given path and set its length
@@ -31,7 +31,9 @@ pub fn create_fat_filesystem(
3131

3232
// choose a file system label
3333
let mut label = *b"MY_RUST_OS!";
34-
if let Some(path) = files.get(KERNEL_FILE_NAME) {
34+
35+
// This __should__ always be a file, but maybe not. Should we allow the caller to set the volume label instead?
36+
if let Some(FileDataSource::File(path)) = files.get(KERNEL_FILE_NAME) {
3537
if let Some(name) = path.file_stem() {
3638
let converted = name.to_string_lossy();
3739
let name = converted.as_bytes();
@@ -48,10 +50,17 @@ pub fn create_fat_filesystem(
4850
fatfs::format_volume(&fat_file, format_options).context("Failed to format FAT file")?;
4951
let filesystem = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new())
5052
.context("Failed to open FAT file system of UEFI FAT file")?;
53+
let root_dir = filesystem.root_dir();
5154

5255
// copy files to file system
53-
let root_dir = filesystem.root_dir();
54-
for (target_path_raw, file_path) in files {
56+
add_files_to_image(&root_dir, files)
57+
}
58+
59+
pub fn add_files_to_image(
60+
root_dir: &Dir<&File>,
61+
files: BTreeMap<&str, &FileDataSource>,
62+
) -> anyhow::Result<()> {
63+
for (target_path_raw, source) in files {
5564
let target_path = Path::new(target_path_raw);
5665
// create parent directories
5766
let ancestors: Vec<_> = target_path.ancestors().skip(1).collect();
@@ -70,12 +79,14 @@ pub fn create_fat_filesystem(
7079
.create_file(target_path_raw)
7180
.with_context(|| format!("failed to create file at `{}`", target_path.display()))?;
7281
new_file.truncate().unwrap();
73-
io::copy(
74-
&mut fs::File::open(file_path)
75-
.with_context(|| format!("failed to open `{}` for copying", file_path.display()))?,
76-
&mut new_file,
77-
)
78-
.with_context(|| format!("failed to copy `{}` to FAT filesystem", file_path.display()))?;
82+
83+
source.copy_to(&mut new_file).with_context(|| {
84+
format!(
85+
"failed to copy source data `{:?}` to file at `{}`",
86+
source,
87+
target_path.display()
88+
)
89+
})?;
7990
}
8091

8192
Ok(())

src/file_data_source.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use alloc::vec::Vec;
2+
use anyhow::Context;
3+
use core::fmt::{Debug, Formatter};
4+
5+
use std::io::Cursor;
6+
use std::path::PathBuf;
7+
use std::{fs, io};
8+
9+
#[derive(Clone)]
10+
/// Defines a data source, either a source `std::path::PathBuf`, or a vector of bytes.
11+
pub enum FileDataSource {
12+
File(PathBuf),
13+
Data(Vec<u8>),
14+
}
15+
16+
impl Debug for FileDataSource {
17+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
18+
match self {
19+
FileDataSource::File(file) => {
20+
f.write_fmt(format_args!("data source: File {}", file.display()))
21+
}
22+
FileDataSource::Data(d) => {
23+
f.write_fmt(format_args!("data source: {} raw bytes ", d.len()))
24+
}
25+
}
26+
}
27+
}
28+
29+
impl FileDataSource {
30+
/// Get the length of the inner data source
31+
pub fn len(&self) -> anyhow::Result<u64> {
32+
Ok(match self {
33+
FileDataSource::File(path) => fs::metadata(path)
34+
.with_context(|| format!("failed to read metadata of file `{}`", path.display()))?
35+
.len(),
36+
FileDataSource::Data(v) => v.len() as u64,
37+
})
38+
}
39+
/// Copy this data source to the specified target that implements io::Write
40+
pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> {
41+
match self {
42+
FileDataSource::File(file_path) => {
43+
io::copy(
44+
&mut fs::File::open(file_path).with_context(|| {
45+
format!("failed to open `{}` for copying", file_path.display())
46+
})?,
47+
target,
48+
)?;
49+
}
50+
FileDataSource::Data(contents) => {
51+
let mut cursor = Cursor::new(contents);
52+
io::copy(&mut cursor, target)?;
53+
}
54+
};
55+
56+
Ok(())
57+
}
58+
}
File renamed without changes.

0 commit comments

Comments
 (0)