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() { fn main() {
let file = File::open("resources/floppy.img").unwrap(); let file = File::open("resources/floppy.img").unwrap();
let mut buf_rdr = BufReader::new(file); let buf_rdr = BufReader::new(file);
let mut fs = FatFileSystem::new(&mut buf_rdr).unwrap(); let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap();
let mut root_dir = fs.root_dir(); 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 { for e in entries {
println!("{} - size {} - modified {}", e.get_name(), e.get_size(), e.get_modify_time()); 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 byteorder::{LittleEndian, ReadBytesExt};
use chrono::{DateTime, Date, TimeZone, Local}; use chrono::{DateTime, Date, TimeZone, Local};
use fs::FatFileSystem; use fs::FatSharedStateRef;
use file::FatFile; use file::FatFile;
bitflags! { bitflags! {
@ -35,6 +35,7 @@ pub struct FatDirEntry {
modify_date: u16, modify_date: u16,
first_cluster_lo: u16, first_cluster_lo: u16,
size: u32, size: u32,
state: FatSharedStateRef,
} }
fn convert_date(dos_date: u16) -> Date<Local> { 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 ((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 { pub fn get_file(&self) -> FatFile {
FatFile::new(fs.sector_from_cluster(self.get_cluster()), self.size) FatFile::new(self.get_cluster(), self.size, self.state.clone())
} }
pub fn get_size(&self) -> u32 { pub fn get_size(&self) -> u32 {
@ -82,19 +83,30 @@ impl FatDirEntry {
} }
} }
impl<T: Read+Seek> FatFileSystem<T> { pub struct FatDir {
pub fn read_dir(&mut self, dir: &mut FatFile) -> io::Result<Vec<FatDirEntry>> { 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 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 { loop {
let size = self.read(dir, &mut buf)?; let size = self.rdr.read(&mut buf)?;
if size == 0 { if size == 0 {
break; break;
} }
let mut cur = Cursor::new(&buf[..size]); let mut cur = Cursor::new(&buf[..size]);
loop { loop {
let entry = read_dir_entry(&mut cur)?; let entry = self.read_dir_entry(&mut cur)?;
if entry.name[0] == 0 { if entry.name[0] == 0 {
break; // end of dir break; // end of dir
} }
@ -107,23 +119,24 @@ impl<T: Read+Seek> FatFileSystem<T> {
Ok(entries) 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]; let mut name = [0; 11];
rdr.read(&mut name)?; rdr.read(&mut name)?;
Ok(FatDirEntry { Ok(FatDirEntry {
name: name, name: name,
attrs: FatFileAttributes::from_bits(rdr.read_u8()?).unwrap(), attrs: FatFileAttributes::from_bits(rdr.read_u8()?).unwrap(),
reserved_0: rdr.read_u8()?, reserved_0: rdr.read_u8()?,
create_time_0: rdr.read_u8()?, create_time_0: rdr.read_u8()?,
create_time_1: rdr.read_u16::<LittleEndian>()?, create_time_1: rdr.read_u16::<LittleEndian>()?,
create_date: rdr.read_u16::<LittleEndian>()?, create_date: rdr.read_u16::<LittleEndian>()?,
access_date: rdr.read_u16::<LittleEndian>()?, access_date: rdr.read_u16::<LittleEndian>()?,
first_cluster_hi: rdr.read_u16::<LittleEndian>()?, first_cluster_hi: rdr.read_u16::<LittleEndian>()?,
modify_time: rdr.read_u16::<LittleEndian>()?, modify_time: rdr.read_u16::<LittleEndian>()?,
modify_date: rdr.read_u16::<LittleEndian>()?, modify_date: rdr.read_u16::<LittleEndian>()?,
first_cluster_lo: rdr.read_u16::<LittleEndian>()?, first_cluster_lo: rdr.read_u16::<LittleEndian>()?,
size: rdr.read_u32::<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::SeekFrom;
use std::io; use std::io;
use fs::FatFileSystem; use fs::FatSharedStateRef;
#[allow(dead_code)] #[allow(dead_code)]
pub struct FatFile { pub struct FatFile {
first_sector: u32, first_sector: u32,
size: u32, size: u32,
offset: u32, offset: u32,
state: FatSharedStateRef,
} }
impl FatFile { impl FatFile {
pub fn new(first_sector: u32, size: u32) -> FatFile { pub(crate) fn new(first_cluster: u32, size: u32, state: FatSharedStateRef) -> FatFile {
FatFile { first_sector, size, offset: 0 } let first_sector = state.borrow().sector_from_cluster(first_cluster);
FatFile {
first_sector, size, state, offset: 0,
}
} }
} }
impl<T: Read+Seek> FatFileSystem<T> { impl Read for FatFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
pub fn file_from_cluster(&mut self, cluster: u32, size: u32) -> FatFile { let (offset, read_size) = {
FatFile { let state = self.state.borrow();
first_sector: self.sector_from_cluster(cluster), let offset = state.offset_from_sector(self.first_sector) + self.offset as u64;
size: size, let mut read_size = cmp::min((self.size - self.offset) as usize, buf.len());
offset: 0, // FIXME: allow only one cluster for now
} read_size = cmp::min(read_size, (state.get_cluster_size() - self.offset) as usize);
} (offset, read_size)
};
pub fn read(&mut self, file: &mut FatFile, buf: &mut [u8]) -> io::Result<usize> { let mut state = self.state.borrow_mut();
let offset = self.offset_from_sector(file.first_sector) + file.offset as u64; state.rdr.seek(SeekFrom::Start(offset))?;
let mut read_size = cmp::min((file.size - file.offset) as usize, buf.len()); let size = state.rdr.read(&mut buf[..read_size])?;
// FIXME: allow only one cluster for now self.offset += size as u32;
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;
Ok(size) Ok(size)
} }
} }

167
src/fs.rs
View File

@ -1,10 +1,15 @@
use std::cell::RefCell;
use std::cmp;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
use std::io::SeekFrom;
use std::io; use std::io;
use std::str; use std::str;
use std::rc::Rc;
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use file::FatFile; use file::FatFile;
use dir::FatDir;
use table::{FatTable, FatTable12, FatTable16, FatTable32}; use table::{FatTable, FatTable12, FatTable16, FatTable32};
// FAT implementation based on: // FAT implementation based on:
@ -16,15 +21,46 @@ pub enum FatType {
Fat12, Fat16, Fat32, ExFat Fat12, Fat16, Fat32, ExFat
} }
pub trait ReadSeek: Read + Seek {}
impl<T> ReadSeek for T where T: Read + Seek {}
#[allow(dead_code)] #[allow(dead_code)]
pub struct FatFileSystem<T: Read+Seek> { pub(crate) struct FatSharedState {
pub(crate) rdr: T, pub rdr: Box<ReadSeek>,
pub(crate) fat_type: FatType, pub fat_type: FatType,
pub(crate) boot: FatBootRecord, pub boot: FatBootRecord,
first_fat_sector: u32, pub first_fat_sector: u32,
pub(crate) first_data_sector: u32, pub first_data_sector: u32,
pub(crate) root_dir_sectors: u32, pub root_dir_sectors: u32,
pub(crate) table: Box<FatTable>, 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)] #[allow(dead_code)]
@ -80,21 +116,11 @@ impl Default for FatBootRecord {
} }
} }
impl<T: Read+Seek> FatFileSystem<T> { 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
}
}
pub fn new(rdr: &mut T) -> io::Result<FatFileSystem<&mut T>> { //pub fn new<T: ReadSeek + 'static>(rdr: T) -> io::Result<FatFileSystem> {
let boot = FatFileSystem::<T>::read_boot_record(rdr)?; 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] { if boot.boot_sig != [0x55, 0xAA] {
return Err(Error::new(ErrorKind::Other, "invalid signature")); 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 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 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 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(); 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 // FIXME: other versions
let table_size_bytes = table_size * boot.bpb.bytes_per_sector as u32; let table_size_bytes = table_size * boot.bpb.bytes_per_sector as u32;
let table: Box<FatTable> = match fat_type { let table: Box<FatTable> = match fat_type {
FatType::Fat12 => Box::new(FatTable12::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(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(rdr, table_size_bytes as usize)?), FatType::Fat32 => Box::new(FatTable32::read(&mut rdr, table_size_bytes as usize)?),
_ => panic!("TODO: exfat") _ => panic!("TODO: exfat")
}; };
let fs = FatFileSystem { let rdr_box = Box::new(rdr);
rdr: rdr, let state = FatSharedState {
rdr: rdr_box,
fat_type: fat_type, fat_type: fat_type,
boot: boot, boot: boot,
first_data_sector: first_data_sector, first_data_sector: first_data_sector,
@ -140,7 +166,9 @@ impl<T: Read+Seek> FatFileSystem<T> {
table: table, table: table,
}; };
Ok(fs) Ok(FatFileSystem {
state: Rc::new(RefCell::new(state)),
})
} }
fn read_bpb(rdr: &mut Read) -> io::Result<FatBiosParameterBlock> { fn read_bpb(rdr: &mut Read) -> io::Result<FatBiosParameterBlock> {
@ -187,11 +215,23 @@ impl<T: Read+Seek> FatFileSystem<T> {
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 if total_clusters < 268435445 {
FatType::Fat32
} else {
FatType::ExFat
}
}
fn read_boot_record(rdr: &mut Read) -> io::Result<FatBootRecord> { fn read_boot_record(rdr: &mut Read) -> io::Result<FatBootRecord> {
let mut boot: FatBootRecord = Default::default(); let mut boot: FatBootRecord = Default::default();
rdr.read(&mut boot.bootjmp)?; rdr.read(&mut boot.bootjmp)?;
rdr.read(&mut boot.oem_name)?; 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 { if boot.bpb.table_size_16 == 0 {
rdr.read_exact(&mut boot.boot_code[0..420])?; 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)) // self.offset_from_sector(self.sector_from_cluster(cluser))
// } // }
pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 { pub fn root_dir(&mut self) -> FatDir {
(sector as u64) * self.boot.bpb.bytes_per_sector as u64 let state = self.state.borrow();
} let root_rdr: Box<Read> = match state.fat_type {
FatType::Fat12 | FatType::Fat16 => Box::new(FatSlice::from_sectors(
pub(crate) fn sector_from_cluster(&self, cluster: u32) -> u32 { state.first_data_sector - state.root_dir_sectors, state.root_dir_sectors, self.state.clone())),
((cluster - 2) * self.boot.bpb.sectors_per_cluster as u32) + self.first_data_sector _ => Box::new(FatFile::new(state.boot.bpb.root_cluster, state.root_dir_sectors, self.state.clone()))
} };
FatDir::new(root_rdr, self.state.clone())
pub(crate) fn get_cluster_size(&self) -> u32 { }
self.boot.bpb.sectors_per_cluster as u32 * self.boot.bpb.bytes_per_sector as u32 }
}
struct FatSlice {
pub(crate) fn get_root_dir_sector(&self) -> u32 { begin: u64,
match self.fat_type { size: u64,
FatType::Fat12 | FatType::Fat16 => self.first_data_sector - self.root_dir_sectors, offset: u64,
_ => self.sector_from_cluster(self.boot.bpb.root_cluster) state: FatSharedStateRef,
} }
}
impl FatSlice {
pub fn root_dir(&mut self) -> FatFile { pub(crate) fn new(begin: u64, size: u64, state: FatSharedStateRef) -> FatSlice {
let first_root_dir_sector = self.get_root_dir_sector(); FatSlice { begin, size, state, offset: 0 }
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(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] #[test]
fn fat12_test() { fn fat12_test() {
let file = File::open("resources/floppy.img").unwrap(); let file = File::open("resources/floppy.img").unwrap();
let mut buf_rdr = BufReader::new(file); let buf_rdr = BufReader::new(file);
let mut fs = FatFileSystem::new(&mut buf_rdr).unwrap(); let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap();
let mut root_dir = fs.root_dir(); 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.len(), 2);
assert_eq!(entries[0].get_name(), "RAFOS"); assert_eq!(entries[0].get_name(), "RAFOS");
assert_eq!(entries[1].get_name(), "GRUB"); assert_eq!(entries[1].get_name(), "GRUB");