Refactor code to use shared state.

This way for example FatFile can implement Read trait.
This commit is contained in:
Rafał Harabień 2017-09-23 19:42:09 +02:00
parent 3933a29078
commit 88fd442dac
5 changed files with 180 additions and 108 deletions

View File

@ -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());
}

View File

@ -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<Local> {
@ -61,8 +62,8 @@ impl FatDirEntry {
((self.first_cluster_hi as u32) << 16) | self.first_cluster_lo as u32
}
pub fn get_file<T: Read+Seek>(&self, fs: &FatFileSystem<T>) -> 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<T: Read+Seek> FatFileSystem<T> {
pub fn read_dir(&mut self, dir: &mut FatFile) -> io::Result<Vec<FatDirEntry>> {
pub struct FatDir {
rdr: Box<Read>,
state: FatSharedStateRef,
}
impl FatDir {
pub(crate) fn new(rdr: Box<Read>, state: FatSharedStateRef) -> FatDir {
FatDir { rdr, state }
}
pub fn list(&mut self) -> io::Result<Vec<FatDirEntry>> {
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,9 +119,8 @@ impl<T: Read+Seek> FatFileSystem<T> {
Ok(entries)
}
}
fn read_dir_entry(rdr: &mut Read) -> io::Result<FatDirEntry> {
fn read_dir_entry(&self, rdr: &mut Read) -> io::Result<FatDirEntry> {
let mut name = [0; 11];
rdr.read(&mut name)?;
Ok(FatDirEntry {
@ -125,5 +136,7 @@ fn read_dir_entry(rdr: &mut Read) -> io::Result<FatDirEntry> {
modify_date: rdr.read_u16::<LittleEndian>()?,
first_cluster_lo: rdr.read_u16::<LittleEndian>()?,
size: rdr.read_u32::<LittleEndian>()?,
state: self.state.clone(),
})
}
}

View File

@ -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 }
}
}
impl<T: Read+Seek> FatFileSystem<T> {
pub fn file_from_cluster(&mut self, cluster: u32, size: u32) -> FatFile {
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: self.sector_from_cluster(cluster),
size: size,
offset: 0,
first_sector, size, state, offset: 0,
}
}
}
pub fn read(&mut self, file: &mut FatFile, buf: &mut [u8]) -> io::Result<usize> {
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());
impl Read for FatFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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, (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;
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)
}
}

161
src/fs.rs
View File

@ -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<T> ReadSeek for T where T: Read + Seek {}
#[allow(dead_code)]
pub struct FatFileSystem<T: Read+Seek> {
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<FatTable>,
pub(crate) struct FatSharedState {
pub rdr: Box<ReadSeek>,
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<FatTable>,
}
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<RefCell<FatSharedState>>;
#[allow(dead_code)]
pub struct FatFileSystem {
pub(crate) state: FatSharedStateRef,
}
#[allow(dead_code)]
@ -80,21 +116,11 @@ impl Default for FatBootRecord {
}
}
impl<T: Read+Seek> FatFileSystem<T> {
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<FatFileSystem<&mut T>> {
let boot = FatFileSystem::<T>::read_boot_record(rdr)?;
//pub fn new<T: ReadSeek + 'static>(rdr: T) -> io::Result<FatFileSystem> {
pub fn new<T: ReadSeek + 'static>(mut rdr: Box<T>) -> io::Result<FatFileSystem> {
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<T: Read+Seek> FatFileSystem<T> {
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::<T>::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<T: Read+Seek> FatFileSystem<T> {
}
// FIXME: other versions
let table_size_bytes = table_size * boot.bpb.bytes_per_sector as u32;
let table: Box<FatTable> = 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<T: Read+Seek> FatFileSystem<T> {
table: table,
};
Ok(fs)
Ok(FatFileSystem {
state: Rc::new(RefCell::new(state)),
})
}
fn read_bpb(rdr: &mut Read) -> io::Result<FatBiosParameterBlock> {
@ -187,11 +215,23 @@ impl<T: Read+Seek> FatFileSystem<T> {
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<FatBootRecord> {
let mut boot: FatBootRecord = Default::default();
rdr.read(&mut boot.bootjmp)?;
rdr.read(&mut boot.oem_name)?;
boot.bpb = FatFileSystem::<T>::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<T: Read+Seek> FatFileSystem<T> {
// 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) -> FatDir {
let state = self.state.borrow();
let root_rdr: Box<Read> = 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())
}
}
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)
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<usize> {
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)
}
}

View File

@ -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");