forked from M-Labs/rust-fatfs
More code refactoring.
This commit is contained in:
parent
681ee56cb3
commit
5c0ad0ce18
@ -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> {
|
||||||
|
@ -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
232
src/fs.rs
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user