diff --git a/README.md b/README.md index 149b22e..3274274 100644 --- a/README.md +++ b/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 diff --git a/src/dir.rs b/src/dir.rs index 60f9204..821d288 100644 --- a/src/dir.rs +++ b/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) { + 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 { - 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::(&mut data.name_1)?; data.reserved_0 = self.rdr.read_u16::()?; self.rdr.read_u16_into::(&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::()?, size: self.rdr.read_u32::()?, }; - 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>; @@ -421,8 +425,8 @@ impl <'a, 'b> Iterator for DirIter<'a, 'b> { } let mut lfn_buf = Vec::::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) => { diff --git a/src/file.rs b/src/file.rs index a0eef58..43ef1e2 100644 --- a/src/file.rs +++ b/src/file.rs @@ -9,6 +9,7 @@ use dir::{FileEntryInfo, DateTime}; #[derive(Clone)] pub struct File<'a, 'b: 'a> { first_cluster: Option, + // Note: if offset points between clusters current_cluster is the previous cluster current_cluster: Option, offset: u32, entry: Option, @@ -20,7 +21,7 @@ impl <'a, 'b> File<'a, 'b> { pub(crate) fn new(first_cluster: Option, entry: Option, 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 { + // 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; diff --git a/src/fs.rs b/src/fs.rs index dc78ffc..8f2da91 100644 --- a/src/fs.rs +++ b/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(disk: &'a mut T) -> io::Result> { 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>, 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) -> io::Result { + let mut disk_slice = self.fat_slice(); + alloc_cluster(&mut disk_slice, self.fat_type, prev_cluster) + } } #[derive(Clone)] diff --git a/src/table.rs b/src/table.rs index e1f53fe..7ac2285 100644 --- a/src/table.rs +++ b/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> { +struct Fat { + #[allow(dead_code)] + dummy: [T; 0], +} + +type Fat12 = Fat; +type Fat16 = Fat; +type Fat32 = Fat; + +#[derive(Debug, Clone, Copy)] +enum FatValue { + Free, + Data(u32), + Bad, + EndOfChain, +} + +trait FatTrait { + fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result; + fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>; + fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result; +} + +fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result { 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> { - 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> { + 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 { + 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) -> io::Result { + 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 { + let fat_offset = cluster + (cluster / 2); + fat.seek(io::SeekFrom::Start(fat_offset as u64))?; + let packed_val = fat.read_u16::()?; + 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::()?; + 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::(new_packed)?; + Ok(()) + } + + fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result { + 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::()?; + 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::()?, + _ => { + 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> { - rdr.seek(io::SeekFrom::Start((cluster*2) as u64))?; - let val = rdr.read_u16::()?; - 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 { + fat.seek(io::SeekFrom::Start((cluster*2) as u64))?; + let val = fat.read_u16::()?; + 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::(raw_val)?; + Ok(()) + } + + fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result { + let mut cluster = hint_cluster; + fat.seek(io::SeekFrom::Start((cluster*2) as u64))?; + loop { + let val = fat.read_u16::()?; + if val == 0 { + return Ok(cluster); + } + cluster += 1; + } } } -fn get_next_cluster_32(rdr: &mut ReadSeek, cluster: u32) -> io::Result> { - rdr.seek(io::SeekFrom::Start((cluster*4) as u64))?; - let val = rdr.read_u32::()? & 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 { + fat.seek(io::SeekFrom::Start((cluster*4) as u64))?; + let val = fat.read_u32::()? & 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::(raw_val)?; + Ok(()) + } + + fn find_free(fat: &mut ReadSeek, hint_cluster: u32) -> io::Result { + let mut cluster = hint_cluster; + fat.seek(io::SeekFrom::Start((cluster*4) as u64))?; + loop { + let val = fat.read_u32::()? & 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, err: bool, } impl <'a, 'b> ClusterIterator<'a, 'b> { - pub(crate) fn new(rdr: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32) - -> iter::Chain>, 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; diff --git a/tests/write.rs b/tests/write.rs index 02216de..47b2526 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -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) }