use crate::mft::mft_location::MftLocationOnDisk;
use crate::ntfs::ntfs_drive_handle::NtfsDriveHandle;
use crate::windows_utils::storage::HandleReadExt;
use tracing::instrument;
use uom::si::information::byte;
use uom::si::usize::Information;
pub struct NtfsBootSector {
pub data: [u8; 512],
}
impl NtfsBootSector {
#[instrument(skip_all)]
pub fn try_from_handle(drive_handle: &NtfsDriveHandle) -> eyre::Result<Self> {
Ok(NtfsBootSector {
data: {
let mut data = [0u8; 512];
drive_handle.try_read_exact(0, data.as_mut_slice())?;
data
},
})
}
#[must_use]
pub fn bytes_per_sector(&self) -> u16 {
u16::from_le_bytes([self.data[0x0b], self.data[0x0c]])
}
#[must_use]
pub fn sectors_per_cluster(&self) -> u8 {
self.data[0x0d]
}
#[must_use]
pub fn mft_cluster_number(&self) -> u64 {
u64::from_le_bytes([
self.data[0x30],
self.data[0x31],
self.data[0x32],
self.data[0x33],
self.data[0x34],
self.data[0x35],
self.data[0x36],
self.data[0x37],
])
}
#[must_use]
pub fn bytes_per_cluster(&self) -> usize {
self.bytes_per_sector() as usize * self.sectors_per_cluster() as usize
}
#[must_use]
pub fn file_record_size(&self) -> Information {
let val = i8::from_ne_bytes([self.data[0x40]]);
let bytes = if val < 0 {
let shift =
usize::try_from(-isize::from(val)).expect("record size exponent fits in usize");
(1usize) << shift
} else {
let cluster_count = usize::try_from(val).expect("cluster count fits in usize");
cluster_count * self.bytes_per_cluster()
};
Information::new::<byte>(bytes)
}
#[must_use]
pub fn mft_location(&self) -> MftLocationOnDisk {
self.into()
}
}
impl std::fmt::Debug for NtfsBootSector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NtfsBootSector")
.field("bytes_per_sector", &self.bytes_per_sector())
.field("sectors_per_cluster", &self.sectors_per_cluster())
.field("mft_cluster_number", &self.mft_cluster_number())
.field("file_record_size", &self.file_record_size())
.field("mft_location", &self.mft_location())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mk_boot_sector(
bytes_per_sector: u16,
sectors_per_cluster: u8,
mft_cluster: u64,
rec_byte: i8,
) -> NtfsBootSector {
let mut bs = NtfsBootSector { data: [0u8; 512] };
bs.data[0x0b] = (bytes_per_sector & 0xFF) as u8;
bs.data[0x0c] = (bytes_per_sector >> 8) as u8;
bs.data[0x0d] = sectors_per_cluster;
bs.data[0x30..0x38].copy_from_slice(&mft_cluster.to_le_bytes());
bs.data[0x40] = rec_byte.to_ne_bytes()[0];
bs
}
#[test]
fn bytes_per_file_record_negative_exponent() {
let bs = mk_boot_sector(512, 8, 100, -10);
assert_eq!(bs.bytes_per_cluster(), 512 * 8);
assert_eq!(bs.file_record_size().get::<byte>(), 1024);
}
#[test]
fn bytes_per_file_record_positive_clusters() {
let bs = mk_boot_sector(512, 4, 200, 2);
assert_eq!(bs.bytes_per_cluster(), 2048);
assert_eq!(bs.file_record_size().get::<byte>(), 4096);
}
}