forked from M-Labs/rust-fatfs
Alloc and free clusters for file data.
This commit is contained in:
parent
f32f1c7279
commit
d8eba51b88
18
README.md
18
README.md
@ -6,16 +6,20 @@ Rust FAT
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
FAT filesystem read-only library implemented in Rust.
|
FAT filesystem library implemented in Rust.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
* FAT12, FAT16, FAT32 filesystem versions,
|
* FAT12, FAT16, FAT32 supported,
|
||||||
* read directory,
|
* read directory entries,
|
||||||
* read file,
|
* read file contents,
|
||||||
* LFN (Long File Names).
|
* LFN (Long File Names),
|
||||||
|
* basic write support (write and truncate existing file).
|
||||||
|
|
||||||
Planned features:
|
Missing features (yet):
|
||||||
* write support,
|
* create new file/directory,
|
||||||
|
* remove file/directory,
|
||||||
|
|
||||||
|
Other planned features (Nice to Have):
|
||||||
* no_std environment support.
|
* no_std environment support.
|
||||||
|
|
||||||
License
|
License
|
||||||
|
14
src/dir.rs
14
src/dir.rs
@ -83,6 +83,12 @@ impl DirFileEntryData {
|
|||||||
if n == 0 { None } else { Some(n) }
|
if n == 0 { None } else { Some(n) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_first_cluster(&mut self, cluster: Option<u32>) {
|
||||||
|
let n = cluster.unwrap_or(0);
|
||||||
|
self.first_cluster_hi = (n >> 16) as u16;
|
||||||
|
self.first_cluster_lo = (n & 0xFFFF) as u16;
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn size(&self) -> u32 {
|
pub(crate) fn size(&self) -> u32 {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
@ -372,10 +378,8 @@ pub struct DirIter<'a, 'b: 'a> {
|
|||||||
|
|
||||||
impl <'a, 'b> DirIter<'a, 'b> {
|
impl <'a, 'b> DirIter<'a, 'b> {
|
||||||
fn read_dir_entry_data(&mut self) -> io::Result<DirEntryData> {
|
fn read_dir_entry_data(&mut self) -> io::Result<DirEntryData> {
|
||||||
println!("read_dir_entry_data");
|
|
||||||
let mut name = [0; 11];
|
let mut name = [0; 11];
|
||||||
self.rdr.read_exact(&mut name)?;
|
self.rdr.read_exact(&mut name)?;
|
||||||
println!("read_dir_entry_data {:?}", &name);
|
|
||||||
let attrs = FileAttributes::from_bits_truncate(self.rdr.read_u8()?);
|
let attrs = FileAttributes::from_bits_truncate(self.rdr.read_u8()?);
|
||||||
if attrs == FileAttributes::LFN {
|
if attrs == FileAttributes::LFN {
|
||||||
let mut data = DirLfnEntryData {
|
let mut data = DirLfnEntryData {
|
||||||
@ -389,7 +393,6 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
|||||||
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_1)?;
|
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_1)?;
|
||||||
data.reserved_0 = self.rdr.read_u16::<LittleEndian>()?;
|
data.reserved_0 = self.rdr.read_u16::<LittleEndian>()?;
|
||||||
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_2)?;
|
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_2)?;
|
||||||
println!("read_dir_entry_data end");
|
|
||||||
Ok(DirEntryData::Lfn(data))
|
Ok(DirEntryData::Lfn(data))
|
||||||
} else {
|
} else {
|
||||||
let data = DirFileEntryData {
|
let data = DirFileEntryData {
|
||||||
@ -406,12 +409,13 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
|||||||
first_cluster_lo: self.rdr.read_u16::<LittleEndian>()?,
|
first_cluster_lo: self.rdr.read_u16::<LittleEndian>()?,
|
||||||
size: self.rdr.read_u32::<LittleEndian>()?,
|
size: self.rdr.read_u32::<LittleEndian>()?,
|
||||||
};
|
};
|
||||||
println!("read_dir_entry_data end");
|
|
||||||
Ok(DirEntryData::File(data))
|
Ok(DirEntryData::File(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DIR_ENTRY_SIZE: u64 = 32;
|
||||||
|
|
||||||
impl <'a, 'b> Iterator for DirIter<'a, 'b> {
|
impl <'a, 'b> Iterator for DirIter<'a, 'b> {
|
||||||
type Item = io::Result<DirEntry<'a, 'b>>;
|
type Item = io::Result<DirEntry<'a, 'b>>;
|
||||||
|
|
||||||
@ -421,8 +425,8 @@ impl <'a, 'b> Iterator for DirIter<'a, 'b> {
|
|||||||
}
|
}
|
||||||
let mut lfn_buf = Vec::<u16>::new();
|
let mut lfn_buf = Vec::<u16>::new();
|
||||||
loop {
|
loop {
|
||||||
let entry_pos = self.rdr.global_pos();
|
|
||||||
let res = self.read_dir_entry_data();
|
let res = self.read_dir_entry_data();
|
||||||
|
let entry_pos = self.rdr.global_pos().map(|p| p - DIR_ENTRY_SIZE);
|
||||||
let data = match res {
|
let data = match res {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
121
src/file.rs
121
src/file.rs
@ -9,6 +9,7 @@ use dir::{FileEntryInfo, DateTime};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct File<'a, 'b: 'a> {
|
pub struct File<'a, 'b: 'a> {
|
||||||
first_cluster: Option<u32>,
|
first_cluster: Option<u32>,
|
||||||
|
// Note: if offset points between clusters current_cluster is the previous cluster
|
||||||
current_cluster: Option<u32>,
|
current_cluster: Option<u32>,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
entry: Option<FileEntryInfo>,
|
entry: Option<FileEntryInfo>,
|
||||||
@ -20,7 +21,7 @@ impl <'a, 'b> File<'a, 'b> {
|
|||||||
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<FileEntryInfo>, fs: FileSystemRef<'a, 'b>) -> Self {
|
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<FileEntryInfo>, fs: FileSystemRef<'a, 'b>) -> Self {
|
||||||
File {
|
File {
|
||||||
first_cluster, entry, fs,
|
first_cluster, entry, fs,
|
||||||
current_cluster: first_cluster,
|
current_cluster: None, // cluster before first one
|
||||||
offset: 0,
|
offset: 0,
|
||||||
entry_dirty: false,
|
entry_dirty: false,
|
||||||
}
|
}
|
||||||
@ -38,20 +39,32 @@ impl <'a, 'b> File<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn truncate(&mut self) {
|
pub fn truncate(&mut self) -> io::Result<()> {
|
||||||
// FIXME: free clusters?
|
|
||||||
match self.entry {
|
match self.entry {
|
||||||
Some(ref mut e) => {
|
Some(ref mut e) => {
|
||||||
if e.data.size != self.offset {
|
if e.data.size == self.offset {
|
||||||
e.data.size = self.offset;
|
return Ok(());
|
||||||
self.entry_dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.data.size = self.offset;
|
||||||
|
if self.offset == 0 {
|
||||||
|
e.data.set_first_cluster(None);
|
||||||
|
}
|
||||||
|
self.entry_dirty = true;
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
if self.offset > 0 {
|
||||||
|
self.fs.cluster_iter(self.current_cluster.unwrap()).truncate()
|
||||||
|
} else {
|
||||||
|
self.fs.cluster_iter(self.first_cluster.unwrap()).free()?;
|
||||||
|
self.first_cluster = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn global_pos(&self) -> Option<u64> {
|
pub(crate) fn global_pos(&self) -> Option<u64> {
|
||||||
|
// 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.get_cluster_size();
|
||||||
@ -93,6 +106,17 @@ impl <'a, 'b> File<'a, 'b> {
|
|||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_first_cluster(&mut self, cluster: u32) {
|
||||||
|
self.first_cluster = Some(cluster);
|
||||||
|
match self.entry {
|
||||||
|
Some(ref mut e) => {
|
||||||
|
e.data.set_first_cluster(self.first_cluster);
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
self.entry_dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Drop for File<'a, 'b> {
|
impl<'a, 'b> Drop for File<'a, 'b> {
|
||||||
@ -106,7 +130,23 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
|||||||
let mut buf_offset: usize = 0;
|
let mut buf_offset: usize = 0;
|
||||||
let cluster_size = self.fs.get_cluster_size();
|
let cluster_size = self.fs.get_cluster_size();
|
||||||
loop {
|
loop {
|
||||||
let current_cluster = match self.current_cluster {
|
let current_cluster_opt = if self.offset % cluster_size == 0 {
|
||||||
|
// next cluster
|
||||||
|
match self.current_cluster {
|
||||||
|
None => self.first_cluster,
|
||||||
|
Some(n) => {
|
||||||
|
let r = self.fs.cluster_iter(n).next();
|
||||||
|
match r {
|
||||||
|
Some(Err(err)) => return Err(err),
|
||||||
|
Some(Ok(n)) => Some(n),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.current_cluster
|
||||||
|
};
|
||||||
|
let current_cluster = match current_cluster_opt {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
@ -118,6 +158,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
|||||||
if read_size == 0 {
|
if read_size == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//println!("read c {} n {}", current_cluster, read_size);
|
||||||
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
||||||
let read_bytes = {
|
let read_bytes = {
|
||||||
let mut disk = self.fs.disk.borrow_mut();
|
let mut disk = self.fs.disk.borrow_mut();
|
||||||
@ -128,15 +169,8 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.offset += read_bytes as u32;
|
self.offset += read_bytes as u32;
|
||||||
|
self.current_cluster = Some(current_cluster);
|
||||||
buf_offset += read_bytes;
|
buf_offset += read_bytes;
|
||||||
if self.offset % cluster_size == 0 {
|
|
||||||
let r = self.fs.cluster_iter(current_cluster).skip(1).next();
|
|
||||||
self.current_cluster = match r {
|
|
||||||
Some(Err(err)) => return Err(err),
|
|
||||||
Some(Ok(n)) => Some(n),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(buf_offset)
|
Ok(buf_offset)
|
||||||
}
|
}
|
||||||
@ -147,17 +181,45 @@ impl<'a, 'b> Write for File<'a, 'b> {
|
|||||||
let mut buf_offset: usize = 0;
|
let mut buf_offset: usize = 0;
|
||||||
let cluster_size = self.fs.get_cluster_size();
|
let cluster_size = self.fs.get_cluster_size();
|
||||||
loop {
|
loop {
|
||||||
let current_cluster = match self.current_cluster {
|
|
||||||
Some(n) => n,
|
|
||||||
None => unimplemented!(), // FIXME: allocate cluster
|
|
||||||
};
|
|
||||||
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 bytes_left_in_buf = buf.len() - buf_offset;
|
let bytes_left_in_buf = buf.len() - buf_offset;
|
||||||
let write_size = cmp::min(bytes_left_in_buf, bytes_left_in_cluster);
|
let write_size = cmp::min(bytes_left_in_buf, bytes_left_in_cluster);
|
||||||
|
//println!("write {:?}", write_size);
|
||||||
if write_size == 0 {
|
if write_size == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_cluster_opt = if self.offset % cluster_size == 0 {
|
||||||
|
// next cluster
|
||||||
|
let next_cluster = match self.current_cluster {
|
||||||
|
None => self.first_cluster,
|
||||||
|
Some(n) => {
|
||||||
|
let r = self.fs.cluster_iter(n).next();
|
||||||
|
match r {
|
||||||
|
Some(Err(err)) => return Err(err),
|
||||||
|
Some(Ok(n)) => Some(n),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match next_cluster {
|
||||||
|
Some(_) => next_cluster,
|
||||||
|
None => {
|
||||||
|
let new_cluster = self.fs.alloc_cluster(self.current_cluster)?;
|
||||||
|
if self.first_cluster.is_none() {
|
||||||
|
self.set_first_cluster(new_cluster);
|
||||||
|
}
|
||||||
|
Some(new_cluster)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.current_cluster
|
||||||
|
};
|
||||||
|
let current_cluster = match current_cluster_opt {
|
||||||
|
Some(n) => n,
|
||||||
|
None => panic!("Offset inside cluster but no cluster allocated"), // FIXME
|
||||||
|
};
|
||||||
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
||||||
let written_bytes = {
|
let written_bytes = {
|
||||||
let mut disk = self.fs.disk.borrow_mut();
|
let mut disk = self.fs.disk.borrow_mut();
|
||||||
@ -168,15 +230,8 @@ impl<'a, 'b> Write for File<'a, 'b> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.offset += written_bytes as u32;
|
self.offset += written_bytes as u32;
|
||||||
|
self.current_cluster = Some(current_cluster);
|
||||||
buf_offset += written_bytes;
|
buf_offset += written_bytes;
|
||||||
if self.offset % cluster_size == 0 {
|
|
||||||
let r = self.fs.cluster_iter(current_cluster).skip(1).next();
|
|
||||||
self.current_cluster = match r {
|
|
||||||
Some(Err(err)) => return Err(err),
|
|
||||||
Some(Ok(n)) => Some(n),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.update_size();
|
self.update_size();
|
||||||
Ok(buf_offset)
|
Ok(buf_offset)
|
||||||
@ -200,11 +255,15 @@ impl<'a, 'b> Seek for File<'a, 'b> {
|
|||||||
return Err(io::Error::new(ErrorKind::InvalidInput, "invalid seek"));
|
return Err(io::Error::new(ErrorKind::InvalidInput, "invalid seek"));
|
||||||
}
|
}
|
||||||
let cluster_size = self.fs.get_cluster_size();
|
let cluster_size = self.fs.get_cluster_size();
|
||||||
let cluster_count = (new_offset / cluster_size as i64) as usize;
|
let cluster_count = ((new_offset + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;
|
||||||
let new_cluster = if cluster_count > 0 {
|
let new_cluster = if cluster_count == -1 {
|
||||||
|
None
|
||||||
|
} else if cluster_count == 0 {
|
||||||
|
self.first_cluster
|
||||||
|
} else {
|
||||||
match self.first_cluster {
|
match self.first_cluster {
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
match self.fs.cluster_iter(n).skip(cluster_count).next() {
|
match self.fs.cluster_iter(n).skip(cluster_count as usize - 1).next() {
|
||||||
Some(Err(err)) => return Err(err),
|
Some(Err(err)) => return Err(err),
|
||||||
Some(Ok(n)) => Some(n),
|
Some(Ok(n)) => Some(n),
|
||||||
None => None,
|
None => None,
|
||||||
@ -212,8 +271,6 @@ impl<'a, 'b> Seek for File<'a, 'b> {
|
|||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.first_cluster
|
|
||||||
};
|
};
|
||||||
self.offset = new_offset as u32;
|
self.offset = new_offset as u32;
|
||||||
self.current_cluster = new_cluster;
|
self.current_cluster = new_cluster;
|
||||||
|
17
src/fs.rs
17
src/fs.rs
@ -1,6 +1,5 @@
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::iter;
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{Error, ErrorKind, SeekFrom};
|
use std::io::{Error, ErrorKind, SeekFrom};
|
||||||
use std::io;
|
use std::io;
|
||||||
@ -8,7 +7,7 @@ use byteorder::{LittleEndian, ReadBytesExt};
|
|||||||
|
|
||||||
use file::File;
|
use file::File;
|
||||||
use dir::{DirRawStream, Dir};
|
use dir::{DirRawStream, Dir};
|
||||||
use table::ClusterIterator;
|
use table::{ClusterIterator, alloc_cluster};
|
||||||
|
|
||||||
// FAT implementation based on:
|
// FAT implementation based on:
|
||||||
// http://wiki.osdev.org/FAT
|
// http://wiki.osdev.org/FAT
|
||||||
@ -92,7 +91,6 @@ impl <'a> FileSystem<'a> {
|
|||||||
|
|
||||||
pub fn new<T: ReadWriteSeek>(disk: &'a mut T) -> io::Result<FileSystem<'a>> {
|
pub fn new<T: ReadWriteSeek>(disk: &'a mut T) -> io::Result<FileSystem<'a>> {
|
||||||
let boot = Self::read_boot_record(disk)?;
|
let boot = Self::read_boot_record(disk)?;
|
||||||
println!("sig {:?}", boot.boot_sig);
|
|
||||||
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"));
|
||||||
}
|
}
|
||||||
@ -218,16 +216,25 @@ impl <'a> FileSystem<'a> {
|
|||||||
self.offset_from_sector(self.sector_from_cluster(cluser))
|
self.offset_from_sector(self.sector_from_cluster(cluser))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> iter::Chain<iter::Once<io::Result<u32>>, ClusterIterator<'b, 'a>> {
|
fn fat_slice<'b>(&'b self) -> DiskSlice<'b, 'a> {
|
||||||
let bytes_per_sector = self.boot.bpb.bytes_per_sector as u64;
|
let bytes_per_sector = self.boot.bpb.bytes_per_sector as u64;
|
||||||
let fat_offset = self.boot.bpb.reserved_sectors as u64 * bytes_per_sector;
|
let fat_offset = self.boot.bpb.reserved_sectors as u64 * bytes_per_sector;
|
||||||
let sectors_per_fat =
|
let sectors_per_fat =
|
||||||
if self.boot.bpb.sectors_per_fat_16 == 0 { self.boot.bpb.sectors_per_fat_32 }
|
if self.boot.bpb.sectors_per_fat_16 == 0 { self.boot.bpb.sectors_per_fat_32 }
|
||||||
else { self.boot.bpb.sectors_per_fat_16 as u32 };
|
else { self.boot.bpb.sectors_per_fat_16 as u32 };
|
||||||
let fat_size = sectors_per_fat as u64 * bytes_per_sector;
|
let fat_size = sectors_per_fat as u64 * bytes_per_sector;
|
||||||
let disk_slice = DiskSlice::new(fat_offset, fat_size, self);
|
DiskSlice::new(fat_offset, fat_size, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator<'b, 'a> {
|
||||||
|
let disk_slice = self.fat_slice();
|
||||||
ClusterIterator::new(disk_slice, self.fat_type, cluster)
|
ClusterIterator::new(disk_slice, self.fat_type, cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn alloc_cluster(&self, prev_cluster: Option<u32>) -> io::Result<u32> {
|
||||||
|
let mut disk_slice = self.fat_slice();
|
||||||
|
alloc_cluster(&mut disk_slice, self.fat_type, prev_cluster)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
264
src/table.rs
264
src/table.rs
@ -1,72 +1,248 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use std::io::prelude::*;
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use fs::{FatType, DiskSlice, ReadSeek};
|
use fs::{FatType, DiskSlice, ReadSeek};
|
||||||
use core::iter;
|
|
||||||
|
|
||||||
fn get_next_cluster(rdr: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
|
struct Fat<T> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
dummy: [T; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fat12 = Fat<u8>;
|
||||||
|
type Fat16 = Fat<u16>;
|
||||||
|
type Fat32 = Fat<u32>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum FatValue {
|
||||||
|
Free,
|
||||||
|
Data(u32),
|
||||||
|
Bad,
|
||||||
|
EndOfChain,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FatTrait {
|
||||||
|
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
|
||||||
|
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
|
||||||
|
fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result<u32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
|
||||||
match fat_type {
|
match fat_type {
|
||||||
FatType::Fat12 => get_next_cluster_12(rdr, cluster),
|
FatType::Fat12 => Fat12::get(fat, cluster),
|
||||||
FatType::Fat16 => get_next_cluster_16(rdr, cluster),
|
FatType::Fat16 => Fat16::get(fat, cluster),
|
||||||
FatType::Fat32 => get_next_cluster_32(rdr, cluster),
|
FatType::Fat32 => Fat32::get(fat, cluster),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_next_cluster_12(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||||
let fat_offset = cluster + (cluster / 2);
|
//println!("write_fat {} {:?}", cluster, value);
|
||||||
let mut bytes = [0;2];
|
match fat_type {
|
||||||
rdr.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
FatType::Fat12 => Fat12::set(fat, cluster, value),
|
||||||
rdr.read(&mut bytes)?;
|
FatType::Fat16 => Fat16::set(fat, cluster, value),
|
||||||
let (val1, val2) = (bytes[0] as u16, bytes[1] as u16);
|
FatType::Fat32 => Fat32::set(fat, cluster, value),
|
||||||
|
|
||||||
let val = if cluster & 1 == 1 {
|
|
||||||
(val1 >> 4) | (val2 << 4)
|
|
||||||
} else {
|
|
||||||
val1 | (val2 & 0x0F)
|
|
||||||
};
|
|
||||||
if val <= 1 || val >= 0xFF7 {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(val as u32))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_next_cluster_16(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
|
||||||
rdr.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
let val = read_fat(fat, fat_type, cluster)?;
|
||||||
let val = rdr.read_u16::<LittleEndian>()?;
|
match val {
|
||||||
if val <= 1 || val >= 0xFFF7 {
|
FatValue::Data(n) => Ok(Some(n)),
|
||||||
Ok(None)
|
_ => Ok(None),
|
||||||
} else {
|
|
||||||
Ok(Some(val as u32))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_next_cluster_32(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<u32> {
|
||||||
rdr.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
match fat_type {
|
||||||
let val = rdr.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
|
FatType::Fat12 => Fat12::find_free(fat, cluster),
|
||||||
if val <= 1 || val >= 0x0FFFFFF7 {
|
FatType::Fat16 => Fat16::find_free(fat, cluster),
|
||||||
Ok(None)
|
FatType::Fat32 => Fat32::find_free(fat, cluster),
|
||||||
} else {
|
}
|
||||||
Ok(Some(val))
|
}
|
||||||
|
|
||||||
|
pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster: Option<u32>) -> io::Result<u32> {
|
||||||
|
let new_cluster = find_free_cluster(fat, fat_type, 2)?;
|
||||||
|
write_fat(fat, fat_type, new_cluster, FatValue::EndOfChain)?;
|
||||||
|
if prev_cluster.is_some() {
|
||||||
|
write_fat(fat, fat_type, prev_cluster.unwrap(), FatValue::Data(new_cluster))?; // safe
|
||||||
|
}
|
||||||
|
//println!("alloc_cluster {}", new_cluster);
|
||||||
|
Ok(new_cluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FatTrait for Fat12 {
|
||||||
|
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
|
||||||
|
let fat_offset = cluster + (cluster / 2);
|
||||||
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
|
let packed_val = fat.read_u16::<LittleEndian>()?;
|
||||||
|
let val = match cluster & 1 {
|
||||||
|
0 => packed_val & 0x0FFF,
|
||||||
|
_ => packed_val >> 4,
|
||||||
|
};
|
||||||
|
Ok(match val {
|
||||||
|
0 => FatValue::Free,
|
||||||
|
0xFF7 => FatValue::Bad,
|
||||||
|
0xFF8...0xFFF => FatValue::EndOfChain,
|
||||||
|
n => FatValue::Data(n as u32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||||
|
let raw_val = match value {
|
||||||
|
FatValue::Free => 0,
|
||||||
|
FatValue::Bad => 0xFF7,
|
||||||
|
FatValue::EndOfChain => 0xFFF,
|
||||||
|
FatValue::Data(n) => n as u16,
|
||||||
|
};
|
||||||
|
let fat_offset = cluster + (cluster / 2);
|
||||||
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
|
let old_packed = fat.read_u16::<LittleEndian>()?;
|
||||||
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
|
let new_packed = match cluster & 1 {
|
||||||
|
0 => (old_packed & 0xF000) | raw_val,
|
||||||
|
_ => (old_packed & 0x000F) | (raw_val << 4),
|
||||||
|
};
|
||||||
|
fat.write_u16::<LittleEndian>(new_packed)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result<u32> {
|
||||||
|
let mut cluster = hint_cluster;
|
||||||
|
let fat_offset = cluster + (cluster / 2);
|
||||||
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
|
let mut packed_val = fat.read_u16::<LittleEndian>()?;
|
||||||
|
loop {
|
||||||
|
let val = match cluster & 1 {
|
||||||
|
0 => packed_val & 0x0FFF,
|
||||||
|
_ => packed_val >> 4,
|
||||||
|
};
|
||||||
|
if val == 0 {
|
||||||
|
return Ok(cluster);
|
||||||
|
}
|
||||||
|
cluster += 1;
|
||||||
|
packed_val = match cluster & 1 {
|
||||||
|
0 => fat.read_u16::<LittleEndian>()?,
|
||||||
|
_ => {
|
||||||
|
let next_byte = fat.read_u8()? as u16;
|
||||||
|
(packed_val >> 8) | (next_byte << 8)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FatTrait for Fat16 {
|
||||||
|
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
|
let val = fat.read_u16::<LittleEndian>()?;
|
||||||
|
Ok(match val {
|
||||||
|
0 => FatValue::Free,
|
||||||
|
0xFFF7 => FatValue::Bad,
|
||||||
|
0xFFF8...0xFFFF => FatValue::EndOfChain,
|
||||||
|
n => FatValue::Data(n as u32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
|
let raw_val = match value {
|
||||||
|
FatValue::Free => 0,
|
||||||
|
FatValue::Bad => 0xFFF7,
|
||||||
|
FatValue::EndOfChain => 0xFFFF,
|
||||||
|
FatValue::Data(n) => n as u16,
|
||||||
|
};
|
||||||
|
fat.write_u16::<LittleEndian>(raw_val)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result<u32> {
|
||||||
|
let mut cluster = hint_cluster;
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
|
loop {
|
||||||
|
let val = fat.read_u16::<LittleEndian>()?;
|
||||||
|
if val == 0 {
|
||||||
|
return Ok(cluster);
|
||||||
|
}
|
||||||
|
cluster += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FatTrait for Fat32 {
|
||||||
|
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
|
let val = fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
|
||||||
|
Ok(match val {
|
||||||
|
0 => FatValue::Free,
|
||||||
|
0x0FFFFFF7 => FatValue::Bad,
|
||||||
|
0x0FFFFFF8...0x0FFFFFFF => FatValue::EndOfChain,
|
||||||
|
n => FatValue::Data(n as u32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
|
let raw_val = match value {
|
||||||
|
FatValue::Free => 0,
|
||||||
|
FatValue::Bad => 0x0FFFFFF7,
|
||||||
|
FatValue::EndOfChain => 0x0FFFFFFF,
|
||||||
|
FatValue::Data(n) => n,
|
||||||
|
};
|
||||||
|
fat.write_u32::<LittleEndian>(raw_val)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result<u32> {
|
||||||
|
let mut cluster = hint_cluster;
|
||||||
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
|
loop {
|
||||||
|
let val = fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
|
||||||
|
if val == 0 {
|
||||||
|
return Ok(cluster);
|
||||||
|
}
|
||||||
|
cluster += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ClusterIterator<'a, 'b: 'a> {
|
pub(crate) struct ClusterIterator<'a, 'b: 'a> {
|
||||||
rdr: DiskSlice<'a, 'b>,
|
fat: DiskSlice<'a, 'b>,
|
||||||
fat_type: FatType,
|
fat_type: FatType,
|
||||||
cluster: Option<u32>,
|
cluster: Option<u32>,
|
||||||
err: bool,
|
err: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> ClusterIterator<'a, 'b> {
|
impl <'a, 'b> ClusterIterator<'a, 'b> {
|
||||||
pub(crate) fn new(rdr: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
|
pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
|
||||||
-> iter::Chain<iter::Once<io::Result<u32>>, ClusterIterator<'a, 'b>> {
|
-> ClusterIterator<'a, 'b> {
|
||||||
let iter = ClusterIterator {
|
ClusterIterator {
|
||||||
rdr: rdr,
|
fat: fat,
|
||||||
fat_type: fat_type,
|
fat_type: fat_type,
|
||||||
cluster: Some(cluster),
|
cluster: Some(cluster),
|
||||||
err: false,
|
err: false,
|
||||||
};
|
}
|
||||||
iter::once(Ok(cluster)).chain(iter)
|
}
|
||||||
|
|
||||||
|
pub(crate) fn truncate(&mut self) -> io::Result<()> {
|
||||||
|
match self.cluster {
|
||||||
|
Some(n) => {
|
||||||
|
write_fat(&mut self.fat, self.fat_type, n, FatValue::EndOfChain)?;
|
||||||
|
self.next();
|
||||||
|
self.free()
|
||||||
|
},
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn free(&mut self) -> io::Result<()> {
|
||||||
|
loop {
|
||||||
|
let prev = self.cluster;
|
||||||
|
self.next();
|
||||||
|
match prev {
|
||||||
|
Some(n) => write_fat(&mut self.fat, self.fat_type, n, FatValue::Free)?,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +255,7 @@ impl <'a, 'b> Iterator for ClusterIterator<'a, 'b> {
|
|||||||
}
|
}
|
||||||
match self.cluster {
|
match self.cluster {
|
||||||
Some(current_cluster) => {
|
Some(current_cluster) => {
|
||||||
self.cluster = match get_next_cluster(&mut self.rdr, self.fat_type, current_cluster) {
|
self.cluster = match get_next_cluster(&mut self.fat, self.fat_type, current_cluster) {
|
||||||
Ok(next_cluster) => next_cluster,
|
Ok(next_cluster) => next_cluster,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.err = true;
|
self.err = true;
|
||||||
|
@ -15,23 +15,27 @@ const IMG_DIR: &str = "resources";
|
|||||||
const TMP_DIR: &str = "tmp";
|
const TMP_DIR: &str = "tmp";
|
||||||
const TEST_STR: &str = "Hi there Rust programmer!\n";
|
const TEST_STR: &str = "Hi there Rust programmer!\n";
|
||||||
|
|
||||||
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) {
|
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str, test_seq: u32) {
|
||||||
let img_path = format!("{}/{}", IMG_DIR, filename);
|
let img_path = format!("{}/{}", IMG_DIR, filename);
|
||||||
let tmp_path = format!("{}/{}", TMP_DIR, filename);
|
let tmp_path = format!("{}/{}-{}", TMP_DIR, test_seq, filename);
|
||||||
fs::create_dir(TMP_DIR).ok();
|
fs::create_dir(TMP_DIR).ok();
|
||||||
fs::copy(&img_path, &tmp_path).unwrap();
|
fs::copy(&img_path, &tmp_path).unwrap();
|
||||||
// let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
{
|
||||||
// let mut buf_file = BufStream::new(file);
|
// TODO: fix BufStream
|
||||||
// let fs = FileSystem::new(&mut buf_file).unwrap();
|
// let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||||
let mut file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
// let mut buf_file = BufStream::new(file);
|
||||||
let fs = FileSystem::new(&mut file).unwrap();
|
// let fs = FileSystem::new(&mut buf_file).unwrap();
|
||||||
f(fs);
|
let mut file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||||
|
let fs = FileSystem::new(&mut file).unwrap();
|
||||||
|
f(fs);
|
||||||
|
}
|
||||||
|
fs::remove_file(tmp_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_write_file(fs: FileSystem) {
|
fn test_write_short_file(fs: FileSystem) {
|
||||||
let mut root_dir = fs.root_dir();
|
let mut root_dir = fs.root_dir();
|
||||||
let mut file = root_dir.open_file("short.txt").expect("open file");
|
let mut file = root_dir.open_file("short.txt").expect("open file");
|
||||||
file.truncate();
|
file.truncate().unwrap();
|
||||||
assert_eq!(TEST_STR.len(), file.write(&TEST_STR.as_bytes()).unwrap());
|
assert_eq!(TEST_STR.len(), file.write(&TEST_STR.as_bytes()).unwrap());
|
||||||
file.seek(io::SeekFrom::Start(0)).unwrap();
|
file.seek(io::SeekFrom::Start(0)).unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
@ -41,15 +45,48 @@ fn test_write_file(fs: FileSystem) {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_file_fat12() {
|
fn test_write_file_fat12() {
|
||||||
call_with_fs(&test_write_file, FAT12_IMG)
|
call_with_fs(&test_write_short_file, FAT12_IMG, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_file_fat16() {
|
fn test_write_file_fat16() {
|
||||||
call_with_fs(&test_write_file, FAT16_IMG)
|
call_with_fs(&test_write_short_file, FAT16_IMG, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_file_fat32() {
|
fn test_write_file_fat32() {
|
||||||
call_with_fs(&test_write_file, FAT32_IMG)
|
call_with_fs(&test_write_short_file, FAT32_IMG, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_write_long_file(fs: FileSystem) {
|
||||||
|
let mut root_dir = fs.root_dir();
|
||||||
|
let mut file = root_dir.open_file("long.txt").expect("open file");
|
||||||
|
file.truncate().unwrap();
|
||||||
|
let test_str = TEST_STR.repeat(100);
|
||||||
|
assert_eq!(test_str.len(), file.write(&test_str.as_bytes()).unwrap());
|
||||||
|
file.seek(io::SeekFrom::Start(0)).unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(test_str, str::from_utf8(&buf).unwrap());
|
||||||
|
file.seek(io::SeekFrom::Start(1234)).unwrap();
|
||||||
|
file.truncate().unwrap();
|
||||||
|
file.seek(io::SeekFrom::Start(0)).unwrap();
|
||||||
|
buf.clear();
|
||||||
|
file.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(&test_str[..1234], str::from_utf8(&buf).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_long_file_fat12() {
|
||||||
|
call_with_fs(&test_write_long_file, FAT12_IMG, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_long_file_fat16() {
|
||||||
|
call_with_fs(&test_write_long_file, FAT16_IMG, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_long_file_fat32() {
|
||||||
|
call_with_fs(&test_write_long_file, FAT32_IMG, 2)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user