springboard 3.0.1

A bootloader that works on both BIOS and UEFI systems.
Documentation
/// Creates a GPT disk from the provided FAT image.
pub fn create_gpt_disk(fat_image: &Path, out_gpt_path: &Path) -> anyhow::Result<()> {
   // create new file
   let mut disk = fs::OpenOptions::new()
      .create(true)
      .truncate(true)
      .read(true)
      .write(true)
      .open(out_gpt_path)
      .with_context(|| format!("failed to create GPT file at `{}`", out_gpt_path.display()))?;

   // set file size
   let partition_size: u64 = fs::metadata(fat_image)
      .context("failed to read metadata of fat image")?
      .len();
   let disk_size = partition_size + 1024 * 64; // for GPT headers
   disk.set_len(disk_size)
      .context("failed to set GPT image file length")?;

   // create a protective MBR at LBA0 so that disk is not considered
   // unformatted on BIOS systems
   let mbr = gpt::mbr::ProtectiveMBR::with_lb_size(
      u32::try_from((disk_size / 512) - 1).unwrap_or(0xFF_FF_FF_FF),
   );
   mbr.overwrite_lba0(&mut disk)
      .context("failed to write protective MBR")?;

   // create new GPT structure
   let block_size = gpt::disk::LogicalBlockSize::Lb512;
   let mut gpt = gpt::GptConfig::new()
      .writable(true)
      .initialized(false)
      .logical_block_size(block_size)
      .create_from_device(Box::new(&mut disk), None)
      .context("failed to create GPT structure in file")?;
   gpt.update_partitions(Default::default())
      .context("failed to update GPT partitions")?;

   // add new EFI system partition and get its byte offset in the file
   let partition_id = gpt
      .add_partition("boot", partition_size, gpt::partition_types::EFI, 0, None)
      .context("failed to add boot EFI partition")?;
   let partition = gpt
      .partitions()
      .get(&partition_id)
      .context("failed to open boot partition after creation")?;
   let start_offset = partition
      .bytes_start(block_size)
      .context("failed to get start offset of boot partition")?;

   // close the GPT structure and write out changes
   gpt.write().context("failed to write out GPT changes")?;

   // place the FAT filesystem in the newly created partition
   disk.seek(io::SeekFrom::Start(start_offset))
      .context("failed to seek to start offset")?;
   io::copy(
      &mut File::open(fat_image).context("failed to open FAT image")?,
      &mut disk,
   )
      .context("failed to copy FAT image to GPT disk")?;

   Ok(())
}

// IMPORTS //

use {
   anyhow::Context,
   std::{
      fs::{self, File},
      io::{self, Seek},
      path::Path,
   }
};