From 70b0a771b83f265965e427f429b4051673b14cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= Date: Sat, 8 Dec 2018 19:38:31 +0100 Subject: [PATCH] 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). --- src/boot_sector.rs | 61 +++++++++++++++++++++++++++---------- src/file.rs | 10 +++--- src/fs.rs | 76 +++++++++++++++++++++++++--------------------- src/table.rs | 6 ++-- 4 files changed, 95 insertions(+), 58 deletions(-) diff --git a/src/boot_sector.rs b/src/boot_sector.rs index ec92899..965f7a4 100644 --- a/src/boot_sector.rs +++ b/src/boot_sector.rs @@ -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 + } + + 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 + } } -#[allow(dead_code)] -pub(crate) struct BootRecord { +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(rdr: &mut T) -> io::Result { - let mut boot: BootRecord = Default::default(); +impl BootSector { + pub(crate) fn deserialize(rdr: &mut T) -> io::Result { + 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()); diff --git a/src/file.rs b/src/file.rs index d909032..065c217 100644 --- a/src/file.rs +++ b/src/file.rs @@ -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; }, }; diff --git a/src/fs.rs b/src/fs.rs index d33417a..272c920 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -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 FileSystem { // 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 FileSystem { // 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 FileSystem { } 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> { let io = FsIoAdapter { fs: self, @@ -482,7 +490,7 @@ impl FileSystem { 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(io: T, bpb: &BiosParameterBlock) -> DiskSlice 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 DiskSlice { } 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(mut disk: T, mut len: usize) -> io::Result<()> { +pub(crate) fn write_zeros(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(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(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 diff --git a/src/table.rs b/src/table.rs index 9188ed5..db8a58d 100644 --- a/src/table.rs +++ b/src/table.rs @@ -120,7 +120,7 @@ pub(crate) fn count_free_clusters(fat: &mut T, fat_type: FatType, t } } -pub(crate) fn format_fat(fat: &mut T, fat_type: FatType, media: u8, bytes_per_fat: u32, total_clusters: u32) -> io::Result<()> { +pub(crate) fn format_fat(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(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)?; }