refactor: Convert between sectors, clusters and bytes in one place (BPB)

Change encapsulates method of calculating it so it can be optimized in
future in one place (e.g. using bit shifts).
This commit is contained in:
Rafał Harabień 2018-12-08 19:38:31 +01:00
parent cf3256bfda
commit 70b0a771b8
4 changed files with 95 additions and 58 deletions

View File

@ -10,7 +10,10 @@ use dir_entry::DIR_ENTRY_SIZE;
use fs::{FatType, FsStatusFlags, FormatVolumeOptions}; use fs::{FatType, FsStatusFlags, FormatVolumeOptions};
use table::RESERVED_FAT_ENTRIES; use table::RESERVED_FAT_ENTRIES;
#[allow(dead_code)] const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub(crate) struct BiosParameterBlock { pub(crate) struct BiosParameterBlock {
pub(crate) bytes_per_sector: u16, pub(crate) bytes_per_sector: u16,
@ -288,6 +291,10 @@ impl BiosParameterBlock {
} }
} }
pub(crate) fn reserved_sectors(&self) -> u32 {
self.reserved_sectors as u32
}
pub(crate) fn root_dir_sectors(&self) -> u32 { pub(crate) fn root_dir_sectors(&self) -> u32 {
let root_dir_bytes = self.root_entries as u32 * DIR_ENTRY_SIZE as u32; let root_dir_bytes = self.root_entries as u32 * DIR_ENTRY_SIZE as u32;
(root_dir_bytes + self.bytes_per_sector as u32 - 1) / self.bytes_per_sector as u32 (root_dir_bytes + self.bytes_per_sector as u32 - 1) / self.bytes_per_sector as u32
@ -300,7 +307,7 @@ impl BiosParameterBlock {
pub(crate) fn first_data_sector(&self) -> u32 { pub(crate) fn first_data_sector(&self) -> u32 {
let root_dir_sectors = self.root_dir_sectors(); let root_dir_sectors = self.root_dir_sectors();
let fat_sectors = self.sectors_per_all_fats(); let fat_sectors = self.sectors_per_all_fats();
self.reserved_sectors as u32 + fat_sectors + root_dir_sectors self.reserved_sectors() + fat_sectors + root_dir_sectors
} }
pub(crate) fn total_clusters(&self) -> u32 { pub(crate) fn total_clusters(&self) -> u32 {
@ -309,10 +316,36 @@ impl BiosParameterBlock {
let data_sectors = total_sectors - first_data_sector; let data_sectors = total_sectors - first_data_sector;
data_sectors / self.sectors_per_cluster as u32 data_sectors / self.sectors_per_cluster as u32
} }
pub(crate) fn bytes_from_sectors(&self, sectors: u32) -> u64 {
// Note: total number of sectors is a 32 bit number so offsets have to be 64 bit
(sectors as u64) * self.bytes_per_sector as u64
} }
#[allow(dead_code)] pub(crate) fn sectors_from_clusters(&self, clusters: u32) -> u32 {
pub(crate) struct BootRecord { // Note: total number of sectors is a 32 bit number so it should not overflow
clusters * (self.sectors_per_cluster as u32)
}
pub(crate) fn cluster_size(&self) -> u32 {
self.sectors_per_cluster as u32 * self.bytes_per_sector as u32
}
pub(crate) fn clusters_from_bytes(&self, bytes: u64) -> u32 {
let cluster_size = self.cluster_size() as i64;
((bytes as i64 + cluster_size - 1) / cluster_size) as u32
}
pub(crate) fn fs_info_sector(&self) -> u32 {
self.fs_info_sector as u32
}
pub(crate) fn backup_boot_sector(&self) -> u32 {
self.backup_boot_sector as u32
}
}
pub(crate) struct BootSector {
bootjmp: [u8; 3], bootjmp: [u8; 3],
oem_name: [u8; 8], oem_name: [u8; 8],
pub(crate) bpb: BiosParameterBlock, pub(crate) bpb: BiosParameterBlock,
@ -320,9 +353,9 @@ pub(crate) struct BootRecord {
boot_sig: [u8; 2], boot_sig: [u8; 2],
} }
impl BootRecord { impl BootSector {
pub(crate) fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BootRecord> { pub(crate) fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BootSector> {
let mut boot: BootRecord = Default::default(); let mut boot: BootSector = Default::default();
rdr.read_exact(&mut boot.bootjmp)?; rdr.read_exact(&mut boot.bootjmp)?;
rdr.read_exact(&mut boot.oem_name)?; rdr.read_exact(&mut boot.oem_name)?;
boot.bpb = BiosParameterBlock::deserialize(rdr)?; boot.bpb = BiosParameterBlock::deserialize(rdr)?;
@ -362,9 +395,9 @@ impl BootRecord {
} }
} }
impl Default for BootRecord { impl Default for BootSector {
fn default() -> BootRecord { fn default() -> BootSector {
BootRecord { BootSector {
bootjmp: Default::default(), bootjmp: Default::default(),
oem_name: Default::default(), oem_name: Default::default(),
bpb: Default::default(), bpb: Default::default(),
@ -374,10 +407,6 @@ impl Default for BootRecord {
} }
} }
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
pub(crate) fn determine_fat_type(total_bytes: u64) -> FatType { pub(crate) fn determine_fat_type(total_bytes: u64) -> FatType {
if total_bytes < 4 * MB { if total_bytes < 4 * MB {
FatType::Fat12 FatType::Fat12
@ -521,8 +550,8 @@ fn format_bpb(options: &FormatVolumeOptions) -> io::Result<(BiosParameterBlock,
Ok((bpb, fat_type)) Ok((bpb, fat_type))
} }
pub(crate) fn format_boot_sector(options: &FormatVolumeOptions) -> io::Result<(BootRecord, FatType)> { pub(crate) fn format_boot_sector(options: &FormatVolumeOptions) -> io::Result<(BootSector, FatType)> {
let mut boot: BootRecord = Default::default(); let mut boot: BootSector = Default::default();
let (bpb, fat_type) = format_bpb(options)?; let (bpb, fat_type) = format_bpb(options)?;
boot.bpb = bpb; boot.bpb = bpb;
boot.oem_name.copy_from_slice("MSWIN4.1".as_bytes()); boot.oem_name.copy_from_slice("MSWIN4.1".as_bytes());

View File

@ -319,15 +319,15 @@ impl<'a, T: ReadWriteSeek> Seek for File<'a, T> {
new_pos = s as i64; new_pos = s as i64;
} }
} }
let mut new_pos = new_pos as u32;
trace!("file seek {} -> {} - entry {:?}", self.offset, new_pos, self.entry); trace!("file seek {} -> {} - entry {:?}", self.offset, new_pos, self.entry);
if new_pos == self.offset as i64 { if new_pos == self.offset {
// position is the same - nothing to do // position is the same - nothing to do
return Ok(self.offset as u64); return Ok(self.offset as u64);
} }
let cluster_size = self.fs.cluster_size();
// get number of clusters to seek (favoring previous cluster in corner case) // get number of clusters to seek (favoring previous cluster in corner case)
let cluster_count = ((new_pos + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize; let cluster_count = (self.fs.clusters_from_bytes(new_pos as u64) as i32 - 1) as isize;
let old_cluster_count = ((self.offset as i64 + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize; let old_cluster_count = (self.fs.clusters_from_bytes(self.offset as u64) as i32 - 1) as isize;
let new_cluster = if new_pos == 0 { let new_cluster = if new_pos == 0 {
None None
} else if cluster_count == old_cluster_count { } else if cluster_count == old_cluster_count {
@ -342,7 +342,7 @@ impl<'a, T: ReadWriteSeek> Seek for File<'a, T> {
Some(r) => r?, Some(r) => r?,
None => { None => {
// chain ends before new position - seek to end of last cluster // chain ends before new position - seek to end of last cluster
new_pos = (i + 1) as i64 * cluster_size as i64; new_pos = self.fs.bytes_from_clusters((i + 1) as u32) as u32;
break; break;
}, },
}; };

View File

@ -12,7 +12,7 @@ use io::{Error, ErrorKind, SeekFrom};
use byteorder::LittleEndian; use byteorder::LittleEndian;
use byteorder_ext::{ReadBytesExt, WriteBytesExt}; use byteorder_ext::{ReadBytesExt, WriteBytesExt};
use boot_sector::{BootRecord, BiosParameterBlock, format_boot_sector}; use boot_sector::{BootSector, BiosParameterBlock, format_boot_sector};
use dir::{Dir, DirRawStream}; use dir::{Dir, DirRawStream};
use file::File; use file::File;
use table::{alloc_cluster, count_free_clusters, read_fat_flags, format_fat, ClusterIterator, RESERVED_FAT_ENTRIES}; use table::{alloc_cluster, count_free_clusters, read_fat_flags, format_fat, ClusterIterator, RESERVED_FAT_ENTRIES};
@ -300,7 +300,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
// read boot sector // read boot sector
let bpb = { let bpb = {
let boot = BootRecord::deserialize(&mut disk)?; let boot = BootSector::deserialize(&mut disk)?;
boot.validate()?; boot.validate()?;
boot.bpb boot.bpb
}; };
@ -312,7 +312,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
// read FSInfo sector if this is FAT32 // read FSInfo sector if this is FAT32
let mut fs_info = if fat_type == FatType::Fat32 { let mut fs_info = if fat_type == FatType::Fat32 {
disk.seek(SeekFrom::Start(bpb.fs_info_sector as u64 * bpb.bytes_per_sector as u64))?; disk.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.fs_info_sector())))?;
FsInfoSector::deserialize(&mut disk)? FsInfoSector::deserialize(&mut disk)?
} else { } else {
FsInfoSector::default() FsInfoSector::default()
@ -429,21 +429,29 @@ impl<T: ReadWriteSeek> FileSystem<T> {
} }
fn offset_from_sector(&self, sector: u32) -> u64 { fn offset_from_sector(&self, sector: u32) -> u64 {
(sector as u64) * self.bpb.bytes_per_sector as u64 self.bpb.bytes_from_sectors(sector)
} }
fn sector_from_cluster(&self, cluster: u32) -> u32 { fn sector_from_cluster(&self, cluster: u32) -> u32 {
((cluster - 2) * self.bpb.sectors_per_cluster as u32) + self.first_data_sector self.first_data_sector + self.bpb.sectors_from_clusters(cluster - RESERVED_FAT_ENTRIES)
} }
pub(crate) fn cluster_size(&self) -> u32 { pub fn cluster_size(&self) -> u32 {
self.bpb.sectors_per_cluster as u32 * self.bpb.bytes_per_sector as u32 self.bpb.cluster_size()
} }
pub(crate) fn offset_from_cluster(&self, cluser: u32) -> u64 { pub(crate) fn offset_from_cluster(&self, cluser: u32) -> u64 {
self.offset_from_sector(self.sector_from_cluster(cluser)) self.offset_from_sector(self.sector_from_cluster(cluser))
} }
pub(crate) fn bytes_from_clusters(&self, clusters: u32) -> u64 {
self.bpb.bytes_from_sectors(self.bpb.sectors_from_clusters(clusters))
}
pub(crate) fn clusters_from_bytes(&self, bytes: u64) -> u32 {
self.bpb.clusters_from_bytes(bytes)
}
fn fat_slice<'b>(&'b self) -> DiskSlice<FsIoAdapter<'b, T>> { fn fat_slice<'b>(&'b self) -> DiskSlice<FsIoAdapter<'b, T>> {
let io = FsIoAdapter { let io = FsIoAdapter {
fs: self, fs: self,
@ -482,7 +490,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
if zero { if zero {
let mut disk = self.disk.borrow_mut(); let mut disk = self.disk.borrow_mut();
disk.seek(SeekFrom::Start(self.offset_from_cluster(cluster)))?; disk.seek(SeekFrom::Start(self.offset_from_cluster(cluster)))?;
write_zeros(&mut *disk, self.cluster_size() as usize)?; write_zeros(&mut *disk, self.cluster_size() as u64)?;
} }
let mut fs_info = self.fs_info.borrow_mut(); let mut fs_info = self.fs_info.borrow_mut();
fs_info.set_next_free_cluster(cluster + 1); fs_info.set_next_free_cluster(cluster + 1);
@ -623,10 +631,10 @@ fn fat_slice<T: ReadWriteSeek>(io: T, bpb: &BiosParameterBlock) -> DiskSlice<T>
let sectors_per_fat = bpb.sectors_per_fat(); let sectors_per_fat = bpb.sectors_per_fat();
let mirroring_enabled = bpb.mirroring_enabled(); let mirroring_enabled = bpb.mirroring_enabled();
let (fat_first_sector, mirrors) = if mirroring_enabled { let (fat_first_sector, mirrors) = if mirroring_enabled {
(bpb.reserved_sectors as u32, bpb.fats) (bpb.reserved_sectors(), bpb.fats)
} else { } else {
let active_fat = bpb.active_fat() as u32; let active_fat = bpb.active_fat() as u32;
let fat_first_sector = (bpb.reserved_sectors as u32) + active_fat * sectors_per_fat; let fat_first_sector = (bpb.reserved_sectors()) + active_fat * sectors_per_fat;
(fat_first_sector, 1) (fat_first_sector, 1)
}; };
DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, bpb, io) DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, bpb, io)
@ -652,10 +660,9 @@ impl<T> DiskSlice<T> {
} }
fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, bpb: &BiosParameterBlock, inner: T) -> Self { fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, bpb: &BiosParameterBlock, inner: T) -> Self {
let bytes_per_sector = bpb.bytes_per_sector as u64;
Self::new( Self::new(
first_sector as u64 * bytes_per_sector, bpb.bytes_from_sectors(first_sector),
sector_count as u64 * bytes_per_sector, bpb.bytes_from_sectors(sector_count),
mirrors, mirrors,
inner, inner,
) )
@ -761,20 +768,20 @@ impl OemCpConverter for LossyOemCpConverter {
pub(crate) static LOSSY_OEM_CP_CONVERTER: LossyOemCpConverter = LossyOemCpConverter { _dummy: () }; pub(crate) static LOSSY_OEM_CP_CONVERTER: LossyOemCpConverter = LossyOemCpConverter { _dummy: () };
fn write_zeros<T: ReadWriteSeek>(mut disk: T, mut len: usize) -> io::Result<()> { pub(crate) fn write_zeros<T: ReadWriteSeek>(mut disk: T, mut len: u64) -> io::Result<()> {
const ZEROS: [u8; 512] = [0u8; 512]; const ZEROS: [u8; 512] = [0u8; 512];
while len > 0 { while len > 0 {
let write_size = cmp::min(len, ZEROS.len()); let write_size = cmp::min(len, ZEROS.len() as u64) as usize;
disk.write_all(&ZEROS[..write_size])?; disk.write_all(&ZEROS[..write_size])?;
len -= write_size; len -= write_size as u64;
} }
Ok(()) Ok(())
} }
fn write_zeros_until_end_of_sector<T: ReadWriteSeek>(mut disk: T, bytes_per_sector: u16) -> io::Result<()> { fn write_zeros_until_end_of_sector<T: ReadWriteSeek>(mut disk: T, bytes_per_sector: u16) -> io::Result<()> {
let pos = disk.seek(SeekFrom::Current(0))?; let pos = disk.seek(SeekFrom::Current(0))?;
let total_bytes_to_write = bytes_per_sector as usize - (pos % bytes_per_sector as u64) as usize; let total_bytes_to_write = bytes_per_sector as u64 - (pos % bytes_per_sector as u64);
if total_bytes_to_write != bytes_per_sector as usize { if total_bytes_to_write != bytes_per_sector as u64 {
write_zeros(disk, total_bytes_to_write)?; write_zeros(disk, total_bytes_to_write)?;
} }
Ok(()) Ok(())
@ -905,46 +912,47 @@ pub fn format_volume<T: ReadWriteSeek>(mut disk: T, options: FormatVolumeOptions
next_free_cluster: None, next_free_cluster: None,
dirty: false, dirty: false,
}; };
disk.seek(SeekFrom::Start(boot.bpb.fs_info_sector as u64 * bytes_per_sector as u64))?; disk.seek(SeekFrom::Start(boot.bpb.bytes_from_sectors(boot.bpb.fs_info_sector())))?;
fs_info_sector.serialize(&mut disk)?; fs_info_sector.serialize(&mut disk)?;
write_zeros_until_end_of_sector(&mut disk, bytes_per_sector)?; write_zeros_until_end_of_sector(&mut disk, bytes_per_sector)?;
// backup boot sector // backup boot sector
disk.seek(SeekFrom::Start(boot.bpb.backup_boot_sector as u64 * bytes_per_sector as u64))?; disk.seek(SeekFrom::Start(boot.bpb.bytes_from_sectors(boot.bpb.backup_boot_sector())))?;
boot.serialize(&mut disk)?; boot.serialize(&mut disk)?;
write_zeros_until_end_of_sector(&mut disk, bytes_per_sector)?; write_zeros_until_end_of_sector(&mut disk, bytes_per_sector)?;
} }
// format File Allocation Table // format File Allocation Table
let sectors_per_fat: u32 = boot.bpb.sectors_per_fat(); let reserved_sectors = boot.bpb.reserved_sectors();
let bytes_per_fat: u32 = sectors_per_fat * bytes_per_sector as u32; let fat_pos = boot.bpb.bytes_from_sectors(reserved_sectors);
let reserved_sectors = boot.bpb.reserved_sectors; let sectors_per_all_fats = boot.bpb.sectors_per_all_fats();
let fat_pos = reserved_sectors as u64 * bytes_per_sector as u64;
disk.seek(SeekFrom::Start(fat_pos))?; disk.seek(SeekFrom::Start(fat_pos))?;
write_zeros(&mut disk, bytes_per_fat as usize * boot.bpb.fats as usize)?; write_zeros(&mut disk, boot.bpb.bytes_from_sectors(sectors_per_all_fats))?;
{ {
let mut fat_slice = fat_slice(&mut disk, &boot.bpb); let mut fat_slice = fat_slice(&mut disk, &boot.bpb);
let sectors_per_fat = boot.bpb.sectors_per_fat();
let bytes_per_fat = boot.bpb.bytes_from_sectors(sectors_per_fat);
format_fat(&mut fat_slice, fat_type, boot.bpb.media, bytes_per_fat, boot.bpb.total_clusters())?; format_fat(&mut fat_slice, fat_type, boot.bpb.media, bytes_per_fat, boot.bpb.total_clusters())?;
} }
// init root directory - zero root directory region for FAT12/16 and alloc first root directory cluster for FAT32 // init root directory - zero root directory region for FAT12/16 and alloc first root directory cluster for FAT32
let root_dir_pos = fat_pos + bytes_per_fat as u64 * boot.bpb.fats as u64; let root_dir_first_sector = reserved_sectors + sectors_per_all_fats;
let root_dir_sectors = boot.bpb.root_dir_sectors();
let root_dir_pos = boot.bpb.bytes_from_sectors(root_dir_first_sector);
disk.seek(SeekFrom::Start(root_dir_pos))?; disk.seek(SeekFrom::Start(root_dir_pos))?;
let root_dir_sectors: u32 = boot.bpb.root_dir_sectors(); write_zeros(&mut disk, boot.bpb.bytes_from_sectors(root_dir_sectors))?;
write_zeros(&mut disk, root_dir_sectors as usize * bytes_per_sector as usize)?;
if fat_type == FatType::Fat32 { if fat_type == FatType::Fat32 {
let root_dir_first_cluster = { let root_dir_first_cluster = {
let mut fat_slice = fat_slice(&mut disk, &boot.bpb); let mut fat_slice = fat_slice(&mut disk, &boot.bpb);
alloc_cluster(&mut fat_slice, fat_type, None, None, 1)? alloc_cluster(&mut fat_slice, fat_type, None, None, 1)?
}; };
assert!(root_dir_first_cluster == boot.bpb.root_dir_first_cluster); assert!(root_dir_first_cluster == boot.bpb.root_dir_first_cluster);
let first_data_sector = reserved_sectors as u32 + boot.bpb.sectors_per_all_fats() + root_dir_sectors; let first_data_sector = reserved_sectors + sectors_per_all_fats + root_dir_sectors;
let sectors_per_cluster = boot.bpb.sectors_per_cluster; let root_dir_first_sector = first_data_sector
let root_dir_first_sector = + boot.bpb.sectors_from_clusters(root_dir_first_cluster - RESERVED_FAT_ENTRIES);
((root_dir_first_cluster - RESERVED_FAT_ENTRIES) * sectors_per_cluster as u32) + first_data_sector; let root_dir_pos = boot.bpb.bytes_from_sectors(root_dir_first_sector);
let root_dir_pos = root_dir_first_sector as u64 * bytes_per_sector as u64;
disk.seek(SeekFrom::Start(root_dir_pos))?; disk.seek(SeekFrom::Start(root_dir_pos))?;
write_zeros(&mut disk, sectors_per_cluster as usize * bytes_per_sector as usize)?; write_zeros(&mut disk, boot.bpb.cluster_size() as u64)?;
} }
// TODO: create volume label dir entry if volume label is set // TODO: create volume label dir entry if volume label is set

View File

@ -120,7 +120,7 @@ pub(crate) fn count_free_clusters<T: ReadSeek>(fat: &mut T, fat_type: FatType, t
} }
} }
pub(crate) fn format_fat<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, media: u8, bytes_per_fat: u32, total_clusters: u32) -> io::Result<()> { pub(crate) fn format_fat<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, media: u8, bytes_per_fat: u64, total_clusters: u32) -> io::Result<()> {
// init first two reserved entries to FAT ID // init first two reserved entries to FAT ID
match fat_type { match fat_type {
FatType::Fat12 => { FatType::Fat12 => {
@ -137,9 +137,9 @@ pub(crate) fn format_fat<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, media
}, },
}; };
// mark entries at the end of FAT as used (after FAT but before sector end) // mark entries at the end of FAT as used (after FAT but before sector end)
const BITS_PER_BYTE: u32 = 8; const BITS_PER_BYTE: u64 = 8;
let start_cluster = total_clusters + RESERVED_FAT_ENTRIES; let start_cluster = total_clusters + RESERVED_FAT_ENTRIES;
let end_cluster = bytes_per_fat * BITS_PER_BYTE / fat_type.bits_per_fat_entry(); let end_cluster = (bytes_per_fat * BITS_PER_BYTE / fat_type.bits_per_fat_entry() as u64) as u32;
for cluster in start_cluster..end_cluster { for cluster in start_cluster..end_cluster {
write_fat(fat, fat_type, cluster, FatValue::EndOfChain)?; write_fat(fat, fat_type, cluster, FatValue::EndOfChain)?;
} }