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
|
||||
------------
|
||||
|
||||
FAT filesystem read-only library implemented in Rust.
|
||||
FAT filesystem library implemented in Rust.
|
||||
|
||||
Features:
|
||||
* FAT12, FAT16, FAT32 filesystem versions,
|
||||
* read directory,
|
||||
* read file,
|
||||
* LFN (Long File Names).
|
||||
* FAT12, FAT16, FAT32 supported,
|
||||
* read directory entries,
|
||||
* read file contents,
|
||||
* LFN (Long File Names),
|
||||
* basic write support (write and truncate existing file).
|
||||
|
||||
Planned features:
|
||||
* write support,
|
||||
Missing features (yet):
|
||||
* create new file/directory,
|
||||
* remove file/directory,
|
||||
|
||||
Other planned features (Nice to Have):
|
||||
* no_std environment support.
|
||||
|
||||
License
|
||||
|
14
src/dir.rs
14
src/dir.rs
@ -83,6 +83,12 @@ impl DirFileEntryData {
|
||||
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 {
|
||||
self.size
|
||||
}
|
||||
@ -372,10 +378,8 @@ pub struct DirIter<'a, 'b: 'a> {
|
||||
|
||||
impl <'a, 'b> DirIter<'a, 'b> {
|
||||
fn read_dir_entry_data(&mut self) -> io::Result<DirEntryData> {
|
||||
println!("read_dir_entry_data");
|
||||
let mut name = [0; 11];
|
||||
self.rdr.read_exact(&mut name)?;
|
||||
println!("read_dir_entry_data {:?}", &name);
|
||||
let attrs = FileAttributes::from_bits_truncate(self.rdr.read_u8()?);
|
||||
if attrs == FileAttributes::LFN {
|
||||
let mut data = DirLfnEntryData {
|
||||
@ -389,7 +393,6 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
||||
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_1)?;
|
||||
data.reserved_0 = self.rdr.read_u16::<LittleEndian>()?;
|
||||
self.rdr.read_u16_into::<LittleEndian>(&mut data.name_2)?;
|
||||
println!("read_dir_entry_data end");
|
||||
Ok(DirEntryData::Lfn(data))
|
||||
} else {
|
||||
let data = DirFileEntryData {
|
||||
@ -406,12 +409,13 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
||||
first_cluster_lo: self.rdr.read_u16::<LittleEndian>()?,
|
||||
size: self.rdr.read_u32::<LittleEndian>()?,
|
||||
};
|
||||
println!("read_dir_entry_data end");
|
||||
Ok(DirEntryData::File(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DIR_ENTRY_SIZE: u64 = 32;
|
||||
|
||||
impl <'a, 'b> Iterator for DirIter<'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();
|
||||
loop {
|
||||
let entry_pos = self.rdr.global_pos();
|
||||
let res = self.read_dir_entry_data();
|
||||
let entry_pos = self.rdr.global_pos().map(|p| p - DIR_ENTRY_SIZE);
|
||||
let data = match res {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
|
121
src/file.rs
121
src/file.rs
@ -9,6 +9,7 @@ use dir::{FileEntryInfo, DateTime};
|
||||
#[derive(Clone)]
|
||||
pub struct File<'a, 'b: 'a> {
|
||||
first_cluster: Option<u32>,
|
||||
// Note: if offset points between clusters current_cluster is the previous cluster
|
||||
current_cluster: Option<u32>,
|
||||
offset: u32,
|
||||
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 {
|
||||
File {
|
||||
first_cluster, entry, fs,
|
||||
current_cluster: first_cluster,
|
||||
current_cluster: None, // cluster before first one
|
||||
offset: 0,
|
||||
entry_dirty: false,
|
||||
}
|
||||
@ -38,20 +39,32 @@ impl <'a, 'b> File<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn truncate(&mut self) {
|
||||
// FIXME: free clusters?
|
||||
pub fn truncate(&mut self) -> io::Result<()> {
|
||||
match self.entry {
|
||||
Some(ref mut e) => {
|
||||
if e.data.size != self.offset {
|
||||
e.data.size = self.offset;
|
||||
self.entry_dirty = true;
|
||||
if e.data.size == self.offset {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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> {
|
||||
// Note: when between clusters it returns position after previous cluster
|
||||
match self.current_cluster {
|
||||
Some(n) => {
|
||||
let cluster_size = self.fs.get_cluster_size();
|
||||
@ -93,6 +106,17 @@ impl <'a, 'b> File<'a, 'b> {
|
||||
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> {
|
||||
@ -106,7 +130,23 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
||||
let mut buf_offset: usize = 0;
|
||||
let cluster_size = self.fs.get_cluster_size();
|
||||
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,
|
||||
None => break,
|
||||
};
|
||||
@ -118,6 +158,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
||||
if read_size == 0 {
|
||||
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 read_bytes = {
|
||||
let mut disk = self.fs.disk.borrow_mut();
|
||||
@ -128,15 +169,8 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
||||
break;
|
||||
}
|
||||
self.offset += read_bytes as u32;
|
||||
self.current_cluster = Some(current_cluster);
|
||||
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)
|
||||
}
|
||||
@ -147,17 +181,45 @@ impl<'a, 'b> Write for File<'a, 'b> {
|
||||
let mut buf_offset: usize = 0;
|
||||
let cluster_size = self.fs.get_cluster_size();
|
||||
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 bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
|
||||
let bytes_left_in_buf = buf.len() - buf_offset;
|
||||
let write_size = cmp::min(bytes_left_in_buf, bytes_left_in_cluster);
|
||||
//println!("write {:?}", write_size);
|
||||
if write_size == 0 {
|
||||
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 written_bytes = {
|
||||
let mut disk = self.fs.disk.borrow_mut();
|
||||
@ -168,15 +230,8 @@ impl<'a, 'b> Write for File<'a, 'b> {
|
||||
break;
|
||||
}
|
||||
self.offset += written_bytes as u32;
|
||||
self.current_cluster = Some(current_cluster);
|
||||
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();
|
||||
Ok(buf_offset)
|
||||
@ -200,11 +255,15 @@ impl<'a, 'b> Seek for File<'a, 'b> {
|
||||
return Err(io::Error::new(ErrorKind::InvalidInput, "invalid seek"));
|
||||
}
|
||||
let cluster_size = self.fs.get_cluster_size();
|
||||
let cluster_count = (new_offset / cluster_size as i64) as usize;
|
||||
let new_cluster = if cluster_count > 0 {
|
||||
let cluster_count = ((new_offset + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;
|
||||
let new_cluster = if cluster_count == -1 {
|
||||
None
|
||||
} else if cluster_count == 0 {
|
||||
self.first_cluster
|
||||
} else {
|
||||
match self.first_cluster {
|
||||
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(Ok(n)) => Some(n),
|
||||
None => None,
|
||||
@ -212,8 +271,6 @@ impl<'a, 'b> Seek for File<'a, 'b> {
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
self.first_cluster
|
||||
};
|
||||
self.offset = new_offset as u32;
|
||||
self.current_cluster = new_cluster;
|
||||
|
17
src/fs.rs
17
src/fs.rs
@ -1,6 +1,5 @@
|
||||
use core::cell::RefCell;
|
||||
use core::cmp;
|
||||
use core::iter;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{Error, ErrorKind, SeekFrom};
|
||||
use std::io;
|
||||
@ -8,7 +7,7 @@ use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
||||
use file::File;
|
||||
use dir::{DirRawStream, Dir};
|
||||
use table::ClusterIterator;
|
||||
use table::{ClusterIterator, alloc_cluster};
|
||||
|
||||
// FAT implementation based on:
|
||||
// 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>> {
|
||||
let boot = Self::read_boot_record(disk)?;
|
||||
println!("sig {:?}", boot.boot_sig);
|
||||
if boot.boot_sig != [0x55, 0xAA] {
|
||||
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))
|
||||
}
|
||||
|
||||
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 fat_offset = self.boot.bpb.reserved_sectors as u64 * bytes_per_sector;
|
||||
let sectors_per_fat =
|
||||
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 };
|
||||
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)
|
||||
}
|
||||
|
||||
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)]
|
||||
|
262
src/table.rs
262
src/table.rs
@ -1,72 +1,248 @@
|
||||
use std::io;
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::prelude::*;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
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 {
|
||||
FatType::Fat12 => get_next_cluster_12(rdr, cluster),
|
||||
FatType::Fat16 => get_next_cluster_16(rdr, cluster),
|
||||
FatType::Fat32 => get_next_cluster_32(rdr, cluster),
|
||||
FatType::Fat12 => Fat12::get(fat, cluster),
|
||||
FatType::Fat16 => Fat16::get(fat, cluster),
|
||||
FatType::Fat32 => Fat32::get(fat, cluster),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_cluster_12(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
||||
let fat_offset = cluster + (cluster / 2);
|
||||
let mut bytes = [0;2];
|
||||
rdr.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||
rdr.read(&mut bytes)?;
|
||||
let (val1, val2) = (bytes[0] as u16, bytes[1] as u16);
|
||||
fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||
//println!("write_fat {} {:?}", cluster, value);
|
||||
match fat_type {
|
||||
FatType::Fat12 => Fat12::set(fat, cluster, value),
|
||||
FatType::Fat16 => Fat16::set(fat, cluster, value),
|
||||
FatType::Fat32 => Fat32::set(fat, cluster, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
|
||||
let val = read_fat(fat, fat_type, cluster)?;
|
||||
match val {
|
||||
FatValue::Data(n) => Ok(Some(n)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<u32> {
|
||||
match fat_type {
|
||||
FatType::Fat12 => Fat12::find_free(fat, cluster),
|
||||
FatType::Fat16 => Fat16::find_free(fat, cluster),
|
||||
FatType::Fat32 => Fat32::find_free(fat, cluster),
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
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 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)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_cluster_16(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
||||
rdr.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||
let val = rdr.read_u16::<LittleEndian>()?;
|
||||
if val <= 1 || val >= 0xFFF7 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(val as u32))
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_cluster_32(rdr: &mut ReadSeek, cluster: u32) -> io::Result<Option<u32>> {
|
||||
rdr.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||
let val = rdr.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
|
||||
if val <= 1 || val >= 0x0FFFFFF7 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(val))
|
||||
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> {
|
||||
rdr: DiskSlice<'a, 'b>,
|
||||
fat: DiskSlice<'a, 'b>,
|
||||
fat_type: FatType,
|
||||
cluster: Option<u32>,
|
||||
err: bool,
|
||||
}
|
||||
|
||||
impl <'a, 'b> ClusterIterator<'a, 'b> {
|
||||
pub(crate) fn new(rdr: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
|
||||
-> iter::Chain<iter::Once<io::Result<u32>>, ClusterIterator<'a, 'b>> {
|
||||
let iter = ClusterIterator {
|
||||
rdr: rdr,
|
||||
pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
|
||||
-> ClusterIterator<'a, 'b> {
|
||||
ClusterIterator {
|
||||
fat: fat,
|
||||
fat_type: fat_type,
|
||||
cluster: Some(cluster),
|
||||
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 {
|
||||
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,
|
||||
Err(err) => {
|
||||
self.err = true;
|
||||
|
@ -15,23 +15,27 @@ const IMG_DIR: &str = "resources";
|
||||
const TMP_DIR: &str = "tmp";
|
||||
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 tmp_path = format!("{}/{}", TMP_DIR, filename);
|
||||
let tmp_path = format!("{}/{}-{}", TMP_DIR, test_seq, filename);
|
||||
fs::create_dir(TMP_DIR).ok();
|
||||
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);
|
||||
// let fs = FileSystem::new(&mut buf_file).unwrap();
|
||||
let mut file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||
let fs = FileSystem::new(&mut file).unwrap();
|
||||
f(fs);
|
||||
{
|
||||
// TODO: fix BufStream
|
||||
// let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||
// let mut buf_file = BufStream::new(file);
|
||||
// let fs = FileSystem::new(&mut buf_file).unwrap();
|
||||
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 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());
|
||||
file.seek(io::SeekFrom::Start(0)).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
@ -41,15 +45,48 @@ fn test_write_file(fs: FileSystem) {
|
||||
|
||||
#[test]
|
||||
fn test_write_file_fat12() {
|
||||
call_with_fs(&test_write_file, FAT12_IMG)
|
||||
call_with_fs(&test_write_short_file, FAT12_IMG, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_file_fat16() {
|
||||
call_with_fs(&test_write_file, FAT16_IMG)
|
||||
call_with_fs(&test_write_short_file, FAT16_IMG, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
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