More code refactoring.

This commit is contained in:
Rafał Harabień 2017-11-08 19:59:03 +01:00
parent 681ee56cb3
commit 5c0ad0ce18
3 changed files with 130 additions and 113 deletions

View File

@ -7,7 +7,8 @@ use std::iter;
use fs::{FileSystemRef, DiskSlice}; use fs::{FileSystemRef, DiskSlice};
use file::File; use file::File;
use dir_entry::*; use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes,
DIR_ENTRY_SIZE, LFN_PART_LEN, LFN_ENTRY_LAST_FLAG};
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum DirRawStream<'a, 'b: 'a> { pub(crate) enum DirRawStream<'a, 'b: 'a> {

View File

@ -74,7 +74,7 @@ impl <'a, 'b> File<'a, 'b> {
// Note: when between clusters it returns position after previous cluster // Note: when between clusters it returns position after previous cluster
match self.current_cluster { match self.current_cluster {
Some(n) => { Some(n) => {
let cluster_size = self.fs.get_cluster_size(); let cluster_size = self.fs.cluster_size();
let offset_in_cluster = self.offset % cluster_size; let offset_in_cluster = self.offset % cluster_size;
let offset_in_fs = self.fs.offset_from_cluster(n) + (offset_in_cluster as u64); let offset_in_fs = self.fs.offset_from_cluster(n) + (offset_in_cluster as u64);
Some(offset_in_fs) Some(offset_in_fs)
@ -152,7 +152,7 @@ impl<'a, 'b> Drop for File<'a, 'b> {
impl<'a, 'b> Read for File<'a, 'b> { impl<'a, 'b> Read for File<'a, 'b> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let cluster_size = self.fs.get_cluster_size(); let cluster_size = self.fs.cluster_size();
let current_cluster_opt = if self.offset % cluster_size == 0 { let current_cluster_opt = if self.offset % cluster_size == 0 {
// next cluster // next cluster
match self.current_cluster { match self.current_cluster {
@ -203,7 +203,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
impl<'a, 'b> Write for File<'a, 'b> { impl<'a, 'b> Write for File<'a, 'b> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let cluster_size = self.fs.get_cluster_size(); let cluster_size = self.fs.cluster_size();
let offset_in_cluster = self.offset % cluster_size; let offset_in_cluster = self.offset % cluster_size;
let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize; let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
let write_size = cmp::min(buf.len(), bytes_left_in_cluster); let write_size = cmp::min(buf.len(), bytes_left_in_cluster);
@ -304,7 +304,7 @@ impl<'a, 'b> Seek for File<'a, 'b> {
if new_pos == self.offset as i64 { if new_pos == self.offset as i64 {
return Ok(self.offset as u64); return Ok(self.offset as u64);
} }
let cluster_size = self.fs.get_cluster_size(); 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 = ((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 old_cluster_count = ((self.offset as i64 + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;

232
src/fs.rs
View File

@ -7,6 +7,7 @@ use byteorder::{LittleEndian, ReadBytesExt};
use file::File; use file::File;
use dir::{DirRawStream, Dir}; use dir::{DirRawStream, Dir};
use dir_entry::DIR_ENTRY_SIZE;
use table::{ClusterIterator, alloc_cluster}; use table::{ClusterIterator, alloc_cluster};
// FAT implementation based on: // FAT implementation based on:
@ -18,6 +19,18 @@ pub enum FatType {
Fat12, Fat16, Fat32, Fat12, Fat16, Fat32,
} }
impl FatType {
fn from_clusters(total_clusters: u32) -> FatType {
if total_clusters < 4085 {
FatType::Fat12
} else if total_clusters < 65525 {
FatType::Fat16
} else {
FatType::Fat32
}
}
}
pub trait ReadSeek: Read + Seek {} pub trait ReadSeek: Read + Seek {}
impl<T> ReadSeek for T where T: Read + Seek {} impl<T> ReadSeek for T where T: Read + Seek {}
@ -56,102 +69,8 @@ struct BiosParameterBlock {
fs_type_label: [u8; 8], fs_type_label: [u8; 8],
} }
#[allow(dead_code)] impl BiosParameterBlock {
struct BootRecord { fn deserialize(rdr: &mut Read) -> io::Result<BiosParameterBlock> {
bootjmp: [u8; 3],
oem_name: [u8; 8],
bpb: BiosParameterBlock,
boot_code: [u8; 448],
boot_sig: [u8; 2],
}
impl Default for BootRecord {
fn default() -> BootRecord {
BootRecord {
bootjmp: Default::default(),
oem_name: Default::default(),
bpb: Default::default(),
boot_code: [0; 448],
boot_sig: Default::default(),
}
}
}
pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
/// FAT filesystem main struct.
pub struct FileSystem<'a> {
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
pub(crate) read_only: bool,
fat_type: FatType,
bpb: BiosParameterBlock,
first_data_sector: u32,
root_dir_sectors: u32,
}
impl <'a> FileSystem<'a> {
/// Creates new filesystem object instance.
///
/// read_only argument is a hint for library. It doesnt prevent user from writing to file.
/// For now it prevents accessed date field automatic update in dir entry.
///
/// Note: creating multiple filesystem objects with one underlying device/disk image can
/// cause filesystem corruption.
pub fn new<T: ReadWriteSeek>(disk: &'a mut T, read_only: bool) -> io::Result<FileSystem<'a>> {
let boot = Self::read_boot_record(disk)?;
if boot.boot_sig != [0x55, 0xAA] {
return Err(Error::new(ErrorKind::Other, "invalid signature"));
}
let total_sectors = if boot.bpb.total_sectors_16 == 0 { boot.bpb.total_sectors_32 } else { boot.bpb.total_sectors_16 as u32 };
let sectors_per_fat = if boot.bpb.sectors_per_fat_16 == 0 { boot.bpb.sectors_per_fat_32 } else { boot.bpb.sectors_per_fat_16 as u32 };
let root_dir_sectors = (((boot.bpb.root_entries * 32) + (boot.bpb.bytes_per_sector - 1)) / boot.bpb.bytes_per_sector) as u32;
let first_data_sector = boot.bpb.reserved_sectors as u32 + (boot.bpb.fats as u32 * sectors_per_fat) + root_dir_sectors;
let data_sectors = total_sectors - (boot.bpb.reserved_sectors as u32 + (boot.bpb.fats as u32 * sectors_per_fat) + root_dir_sectors as u32);
let total_clusters = data_sectors / boot.bpb.sectors_per_cluster as u32;
let fat_type = Self::fat_type_from_clusters(total_clusters);
Ok(FileSystem {
disk: RefCell::new(disk),
read_only,
fat_type,
bpb: boot.bpb,
first_data_sector,
root_dir_sectors,
})
}
/// Returns type of used File Allocation Table (FAT).
pub fn fat_type(&self) -> FatType {
self.fat_type
}
/// Returns volume identifier read from BPB in Boot Sector.
pub fn volume_id(&self) -> u32 {
self.bpb.volume_id
}
/// Returns volume label from BPB in Boot Sector.
///
/// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
/// Only label from BPB is used.
pub fn volume_label(&self) -> String {
String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
}
/// Returns root directory object allowing futher penetration of filesystem structure.
pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
let root_rdr = {
match self.fat_type {
FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
self.first_data_sector - self.root_dir_sectors, self.root_dir_sectors, 1, self)),
_ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)),
}
};
Dir::new(root_rdr, self)
}
fn read_bpb(rdr: &mut Read) -> io::Result<BiosParameterBlock> {
let mut bpb: BiosParameterBlock = Default::default(); let mut bpb: BiosParameterBlock = Default::default();
bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?; bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?;
bpb.sectors_per_cluster = rdr.read_u8()?; bpb.sectors_per_cluster = rdr.read_u8()?;
@ -204,22 +123,23 @@ impl <'a> FileSystem<'a> {
} }
Ok(bpb) Ok(bpb)
} }
fn fat_type_from_clusters(total_clusters: u32) -> FatType {
if total_clusters < 4085 {
FatType::Fat12
} else if total_clusters < 65525 {
FatType::Fat16
} else {
FatType::Fat32
}
} }
fn read_boot_record(rdr: &mut Read) -> io::Result<BootRecord> { #[allow(dead_code)]
struct BootRecord {
bootjmp: [u8; 3],
oem_name: [u8; 8],
bpb: BiosParameterBlock,
boot_code: [u8; 448],
boot_sig: [u8; 2],
}
impl BootRecord {
fn deserialize(rdr: &mut Read) -> io::Result<BootRecord> {
let mut boot: BootRecord = Default::default(); let mut boot: BootRecord = 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 = Self::read_bpb(rdr)?; boot.bpb = BiosParameterBlock::deserialize(rdr)?;
if boot.bpb.sectors_per_fat_16 == 0 { if boot.bpb.sectors_per_fat_16 == 0 {
rdr.read_exact(&mut boot.boot_code[0..420])?; rdr.read_exact(&mut boot.boot_code[0..420])?;
@ -229,6 +149,102 @@ impl <'a> FileSystem<'a> {
rdr.read_exact(&mut boot.boot_sig)?; rdr.read_exact(&mut boot.boot_sig)?;
Ok(boot) Ok(boot)
} }
}
impl Default for BootRecord {
fn default() -> BootRecord {
BootRecord {
bootjmp: Default::default(),
oem_name: Default::default(),
bpb: Default::default(),
boot_code: [0; 448],
boot_sig: Default::default(),
}
}
}
pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
/// FAT filesystem main struct.
pub struct FileSystem<'a> {
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
pub(crate) read_only: bool,
fat_type: FatType,
bpb: BiosParameterBlock,
first_data_sector: u32,
root_dir_sectors: u32,
}
impl <'a> FileSystem<'a> {
/// Creates new filesystem object instance.
///
/// read_only argument is a hint for library. It doesnt prevent user from writing to file.
/// For now it prevents accessed date field automatic update in dir entry.
///
/// Note: creating multiple filesystem objects with one underlying device/disk image can
/// cause filesystem corruption.
pub fn new<T: ReadWriteSeek>(disk: &'a mut T, read_only: bool) -> io::Result<FileSystem<'a>> {
let bpb = {
let boot = BootRecord::deserialize(disk)?;
if boot.boot_sig != [0x55, 0xAA] {
return Err(Error::new(ErrorKind::Other, "invalid signature"));
}
boot.bpb
};
let total_sectors =
if bpb.total_sectors_16 == 0 { bpb.total_sectors_32 }
else { bpb.total_sectors_16 as u32 };
let sectors_per_fat =
if bpb.sectors_per_fat_16 == 0 { bpb.sectors_per_fat_32 }
else { bpb.sectors_per_fat_16 as u32 };
let root_dir_bytes = bpb.root_entries as u32 * DIR_ENTRY_SIZE as u32;
let root_dir_sectors = (root_dir_bytes + (bpb.bytes_per_sector as u32 - 1)) / bpb.bytes_per_sector as u32;
let first_data_sector = bpb.reserved_sectors as u32 + (bpb.fats as u32 * sectors_per_fat) + root_dir_sectors;
let fat_sectors = bpb.fats as u32 * sectors_per_fat;
let data_sectors = total_sectors - (bpb.reserved_sectors as u32 + fat_sectors + root_dir_sectors as u32);
let total_clusters = data_sectors / bpb.sectors_per_cluster as u32;
let fat_type = FatType::from_clusters(total_clusters);
Ok(FileSystem {
disk: RefCell::new(disk),
read_only,
fat_type,
bpb: bpb,
first_data_sector,
root_dir_sectors,
})
}
/// Returns type of used File Allocation Table (FAT).
pub fn fat_type(&self) -> FatType {
self.fat_type
}
/// Returns volume identifier read from BPB in Boot Sector.
pub fn volume_id(&self) -> u32 {
self.bpb.volume_id
}
/// Returns volume label from BPB in Boot Sector.
///
/// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
/// Only label from BPB is used.
pub fn volume_label(&self) -> String {
String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
}
/// Returns root directory object allowing futher penetration of filesystem structure.
pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
let root_rdr = {
match self.fat_type {
FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
self.first_data_sector - self.root_dir_sectors, self.root_dir_sectors, 1, self)),
_ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)),
}
};
Dir::new(root_rdr, self)
}
pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 { pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 {
(sector as u64) * self.bpb.bytes_per_sector as u64 (sector as u64) * self.bpb.bytes_per_sector as u64
@ -238,7 +254,7 @@ impl <'a> FileSystem<'a> {
((cluster - 2) * self.bpb.sectors_per_cluster as u32) + self.first_data_sector ((cluster - 2) * self.bpb.sectors_per_cluster as u32) + self.first_data_sector
} }
pub(crate) fn get_cluster_size(&self) -> u32 { pub(crate) fn cluster_size(&self) -> u32 {
self.bpb.sectors_per_cluster as u32 * self.bpb.bytes_per_sector as u32 self.bpb.sectors_per_cluster as u32 * self.bpb.bytes_per_sector as u32
} }