use crate::disk;
use crate::DiskDevice;
use std::io::{Read, Write};
use std::{fmt, io};
pub struct ProtectiveMBR {
bootcode: [u8; 440],
disk_signature: [u8; 4],
unknown: u16,
partitions: [PartRecord; 4],
signature: [u8; 2],
}
impl fmt::Debug for ProtectiveMBR {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Protective MBR, partitions: {:#?}", self.partitions)
}
}
impl Default for ProtectiveMBR {
fn default() -> Self {
Self {
bootcode: [0x00; 440],
disk_signature: [0x00; 4],
unknown: 0,
partitions: [
PartRecord::new_protective(None),
PartRecord::zero(),
PartRecord::zero(),
PartRecord::zero(),
],
signature: [0x55, 0xAA],
}
}
}
impl ProtectiveMBR {
pub fn new() -> Self {
Self::default()
}
pub fn with_lb_size(lb_size: u32) -> Self {
Self {
bootcode: [0x00; 440],
disk_signature: [0x00; 4],
unknown: 0,
partitions: [
PartRecord::new_protective(Some(lb_size)),
PartRecord::zero(),
PartRecord::zero(),
PartRecord::zero(),
],
signature: [0x55, 0xAA],
}
}
pub fn from_bytes(buf: &[u8], sector_size: disk::LogicalBlockSize) -> io::Result<Self> {
let mut pmbr = Self::new();
let totlen: u64 = sector_size.into();
if buf.len() != (totlen as usize) {
return Err(io::Error::new(io::ErrorKind::Other, "invalid MBR length"));
}
pmbr.bootcode.copy_from_slice(&buf[0..440]);
pmbr.disk_signature.copy_from_slice(&buf[440..444]);
pmbr.unknown = u16::from_le_bytes(read_exact_buff!(pmbru, &buf[444..446], 2));
for (i, p) in pmbr.partitions.iter_mut().enumerate() {
let start = i
.checked_mul(16)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"partition record overflow - entry start",
)
})?
.checked_add(446)
.ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "partition overflow - start offset")
})?;
let end = start.checked_add(16).ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"partition record overflow - end offset",
)
})?;
*p = PartRecord::from_bytes(&buf[start..end])?;
}
pmbr.signature.copy_from_slice(&buf[510..512]);
if pmbr.signature != [0x55, 0xAA] {
return Err(io::Error::new(
io::ErrorKind::Other,
"invalid MBR signature",
));
};
Ok(pmbr)
}
pub fn from_disk<D: DiskDevice>(
device: &mut D,
sector_size: disk::LogicalBlockSize
) -> io::Result<Self> {
let totlen: u64 = sector_size.into();
let mut buf = vec![0_u8; totlen as usize];
let cur = device.seek(io::SeekFrom::Current(0))?;
device.seek(io::SeekFrom::Start(0))?;
device.read_exact(&mut buf)?;
let pmbr = Self::from_bytes(&buf, sector_size);
device.seek(io::SeekFrom::Start(cur))?;
pmbr
}
pub fn as_bytes(&self) -> io::Result<Vec<u8>> {
let mut buf: Vec<u8> = Vec::with_capacity(512);
buf.write_all(&self.bootcode)?;
buf.write_all(&self.disk_signature)?;
buf.write_all(&self.unknown.to_le_bytes())?;
for p in &self.partitions {
let pdata = p.as_bytes()?;
buf.write_all(&pdata)?;
}
buf.write_all(&self.signature)?;
Ok(buf)
}
pub fn bootcode(&self) -> &[u8; 440] {
&self.bootcode
}
pub fn set_bootcode(&mut self, bootcode: [u8; 440]) -> &Self {
self.bootcode = bootcode;
self
}
pub fn disk_signature(&self) -> &[u8; 4] {
&self.disk_signature
}
pub fn set_disk_signature(&mut self, sig: [u8; 4]) -> &Self {
self.disk_signature = sig;
self
}
pub fn partition(&self, partition_index: usize) -> Option<PartRecord> {
if partition_index >= self.partitions.len() {
None
} else {
Some(self.partitions[partition_index])
}
}
pub fn set_partition(&mut self, partition_index: usize, partition: PartRecord) -> Option<PartRecord> {
if partition_index >= self.partitions.len() {
None
} else {
Some(std::mem::replace(&mut self.partitions[partition_index], partition))
}
}
pub fn overwrite_lba0<D: DiskDevice>(&self, device: &mut D) -> io::Result<usize> {
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(0))?;
let data = self.as_bytes()?;
device.write_all(&data)?;
device.flush()?;
device.seek(io::SeekFrom::Start(cur))?;
Ok(data.len())
}
pub fn update_conservative<D: DiskDevice>(&self, device: &mut D) -> io::Result<usize> {
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(446))?;
for p in &self.partitions {
let pdata = p.as_bytes()?;
device.write_all(&pdata)?;
}
device.write_all(&self.signature)?;
device.flush()?;
device.seek(io::SeekFrom::Start(cur))?;
let bytes_updated: usize = (16 * 4) + 2;
Ok(bytes_updated)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PartRecord {
pub boot_indicator: u8,
pub start_head: u8,
pub start_sector: u8,
pub start_track: u8,
pub os_type: u8,
pub end_head: u8,
pub end_sector: u8,
pub end_track: u8,
pub lb_start: u32,
pub lb_size: u32,
}
impl PartRecord {
pub fn new_protective(lb_size: Option<u32>) -> Self {
let size = lb_size.unwrap_or(0xFF_FF_FF_FF);
Self {
boot_indicator: 0x00,
start_head: 0x00,
start_sector: 0x02,
start_track: 0x00,
os_type: 0xEE,
end_head: 0xFF,
end_sector: 0xFF,
end_track: 0xFF,
lb_start: 1,
lb_size: size,
}
}
pub fn zero() -> Self {
Self {
boot_indicator: 0x00,
start_head: 0x00,
start_sector: 0x00,
start_track: 0x00,
os_type: 0x00,
end_head: 0x00,
end_sector: 0x00,
end_track: 0x00,
lb_start: 0,
lb_size: 0,
}
}
pub fn from_bytes(buf: &[u8]) -> io::Result<Self> {
if buf.len() != 16 {
return Err(io::Error::new(
io::ErrorKind::Other,
"invalid length for a partition record",
));
};
let pr = Self {
boot_indicator: buf[0],
start_head: buf[1],
start_sector: buf[2],
start_track: buf[3],
os_type: buf[4],
end_head: buf[5],
end_sector: buf[6],
end_track: buf[7],
lb_start: u32::from_le_bytes(read_exact_buff!(lbs, &buf[8..12], 4)),
lb_size: u32::from_le_bytes(read_exact_buff!(lbsize, &buf[12..16], 4) ),
};
Ok(pr)
}
pub fn as_bytes(&self) -> io::Result<Vec<u8>> {
let mut buf: Vec<u8> = Vec::with_capacity(16);
buf.write_all(&self.boot_indicator.to_le_bytes())?;
buf.write_all(&self.start_head.to_le_bytes())?;
buf.write_all(&self.start_sector.to_le_bytes())?;
buf.write_all(&self.start_track.to_le_bytes())?;
buf.write_all(&self.os_type.to_le_bytes())?;
buf.write_all(&self.end_head.to_le_bytes())?;
buf.write_all(&self.end_sector.to_le_bytes())?;
buf.write_all(&self.end_track.to_le_bytes())?;
buf.write_all(&self.lb_start.to_le_bytes())?;
buf.write_all(&self.lb_size.to_le_bytes())?;
Ok(buf)
}
}
pub fn read_bootcode<D: DiskDevice>(device: &mut D) -> io::Result<[u8; 440]> {
let bootcode_offset = 0;
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(bootcode_offset))?;
let mut bootcode = [0x00; 440];
device.read_exact(&mut bootcode)?;
device.seek(io::SeekFrom::Start(cur))?;
Ok(bootcode)
}
pub fn write_bootcode<D: DiskDevice>(device: &mut D, bootcode: &[u8; 440]) -> io::Result<()> {
let bootcode_offset = 0;
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(bootcode_offset))?;
device.write_all(bootcode)?;
device.flush()?;
device.seek(io::SeekFrom::Start(cur))?;
Ok(())
}
pub fn read_disk_signature<D: DiskDevice>(device: &mut D) -> io::Result<[u8; 4]> {
let dsig_offset = 440;
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(dsig_offset))?;
let mut dsig = [0x00; 4];
device.read_exact(&mut dsig)?;
device.seek(io::SeekFrom::Start(cur))?;
Ok(dsig)
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
pub fn write_disk_signature<D: DiskDevice>(device: &mut D, sig: &[u8; 4]) -> io::Result<()> {
let dsig_offset = 440;
let cur = device.seek(io::SeekFrom::Current(0))?;
let _ = device.seek(io::SeekFrom::Start(dsig_offset))?;
device.write_all(sig)?;
device.flush()?;
device.seek(io::SeekFrom::Start(cur))?;
Ok(())
}