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 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)]
pub(crate) struct BiosParameterBlock {
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 {
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
@ -300,7 +307,7 @@ impl BiosParameterBlock {
pub(crate) fn first_data_sector(&self) -> u32 {
let root_dir_sectors = self.root_dir_sectors();
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 {
@ -309,10 +316,36 @@ impl BiosParameterBlock {
let data_sectors = total_sectors - first_data_sector;
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) struct BootRecord {
pub(crate) fn sectors_from_clusters(&self, clusters: u32) -> u32 {
// 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],
oem_name: [u8; 8],
pub(crate) bpb: BiosParameterBlock,
@ -320,9 +353,9 @@ pub(crate) struct BootRecord {
boot_sig: [u8; 2],
}
impl BootRecord {
pub(crate) fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BootRecord> {
let mut boot: BootRecord = Default::default();
impl BootSector {
pub(crate) fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BootSector> {
let mut boot: BootSector = Default::default();
rdr.read_exact(&mut boot.bootjmp)?;
rdr.read_exact(&mut boot.oem_name)?;
boot.bpb = BiosParameterBlock::deserialize(rdr)?;
@ -362,9 +395,9 @@ impl BootRecord {
}
}
impl Default for BootRecord {
fn default() -> BootRecord {
BootRecord {
impl Default for BootSector {
fn default() -> BootSector {
BootSector {
bootjmp: Default::default(),
oem_name: 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 {
if total_bytes < 4 * MB {
FatType::Fat12
@ -521,8 +550,8 @@ fn format_bpb(options: &FormatVolumeOptions) -> io::Result<(BiosParameterBlock,
Ok((bpb, fat_type))
}
pub(crate) fn format_boot_sector(options: &FormatVolumeOptions) -> io::Result<(BootRecord, FatType)> {
let mut boot: BootRecord = Default::default();
pub(crate) fn format_boot_sector(options: &FormatVolumeOptions) -> io::Result<(BootSector, FatType)> {
let mut boot: BootSector = Default::default();
let (bpb, fat_type) = format_bpb(options)?;
boot.bpb = bpb;
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;
}
}
let mut new_pos = new_pos as u32;
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
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)
let cluster_count = ((new_pos + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;
let old_cluster_count = ((self.offset as i64 + 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.fs.clusters_from_bytes(self.offset as u64) as i32 - 1) as isize;
let new_cluster = if new_pos == 0 {
None
} else if cluster_count == old_cluster_count {
@ -342,7 +342,7 @@ impl<'a, T: ReadWriteSeek> Seek for File<'a, T> {
Some(r) => r?,
None => {
// 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;
},
};

View File

@ -12,7 +12,7 @@ use io::{Error, ErrorKind, SeekFrom};
use byteorder::LittleEndian;
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 file::File;
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
let bpb = {
let boot = BootRecord::deserialize(&mut disk)?;
let boot = BootSector::deserialize(&mut disk)?;
boot.validate()?;
boot.bpb
};
@ -312,7 +312,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
// read FSInfo sector if this is 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)?
} else {
FsInfoSector::default()
@ -429,21 +429,29 @@ impl<T: ReadWriteSeek> FileSystem<T> {
}
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 {
((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 {
self.bpb.sectors_per_cluster as u32 * self.bpb.bytes_per_sector as u32
pub fn cluster_size(&self) -> u32 {
self.bpb.cluster_size()
}
pub(crate) fn offset_from_cluster(&self, cluser: u32) -> u64 {
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>> {
let io = FsIoAdapter {
fs: self,
@ -482,7 +490,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
if zero {
let mut disk = self.disk.borrow_mut();
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();
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 mirroring_enabled = bpb.mirroring_enabled();
let (fat_first_sector, mirrors) = if mirroring_enabled {
(bpb.reserved_sectors as u32, bpb.fats)
(bpb.reserved_sectors(), bpb.fats)
} else {
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)
};
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 {
let bytes_per_sector = bpb.bytes_per_sector as u64;
Self::new(
first_sector as u64 * bytes_per_sector,
sector_count as u64 * bytes_per_sector,
bpb.bytes_from_sectors(first_sector),
bpb.bytes_from_sectors(sector_count),
mirrors,
inner,
)
@ -761,20 +768,20 @@ impl OemCpConverter for LossyOemCpConverter {
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];
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])?;
len -= write_size;
len -= write_size as u64;
}
Ok(())
}
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 total_bytes_to_write = bytes_per_sector as usize - (pos % bytes_per_sector as u64) as usize;
if total_bytes_to_write != bytes_per_sector 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 u64 {
write_zeros(disk, total_bytes_to_write)?;
}
Ok(())
@ -905,46 +912,47 @@ pub fn format_volume<T: ReadWriteSeek>(mut disk: T, options: FormatVolumeOptions
next_free_cluster: None,
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)?;
write_zeros_until_end_of_sector(&mut disk, bytes_per_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)?;
write_zeros_until_end_of_sector(&mut disk, bytes_per_sector)?;
}
// format File Allocation Table
let sectors_per_fat: u32 = boot.bpb.sectors_per_fat();
let bytes_per_fat: u32 = sectors_per_fat * bytes_per_sector as u32;
let reserved_sectors = boot.bpb.reserved_sectors;
let fat_pos = reserved_sectors as u64 * bytes_per_sector as u64;
let reserved_sectors = boot.bpb.reserved_sectors();
let fat_pos = boot.bpb.bytes_from_sectors(reserved_sectors);
let sectors_per_all_fats = boot.bpb.sectors_per_all_fats();
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 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())?;
}
// 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))?;
let root_dir_sectors: u32 = boot.bpb.root_dir_sectors();
write_zeros(&mut disk, root_dir_sectors as usize * bytes_per_sector as usize)?;
write_zeros(&mut disk, boot.bpb.bytes_from_sectors(root_dir_sectors))?;
if fat_type == FatType::Fat32 {
let root_dir_first_cluster = {
let mut fat_slice = fat_slice(&mut disk, &boot.bpb);
alloc_cluster(&mut fat_slice, fat_type, None, None, 1)?
};
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 sectors_per_cluster = boot.bpb.sectors_per_cluster;
let root_dir_first_sector =
((root_dir_first_cluster - RESERVED_FAT_ENTRIES) * sectors_per_cluster as u32) + first_data_sector;
let root_dir_pos = root_dir_first_sector as u64 * bytes_per_sector as u64;
let first_data_sector = reserved_sectors + sectors_per_all_fats + root_dir_sectors;
let root_dir_first_sector = first_data_sector
+ boot.bpb.sectors_from_clusters(root_dir_first_cluster - RESERVED_FAT_ENTRIES);
let root_dir_pos = boot.bpb.bytes_from_sectors(root_dir_first_sector);
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

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
match fat_type {
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)
const BITS_PER_BYTE: u32 = 8;
const BITS_PER_BYTE: u64 = 8;
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 {
write_fat(fat, fat_type, cluster, FatValue::EndOfChain)?;
}