Refactor code to use shared state.
This way for example FatFile can implement Read trait.
This commit is contained in:
parent
3933a29078
commit
88fd442dac
@ -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());
|
||||
}
|
||||
|
67
src/dir.rs
67
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<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,23 +119,24 @@ impl<T: Read+Seek> FatFileSystem<T> {
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_dir_entry(rdr: &mut Read) -> io::Result<FatDirEntry> {
|
||||
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::<LittleEndian>()?,
|
||||
create_date: rdr.read_u16::<LittleEndian>()?,
|
||||
access_date: rdr.read_u16::<LittleEndian>()?,
|
||||
first_cluster_hi: rdr.read_u16::<LittleEndian>()?,
|
||||
modify_time: rdr.read_u16::<LittleEndian>()?,
|
||||
modify_date: rdr.read_u16::<LittleEndian>()?,
|
||||
first_cluster_lo: rdr.read_u16::<LittleEndian>()?,
|
||||
size: rdr.read_u32::<LittleEndian>()?,
|
||||
})
|
||||
|
||||
fn read_dir_entry(&self, rdr: &mut Read) -> io::Result<FatDirEntry> {
|
||||
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::<LittleEndian>()?,
|
||||
create_date: rdr.read_u16::<LittleEndian>()?,
|
||||
access_date: rdr.read_u16::<LittleEndian>()?,
|
||||
first_cluster_hi: rdr.read_u16::<LittleEndian>()?,
|
||||
modify_time: rdr.read_u16::<LittleEndian>()?,
|
||||
modify_date: rdr.read_u16::<LittleEndian>()?,
|
||||
first_cluster_lo: rdr.read_u16::<LittleEndian>()?,
|
||||
size: rdr.read_u32::<LittleEndian>()?,
|
||||
state: self.state.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
42
src/file.rs
42
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<T: Read+Seek> FatFileSystem<T> {
|
||||
|
||||
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<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());
|
||||
// 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<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, (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)
|
||||
}
|
||||
}
|
||||
|
167
src/fs.rs
167
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<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) -> 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<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())
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user