use clap::{ArgGroup, Parser};
use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;
use std::process::ExitCode;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Parser)]
#[command(version, about = "Clean-room MIT replacement for ms-sys.")]
#[command(group = ArgGroup::new("variant").required(true).multiple(false))]
struct Args {
device: PathBuf,
#[arg(long, group = "variant", visible_alias = "mbr")]
mbr_xp: bool,
#[arg(long, group = "variant", visible_alias = "mbr7")]
mbr_win7: bool,
#[arg(long, group = "variant", visible_alias = "fat32nt")]
fat32_ntldr: bool,
#[arg(long, group = "variant", visible_alias = "fat32pe")]
fat32_bootmgr: bool,
#[arg(long, group = "variant", visible_alias = "ntfs")]
ntfs_bootmgr: bool,
}
enum Variant {
MbrXp,
MbrWin7,
Fat32Ntldr,
Fat32Bootmgr,
NtfsBootmgr,
}
impl Variant {
fn label(&self) -> &'static str {
match self {
Variant::MbrXp => "Windows XP MBR",
Variant::MbrWin7 => "Windows 7 MBR",
Variant::Fat32Ntldr => "FAT32 NTLDR PBR",
Variant::Fat32Bootmgr => "FAT32 BOOTMGR PBR",
Variant::NtfsBootmgr => "NTFS BOOTMGR PBR",
}
}
}
fn main() -> ExitCode {
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("mkmsbr: {e}");
ExitCode::from(1)
}
}
}
fn run() -> Result<()> {
let args = Args::parse();
let variant = if args.mbr_xp {
Variant::MbrXp
} else if args.mbr_win7 {
Variant::MbrWin7
} else if args.fat32_ntldr {
Variant::Fat32Ntldr
} else if args.fat32_bootmgr {
Variant::Fat32Bootmgr
} else {
Variant::NtfsBootmgr
};
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(&args.device)?;
let written = match variant {
Variant::MbrXp => splice_and_write_mbr(&mut file, mkmsbr::MBR_XP_BOOT)?,
Variant::MbrWin7 => splice_and_write_mbr(&mut file, mkmsbr::MBR_WIN7_BOOT)?,
Variant::Fat32Ntldr => {
splice_and_write_fat32_multi(&mut file, mkmsbr::FAT32_PBR_NTLDR_MULTI_BOOT)?
}
Variant::Fat32Bootmgr => {
splice_and_write_fat32_multi(&mut file, mkmsbr::FAT32_PBR_BOOTMGR_MULTI_BOOT)?
}
Variant::NtfsBootmgr => {
splice_and_write_ntfs_multi(&mut file, mkmsbr::NTFS_PBR_BOOTMGR_MULTI_BOOT)?
}
};
println!(
"Wrote {} ({} bytes) to {}",
variant.label(),
written,
args.device.display()
);
Ok(())
}
fn splice_and_write_mbr(file: &mut std::fs::File, boot: &[u8]) -> Result<usize> {
let mut existing = [0u8; 512];
file.read_exact(&mut existing)?;
let out = mkmsbr::splice_mbr(&existing, boot)?;
file.seek(SeekFrom::Start(0))?;
file.write_all(&out)?;
file.flush()?;
Ok(out.len())
}
fn splice_and_write_fat32_multi(file: &mut std::fs::File, blob: &[u8]) -> Result<usize> {
let mut existing = [0u8; 1024];
file.read_exact(&mut existing)?;
let out = mkmsbr::splice_fat32_pbr_multi(&existing, blob)?;
file.seek(SeekFrom::Start(0))?;
file.write_all(&out)?;
file.flush()?;
Ok(out.len())
}
fn splice_and_write_ntfs_multi(file: &mut std::fs::File, blob: &[u8]) -> Result<usize> {
let mut existing = [0u8; 1024];
file.read_exact(&mut existing)?;
let out = mkmsbr::splice_ntfs_pbr_multi(&existing, blob)?;
file.seek(SeekFrom::Start(0))?;
file.write_all(&out)?;
file.flush()?;
Ok(out.len())
}