use fatfs::{Dir, FatType, FileSystem, FormatVolumeOptions, FsOptions};
use std::{
fs::{self, OpenOptions},
io::{self, Seek, Write},
path::Path,
};
fn copy_to_fat(fat_dir: &Dir<fs::File>, source_path: &Path, dest_name: &str) -> io::Result<()> {
let mut dest_file = fat_dir.create_file(dest_name)?;
let mut source_file = fs::File::open(source_path)?;
io::copy(&mut source_file, &mut dest_file)?;
Ok(())
}
pub fn create_fat_image(
fat_img_path: &Path,
loader_path: &Path,
kernel_path: &Path,
) -> io::Result<u32> {
if !loader_path.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("Loader file not found at {:?}", loader_path),
));
}
if !kernel_path.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("Kernel file not found at {:?}", kernel_path),
));
}
let loader_size = loader_path.metadata()?.len();
let kernel_size = kernel_path.metadata()?.len();
let content_size = loader_size + kernel_size;
const MIN_FAT_SIZE: u64 = 16 * 1024 * 1024; const FAT_OVERHEAD: u64 = 2 * 1024 * 1024; const SECTOR_SIZE: u64 = 512;
let mut logical_size = (content_size + FAT_OVERHEAD).div_ceil(SECTOR_SIZE) * SECTOR_SIZE;
if logical_size == 0 {
logical_size = SECTOR_SIZE;
}
let total_size = std::cmp::max(logical_size, MIN_FAT_SIZE);
let fat_type = if total_size <= 268_435_456 {
FatType::Fat16
} else {
FatType::Fat32
};
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(fat_img_path)?;
file.set_len(total_size)?;
file.flush()?;
file.seek(io::SeekFrom::Start(0))?;
fatfs::format_volume(
&mut file,
FormatVolumeOptions::new()
.fat_type(fat_type)
.bytes_per_sector(512), )?;
let fs = FileSystem::new(file, FsOptions::new())?;
let root_dir = fs.root_dir();
let efi_dir = root_dir.create_dir("EFI")?;
let boot_dir = efi_dir.create_dir("BOOT")?;
copy_to_fat(&boot_dir, loader_path, "BOOTX64.EFI")?;
copy_to_fat(&boot_dir, kernel_path, "KERNEL.EFI")?;
Ok((logical_size / SECTOR_SIZE) as u32)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::io::Read;
use tempfile::tempdir;
#[test]
fn test_create_fat_image() -> io::Result<()> {
let dir = tempdir()?;
let loader_path = dir.path().join("loader.efi");
let kernel_path = dir.path().join("kernel.elf");
let fat_img_path = dir.path().join("fat.img");
let loader_content = b"UEFI loader";
let kernel_content = b"ELF kernel";
fs::write(&loader_path, loader_content)?;
fs::write(&kernel_path, kernel_content)?;
create_fat_image(&fat_img_path, &loader_path, &kernel_path)?;
assert!(fat_img_path.exists());
let fat_img_size = fat_img_path.metadata()?.len();
assert!(fat_img_size > 0);
let fat_file = fs::File::open(&fat_img_path)?;
let fs = FileSystem::new(fat_file, FsOptions::new())?;
let root_dir = fs.root_dir();
let mut loader_in_fat = root_dir.open_file("EFI/BOOT/BOOTX64.EFI")?;
let mut loader_in_fat_content = Vec::new();
loader_in_fat.read_to_end(&mut loader_in_fat_content)?;
assert_eq!(loader_content, loader_in_fat_content.as_slice());
let mut kernel_in_fat = root_dir.open_file("EFI/BOOT/KERNEL.EFI")?;
let mut kernel_in_fat_content = Vec::new();
kernel_in_fat.read_to_end(&mut kernel_in_fat_content)?;
assert_eq!(kernel_content, kernel_in_fat_content.as_slice());
Ok(())
}
}