From 88fd442dac66dc59986b4e829567908bdb062a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= Date: Sat, 23 Sep 2017 19:42:09 +0200 Subject: [PATCH] Refactor code to use shared state. This way for example FatFile can implement Read trait. --- examples/sample.rs | 6 +- src/dir.rs | 67 +++++++++------ src/file.rs | 42 +++++----- src/fs.rs | 167 ++++++++++++++++++++++++++------------ tests/integration-test.rs | 6 +- 5 files changed, 180 insertions(+), 108 deletions(-) diff --git a/examples/sample.rs b/examples/sample.rs index 29f709e..603c48b 100644 --- a/examples/sample.rs +++ b/examples/sample.rs @@ -8,10 +8,10 @@ use rustfat::FatFileSystem; fn main() { let file = File::open("resources/floppy.img").unwrap(); - let mut buf_rdr = BufReader::new(file); - let mut fs = FatFileSystem::new(&mut buf_rdr).unwrap(); + let buf_rdr = BufReader::new(file); + let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap(); let mut root_dir = fs.root_dir(); - let entries = fs.read_dir(&mut root_dir).unwrap(); + let entries = root_dir.list().unwrap(); for e in entries { println!("{} - size {} - modified {}", e.get_name(), e.get_size(), e.get_modify_time()); } diff --git a/src/dir.rs b/src/dir.rs index 9fed0e8..fe6d8b4 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -5,7 +5,7 @@ use std::str; use byteorder::{LittleEndian, ReadBytesExt}; use chrono::{DateTime, Date, TimeZone, Local}; -use fs::FatFileSystem; +use fs::FatSharedStateRef; use file::FatFile; bitflags! { @@ -35,6 +35,7 @@ pub struct FatDirEntry { modify_date: u16, first_cluster_lo: u16, size: u32, + state: FatSharedStateRef, } fn convert_date(dos_date: u16) -> Date { @@ -61,8 +62,8 @@ impl FatDirEntry { ((self.first_cluster_hi as u32) << 16) | self.first_cluster_lo as u32 } - pub fn get_file(&self, fs: &FatFileSystem) -> FatFile { - FatFile::new(fs.sector_from_cluster(self.get_cluster()), self.size) + pub fn get_file(&self) -> FatFile { + FatFile::new(self.get_cluster(), self.size, self.state.clone()) } pub fn get_size(&self) -> u32 { @@ -82,19 +83,30 @@ impl FatDirEntry { } } -impl FatFileSystem { - pub fn read_dir(&mut self, dir: &mut FatFile) -> io::Result> { +pub struct FatDir { + rdr: Box, + state: FatSharedStateRef, +} + +impl FatDir { + + pub(crate) fn new(rdr: Box, state: FatSharedStateRef) -> FatDir { + FatDir { rdr, state } + } + + pub fn list(&mut self) -> io::Result> { let mut entries = Vec::new(); - let mut buf = vec![0; self.get_cluster_size() as usize]; + let cluster_size = self.state.borrow().get_cluster_size() as usize; + let mut buf = vec![0; cluster_size]; loop { - let size = self.read(dir, &mut buf)?; + let size = self.rdr.read(&mut buf)?; if size == 0 { break; } let mut cur = Cursor::new(&buf[..size]); loop { - let entry = read_dir_entry(&mut cur)?; + let entry = self.read_dir_entry(&mut cur)?; if entry.name[0] == 0 { break; // end of dir } @@ -107,23 +119,24 @@ impl FatFileSystem { Ok(entries) } -} - -fn read_dir_entry(rdr: &mut Read) -> io::Result { - let mut name = [0; 11]; - rdr.read(&mut name)?; - Ok(FatDirEntry { - name: name, - attrs: FatFileAttributes::from_bits(rdr.read_u8()?).unwrap(), - reserved_0: rdr.read_u8()?, - create_time_0: rdr.read_u8()?, - create_time_1: rdr.read_u16::()?, - create_date: rdr.read_u16::()?, - access_date: rdr.read_u16::()?, - first_cluster_hi: rdr.read_u16::()?, - modify_time: rdr.read_u16::()?, - modify_date: rdr.read_u16::()?, - first_cluster_lo: rdr.read_u16::()?, - size: rdr.read_u32::()?, - }) + + fn read_dir_entry(&self, rdr: &mut Read) -> io::Result { + let mut name = [0; 11]; + rdr.read(&mut name)?; + Ok(FatDirEntry { + name: name, + attrs: FatFileAttributes::from_bits(rdr.read_u8()?).unwrap(), + reserved_0: rdr.read_u8()?, + create_time_0: rdr.read_u8()?, + create_time_1: rdr.read_u16::()?, + create_date: rdr.read_u16::()?, + access_date: rdr.read_u16::()?, + first_cluster_hi: rdr.read_u16::()?, + modify_time: rdr.read_u16::()?, + modify_date: rdr.read_u16::()?, + first_cluster_lo: rdr.read_u16::()?, + size: rdr.read_u32::()?, + state: self.state.clone(), + }) + } } diff --git a/src/file.rs b/src/file.rs index f599f84..c91d6d6 100644 --- a/src/file.rs +++ b/src/file.rs @@ -3,39 +3,39 @@ use std::io::prelude::*; use std::io::SeekFrom; use std::io; -use fs::FatFileSystem; +use fs::FatSharedStateRef; #[allow(dead_code)] pub struct FatFile { first_sector: u32, size: u32, offset: u32, + state: FatSharedStateRef, } impl FatFile { - pub fn new(first_sector: u32, size: u32) -> FatFile { - FatFile { first_sector, size, offset: 0 } + pub(crate) fn new(first_cluster: u32, size: u32, state: FatSharedStateRef) -> FatFile { + let first_sector = state.borrow().sector_from_cluster(first_cluster); + FatFile { + first_sector, size, state, offset: 0, + } } } -impl FatFileSystem { - - pub fn file_from_cluster(&mut self, cluster: u32, size: u32) -> FatFile { - FatFile { - first_sector: self.sector_from_cluster(cluster), - size: size, - offset: 0, - } - } - - pub fn read(&mut self, file: &mut FatFile, buf: &mut [u8]) -> io::Result { - let offset = self.offset_from_sector(file.first_sector) + file.offset as u64; - let mut read_size = cmp::min((file.size - file.offset) as usize, buf.len()); - // FIXME: allow only one cluster for now - read_size = cmp::min(read_size, (self.get_cluster_size() - file.offset) as usize); - self.rdr.seek(SeekFrom::Start(offset))?; - let size = self.rdr.read(&mut buf[..read_size])?; - file.offset += size as u32; +impl Read for FatFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (offset, read_size) = { + let state = self.state.borrow(); + let offset = state.offset_from_sector(self.first_sector) + self.offset as u64; + let mut read_size = cmp::min((self.size - self.offset) as usize, buf.len()); + // FIXME: allow only one cluster for now + read_size = cmp::min(read_size, (state.get_cluster_size() - self.offset) as usize); + (offset, read_size) + }; + let mut state = self.state.borrow_mut(); + state.rdr.seek(SeekFrom::Start(offset))?; + let size = state.rdr.read(&mut buf[..read_size])?; + self.offset += size as u32; Ok(size) } } diff --git a/src/fs.rs b/src/fs.rs index 2e5af17..d0d24fe 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,10 +1,15 @@ +use std::cell::RefCell; +use std::cmp; use std::io::prelude::*; use std::io::{Error, ErrorKind}; +use std::io::SeekFrom; use std::io; use std::str; +use std::rc::Rc; use byteorder::{LittleEndian, ReadBytesExt}; use file::FatFile; +use dir::FatDir; use table::{FatTable, FatTable12, FatTable16, FatTable32}; // FAT implementation based on: @@ -16,15 +21,46 @@ pub enum FatType { Fat12, Fat16, Fat32, ExFat } +pub trait ReadSeek: Read + Seek {} +impl ReadSeek for T where T: Read + Seek {} + #[allow(dead_code)] -pub struct FatFileSystem { - pub(crate) rdr: T, - pub(crate) fat_type: FatType, - pub(crate) boot: FatBootRecord, - first_fat_sector: u32, - pub(crate) first_data_sector: u32, - pub(crate) root_dir_sectors: u32, - pub(crate) table: Box, +pub(crate) struct FatSharedState { + pub rdr: Box, + pub fat_type: FatType, + pub boot: FatBootRecord, + pub first_fat_sector: u32, + pub first_data_sector: u32, + pub root_dir_sectors: u32, + pub table: Box, +} + +impl FatSharedState { + pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 { + (sector as u64) * self.boot.bpb.bytes_per_sector as u64 + } + + pub(crate) fn sector_from_cluster(&self, cluster: u32) -> u32 { + ((cluster - 2) * self.boot.bpb.sectors_per_cluster as u32) + self.first_data_sector + } + + pub(crate) fn get_cluster_size(&self) -> u32 { + self.boot.bpb.sectors_per_cluster as u32 * self.boot.bpb.bytes_per_sector as u32 + } + + pub(crate) fn get_root_dir_sector(&self) -> u32 { + match self.fat_type { + FatType::Fat12 | FatType::Fat16 => self.first_data_sector - self.root_dir_sectors, + _ => self.sector_from_cluster(self.boot.bpb.root_cluster) + } + } +} + +pub(crate) type FatSharedStateRef = Rc>; + +#[allow(dead_code)] +pub struct FatFileSystem { + pub(crate) state: FatSharedStateRef, } #[allow(dead_code)] @@ -80,21 +116,11 @@ impl Default for FatBootRecord { } } -impl FatFileSystem { - fn fat_type_from_clusters(total_clusters: u32) -> FatType { - if total_clusters < 4085 { - FatType::Fat12 - } else if total_clusters < 65525 { - FatType::Fat16 - } else if total_clusters < 268435445 { - FatType::Fat32 - } else { - FatType::ExFat - } - } +impl FatFileSystem { - pub fn new(rdr: &mut T) -> io::Result> { - let boot = FatFileSystem::::read_boot_record(rdr)?; + //pub fn new(rdr: T) -> io::Result { + pub fn new(mut rdr: Box) -> io::Result { + let boot = Self::read_boot_record(&mut *rdr)?; if boot.boot_sig != [0x55, 0xAA] { return Err(Error::new(ErrorKind::Other, "invalid signature")); } @@ -106,7 +132,7 @@ impl FatFileSystem { let first_fat_sector = boot.bpb.reserved_sector_count as u32; let data_sectors = total_sectors - (boot.bpb.reserved_sector_count as u32 + (boot.bpb.table_count as u32 * table_size) + root_dir_sectors as u32); let total_clusters = data_sectors / boot.bpb.sectors_per_cluster as u32; - let fat_type = FatFileSystem::::fat_type_from_clusters(total_clusters); + let fat_type = Self::fat_type_from_clusters(total_clusters); { let oem_name_str = str::from_utf8(&boot.oem_name).unwrap().trim_right(); @@ -121,17 +147,17 @@ impl FatFileSystem { } // FIXME: other versions - let table_size_bytes = table_size * boot.bpb.bytes_per_sector as u32; let table: Box = match fat_type { - FatType::Fat12 => Box::new(FatTable12::read(rdr, table_size_bytes as usize)?), - FatType::Fat16 => Box::new(FatTable16::read(rdr, table_size_bytes as usize)?), - FatType::Fat32 => Box::new(FatTable32::read(rdr, table_size_bytes as usize)?), + FatType::Fat12 => Box::new(FatTable12::read(&mut rdr, table_size_bytes as usize)?), + FatType::Fat16 => Box::new(FatTable16::read(&mut rdr, table_size_bytes as usize)?), + FatType::Fat32 => Box::new(FatTable32::read(&mut rdr, table_size_bytes as usize)?), _ => panic!("TODO: exfat") }; - let fs = FatFileSystem { - rdr: rdr, + let rdr_box = Box::new(rdr); + let state = FatSharedState { + rdr: rdr_box, fat_type: fat_type, boot: boot, first_data_sector: first_data_sector, @@ -140,7 +166,9 @@ impl FatFileSystem { table: table, }; - Ok(fs) + Ok(FatFileSystem { + state: Rc::new(RefCell::new(state)), + }) } fn read_bpb(rdr: &mut Read) -> io::Result { @@ -187,11 +215,23 @@ impl FatFileSystem { 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 if total_clusters < 268435445 { + FatType::Fat32 + } else { + FatType::ExFat + } + } + fn read_boot_record(rdr: &mut Read) -> io::Result { let mut boot: FatBootRecord = Default::default(); rdr.read(&mut boot.bootjmp)?; rdr.read(&mut boot.oem_name)?; - boot.bpb = FatFileSystem::::read_bpb(rdr)?; + boot.bpb = Self::read_bpb(rdr)?; if boot.bpb.table_size_16 == 0 { rdr.read_exact(&mut boot.boot_code[0..420])?; @@ -206,28 +246,47 @@ impl FatFileSystem { // self.offset_from_sector(self.sector_from_cluster(cluser)) // } - pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 { - (sector as u64) * self.boot.bpb.bytes_per_sector as u64 - } - - pub(crate) fn sector_from_cluster(&self, cluster: u32) -> u32 { - ((cluster - 2) * self.boot.bpb.sectors_per_cluster as u32) + self.first_data_sector - } - - pub(crate) fn get_cluster_size(&self) -> u32 { - self.boot.bpb.sectors_per_cluster as u32 * self.boot.bpb.bytes_per_sector as u32 - } - - pub(crate) fn get_root_dir_sector(&self) -> u32 { - match self.fat_type { - FatType::Fat12 | FatType::Fat16 => self.first_data_sector - self.root_dir_sectors, - _ => self.sector_from_cluster(self.boot.bpb.root_cluster) - } - } - - pub fn root_dir(&mut self) -> FatFile { - let first_root_dir_sector = self.get_root_dir_sector(); - let root_dir_size = self.root_dir_sectors * self.boot.bpb.bytes_per_sector as u32; - FatFile::new(first_root_dir_sector, root_dir_size) + pub fn root_dir(&mut self) -> FatDir { + let state = self.state.borrow(); + let root_rdr: Box = match state.fat_type { + FatType::Fat12 | FatType::Fat16 => Box::new(FatSlice::from_sectors( + state.first_data_sector - state.root_dir_sectors, state.root_dir_sectors, self.state.clone())), + _ => Box::new(FatFile::new(state.boot.bpb.root_cluster, state.root_dir_sectors, self.state.clone())) + }; + FatDir::new(root_rdr, self.state.clone()) + } +} + +struct FatSlice { + begin: u64, + size: u64, + offset: u64, + state: FatSharedStateRef, +} + +impl FatSlice { + pub(crate) fn new(begin: u64, size: u64, state: FatSharedStateRef) -> FatSlice { + FatSlice { begin, size, state, offset: 0 } + } + + pub(crate) fn from_sectors(first_sector: u32, sectors_count: u32, state: FatSharedStateRef) -> FatSlice { + let bytes_per_sector = state.borrow().boot.bpb.bytes_per_sector as u64; + Self::new(first_sector as u64 * bytes_per_sector, sectors_count as u64 * bytes_per_sector, state) + } +} + +impl Read for FatSlice { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (offset, read_size) = { + let state = self.state.borrow(); + let offset = self.begin + self.offset; + let mut read_size = cmp::min((self.size - self.offset) as usize, buf.len()); + (offset, read_size) + }; + let mut state = self.state.borrow_mut(); + state.rdr.seek(SeekFrom::Start(offset))?; + let size = state.rdr.read(&mut buf[..read_size])?; + self.offset += size as u64; + Ok(size) } } diff --git a/tests/integration-test.rs b/tests/integration-test.rs index 01a6c95..5f19e5e 100644 --- a/tests/integration-test.rs +++ b/tests/integration-test.rs @@ -9,10 +9,10 @@ use rustfat::FatFileSystem; #[test] fn fat12_test() { let file = File::open("resources/floppy.img").unwrap(); - let mut buf_rdr = BufReader::new(file); - let mut fs = FatFileSystem::new(&mut buf_rdr).unwrap(); + let buf_rdr = BufReader::new(file); + let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap(); let mut root_dir = fs.root_dir(); - let entries = fs.read_dir(&mut root_dir).unwrap(); + let entries = root_dir.list().unwrap(); assert_eq!(entries.len(), 2); assert_eq!(entries[0].get_name(), "RAFOS"); assert_eq!(entries[1].get_name(), "GRUB");