Alloc and free clusters for file data.

This commit is contained in:
Rafał Harabień 2017-10-09 21:14:28 +02:00
parent f32f1c7279
commit d8eba51b88
6 changed files with 390 additions and 105 deletions

View File

@ -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

View File

@ -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) => {

View File

@ -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;

View File

@ -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)]

View File

@ -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;

View File

@ -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)
}