diff --git a/src/dir.rs b/src/dir.rs index 4dae8ca..f365c29 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -11,7 +11,7 @@ use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileA #[cfg(feature = "alloc")] use dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN}; use file::File; -use fs::{DiskSlice, FileSystem, ReadWriteSeek}; +use fs::{DiskSlice, FsIoAdapter, FileSystem, ReadWriteSeek}; #[cfg(feature = "alloc")] type LfnUtf16 = Vec; @@ -20,7 +20,7 @@ type LfnUtf16 = (); pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> { File(File<'a, T>), - Root(DiskSlice<'a, T>), + Root(DiskSlice>), } impl<'a, T: ReadWriteSeek> DirRawStream<'a, T> { diff --git a/src/fs.rs b/src/fs.rs index 8db96be..675a97e 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -788,7 +788,8 @@ impl FileSystem { self.first_data_sector - self.root_dir_sectors, self.root_dir_sectors, 1, - self, + &self.bpb, + FsIoAdapter { fs: self }, )), _ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)), } @@ -812,20 +813,14 @@ impl FileSystem { self.offset_from_sector(self.sector_from_cluster(cluser)) } - fn fat_slice<'b>(&'b self) -> DiskSlice<'b, T> { - let sectors_per_fat = self.bpb.sectors_per_fat(); - let mirroring_enabled = self.bpb.mirroring_enabled(); - let (fat_first_sector, mirrors) = if mirroring_enabled { - (self.bpb.reserved_sectors as u32, self.bpb.fats) - } else { - let active_fat = self.bpb.active_fat() as u32; - let fat_first_sector = (self.bpb.reserved_sectors as u32) + active_fat * sectors_per_fat; - (fat_first_sector, 1) + fn fat_slice<'b>(&'b self) -> DiskSlice> { + let io = FsIoAdapter { + fs: self, }; - DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, self) + fat_slice(io, &self.bpb) } - pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator> { + pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator>> { let disk_slice = self.fat_slice(); ClusterIterator::new(disk_slice, self.fat_type, cluster) } @@ -946,32 +941,84 @@ impl Drop for FileSystem { } } -pub(crate) struct DiskSlice<'a, T: ReadWriteSeek + 'a> { +pub(crate) struct FsIoAdapter<'a, T: ReadWriteSeek + 'a> { + fs: &'a FileSystem, +} + +impl<'a, T: ReadWriteSeek> Read for FsIoAdapter<'a, T> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.fs.disk.borrow_mut().read(buf) + } +} + +impl<'a, T: ReadWriteSeek> Write for FsIoAdapter<'a, T> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let size = self.fs.disk.borrow_mut().write(buf)?; + if size > 0 { + self.fs.set_dirty_flag(true)?; + } + Ok(size) + } + + fn flush(&mut self) -> io::Result<()> { + self.fs.disk.borrow_mut().flush() + } +} + +impl<'a, T: ReadWriteSeek> Seek for FsIoAdapter<'a, T> { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.fs.disk.borrow_mut().seek(pos) + } +} + +// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925 +impl<'a, T: ReadWriteSeek> Clone for FsIoAdapter<'a, T> { + fn clone(&self) -> Self { + FsIoAdapter { + fs: self.fs, + } + } +} + +fn fat_slice(io: T, bpb: &BiosParameterBlock) -> DiskSlice { + let sectors_per_fat = bpb.sectors_per_fat(); + let mirroring_enabled = bpb.mirroring_enabled(); + let (fat_first_sector, mirrors) = if mirroring_enabled { + (bpb.reserved_sectors as u32, bpb.fats) + } else { + let active_fat = bpb.active_fat() as u32; + let fat_first_sector = (bpb.reserved_sectors as u32) + active_fat * sectors_per_fat; + (fat_first_sector, 1) + }; + DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, bpb, io) +} + +pub(crate) struct DiskSlice { begin: u64, size: u64, offset: u64, mirrors: u8, - fs: &'a FileSystem, + inner: T, } -impl<'a, T: ReadWriteSeek> DiskSlice<'a, T> { - pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: &'a FileSystem) -> Self { +impl DiskSlice { + pub(crate) fn new(begin: u64, size: u64, mirrors: u8, inner: T) -> Self { DiskSlice { begin, size, mirrors, - fs, + inner, offset: 0, } } - pub(crate) fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, fs: &'a FileSystem) -> Self { - let bytes_per_sector = fs.bpb.bytes_per_sector as u64; + fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, bpb: &BiosParameterBlock, inner: T) -> Self { + let bytes_per_sector = bpb.bytes_per_sector as u64; Self::new( first_sector as u64 * bytes_per_sector, sector_count as u64 * bytes_per_sector, mirrors, - fs, + inner, ) } @@ -981,56 +1028,51 @@ impl<'a, T: ReadWriteSeek> DiskSlice<'a, T> { } // Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925 -impl<'a, T: ReadWriteSeek> Clone for DiskSlice<'a, T> { +impl Clone for DiskSlice { fn clone(&self) -> Self { DiskSlice { begin: self.begin, size: self.size, offset: self.offset, mirrors: self.mirrors, - fs: self.fs, + inner: self.inner.clone(), } } } -impl<'a, T: ReadWriteSeek> Read for DiskSlice<'a, T> { +impl<'a, T: Read + Seek> Read for DiskSlice { fn read(&mut self, buf: &mut [u8]) -> io::Result { let offset = self.begin + self.offset; let read_size = cmp::min((self.size - self.offset) as usize, buf.len()); - let mut disk = self.fs.disk.borrow_mut(); - disk.seek(SeekFrom::Start(offset))?; - let size = disk.read(&mut buf[..read_size])?; + self.inner.seek(SeekFrom::Start(offset))?; + let size = self.inner.read(&mut buf[..read_size])?; self.offset += size as u64; Ok(size) } } -impl<'a, T: ReadWriteSeek> Write for DiskSlice<'a, T> { +impl<'a, T: Write + Seek> Write for DiskSlice { fn write(&mut self, buf: &[u8]) -> io::Result { let offset = self.begin + self.offset; let write_size = cmp::min((self.size - self.offset) as usize, buf.len()); if write_size == 0 { return Ok(0); } - // Mark the volume 'dirty' - self.fs.set_dirty_flag(true)?; // Write data - let mut disk = self.fs.disk.borrow_mut(); for i in 0..self.mirrors { - disk.seek(SeekFrom::Start(offset + i as u64 * self.size))?; - disk.write_all(&buf[..write_size])?; + self.inner.seek(SeekFrom::Start(offset + i as u64 * self.size))?; + self.inner.write_all(&buf[..write_size])?; } self.offset += write_size as u64; Ok(write_size) } fn flush(&mut self) -> io::Result<()> { - let mut disk = self.fs.disk.borrow_mut(); - disk.flush() + self.inner.flush() } } -impl<'a, T: ReadWriteSeek> Seek for DiskSlice<'a, T> { +impl<'a, T> Seek for DiskSlice { fn seek(&mut self, pos: SeekFrom) -> io::Result { let new_offset = match pos { SeekFrom::Current(x) => self.offset as i64 + x, @@ -1325,25 +1367,24 @@ pub fn format_volume(mut disk: T, options: FormatOptions) -> i let sectors_per_fat: u32 = boot.bpb.sectors_per_fat(); let bytes_per_fat: u32 = sectors_per_fat * bytes_per_sector as u32; let reserved_sectors = boot.bpb.reserved_sectors; - let mut fat_pos = reserved_sectors as u64 * bytes_per_sector as u64; - for _ in 0..boot.bpb.fats { - disk.seek(SeekFrom::Start(fat_pos))?; - write_zeros(&mut disk, bytes_per_fat as usize)?; - disk.seek(SeekFrom::Start(fat_pos))?; - format_fat(&mut disk, fat_type, boot.bpb.media)?; - fat_pos += bytes_per_fat as u64; - // TODO: mark entries at the end of FAT as used (after FAT but before sector end) - // TODO: mark special entries 0x0FFFFFF0 - 0x0FFFFFFF as BAD if they exists on FAT32 volume + let fat_pos = reserved_sectors as u64 * bytes_per_sector as u64; + disk.seek(SeekFrom::Start(fat_pos))?; + write_zeros(&mut disk, bytes_per_fat as usize * boot.bpb.fats as usize)?; + { + let mut fat_slice = fat_slice(&mut disk, &boot.bpb); + format_fat(&mut fat_slice, fat_type, boot.bpb.media, bytes_per_fat, boot.bpb.total_clusters())?; } // Root directory - let root_dir_pos = fat_pos; + let root_dir_pos = fat_pos + bytes_per_fat as u64 * boot.bpb.fats as u64; disk.seek(SeekFrom::Start(root_dir_pos))?; let root_dir_sectors: u32 = boot.bpb.root_dir_sectors(); write_zeros(&mut disk, root_dir_sectors as usize * bytes_per_sector as usize)?; if fat_type == FatType::Fat32 { - // FIXME: alloc_cluster needs FAT stream, not entire disk - let root_dir_first_cluster = alloc_cluster(&mut disk, fat_type, None, None, 1)?; + let root_dir_first_cluster = { + let mut fat_slice = fat_slice(&mut disk, &boot.bpb); + alloc_cluster(&mut fat_slice, fat_type, None, None, 1)? + }; assert!(root_dir_first_cluster == boot.bpb.root_dir_first_cluster); let first_data_sector = reserved_sectors as u32 + sectors_per_fat + root_dir_sectors; let sectors_per_cluster = boot.bpb.sectors_per_cluster; diff --git a/src/table.rs b/src/table.rs index 66a510c..9188ed5 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,5 +1,6 @@ use byteorder::LittleEndian; use byteorder_ext::{ReadBytesExt, WriteBytesExt}; +use core::cmp; use io; use fs::{FatType, FsStatusFlags, ReadSeek, ReadWriteSeek}; @@ -26,6 +27,7 @@ enum FatValue { trait FatTrait { fn get_raw(fat: &mut T, cluster: u32) -> io::Result; fn get(fat: &mut T, cluster: u32) -> io::Result; + fn set_raw(fat: &mut T, cluster: u32, raw_value: u32) -> io::Result<()>; fn set(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()>; fn find_free(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result; fn count_free(fat: &mut T, end_cluster: u32) -> io::Result; @@ -109,8 +111,17 @@ pub(crate) fn read_fat_flags(fat: &mut T, fat_type: FatType) -> io: Ok(FsStatusFlags { dirty, io_error }) } -pub(crate) fn format_fat(fat: &mut T, fat_type: FatType, media: u8) -> io::Result<()> { - // Note: cannot use any seeking function here because fat parameter is not a disk slice +pub(crate) fn count_free_clusters(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result { + let end_cluster = total_clusters + RESERVED_FAT_ENTRIES; + match fat_type { + FatType::Fat12 => Fat12::count_free(fat, end_cluster), + FatType::Fat16 => Fat16::count_free(fat, end_cluster), + FatType::Fat32 => Fat32::count_free(fat, end_cluster), + } +} + +pub(crate) fn format_fat(fat: &mut T, fat_type: FatType, media: u8, bytes_per_fat: u32, total_clusters: u32) -> io::Result<()> { + // init first two reserved entries to FAT ID match fat_type { FatType::Fat12 => { fat.write_u8(media)?; @@ -125,16 +136,21 @@ pub(crate) fn format_fat(fat: &mut T, fat_type: FatType, media fat.write_u32::(0xFFFFFFFF)?; }, }; - Ok(()) -} - -pub(crate) fn count_free_clusters(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result { - let end_cluster = total_clusters + RESERVED_FAT_ENTRIES; - match fat_type { - FatType::Fat12 => Fat12::count_free(fat, end_cluster), - FatType::Fat16 => Fat16::count_free(fat, end_cluster), - FatType::Fat32 => Fat32::count_free(fat, end_cluster), + // mark entries at the end of FAT as used (after FAT but before sector end) + const BITS_PER_BYTE: u32 = 8; + let start_cluster = total_clusters + RESERVED_FAT_ENTRIES; + let end_cluster = bytes_per_fat * BITS_PER_BYTE / fat_type.bits_per_fat_entry(); + for cluster in start_cluster..end_cluster { + write_fat(fat, fat_type, cluster, FatValue::EndOfChain)?; } + // mark special entries 0x0FFFFFF0 - 0x0FFFFFFF as BAD if they exists on FAT32 volume + if end_cluster > 0x0FFFFFF0 { + let end_bad_cluster = cmp::min(0x0FFFFFFF + 1, end_cluster); + for cluster in 0x0FFFFFF0..end_bad_cluster { + write_fat(fat, fat_type, cluster, FatValue::Bad)?; + } + } + Ok(()) } impl FatTrait for Fat12 { @@ -165,13 +181,17 @@ impl FatTrait for Fat12 { FatValue::EndOfChain => 0xFFF, FatValue::Data(n) => n as u16, }; + Self::set_raw(fat, cluster, raw_val as u32) + } + + fn set_raw(fat: &mut T, cluster: u32, raw_val: u32) -> io::Result<()> { 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), + 0 => (old_packed & 0xF000) | raw_val as u16, + _ => (old_packed & 0x000F) | ((raw_val as u16) << 4), }; fat.write_u16::(new_packed)?; Ok(()) @@ -248,16 +268,20 @@ impl FatTrait for Fat16 { }) } - fn set(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> { + fn set_raw(fat: &mut T, cluster: u32, raw_value: u32) -> io::Result<()> { fat.seek(io::SeekFrom::Start((cluster * 2) as u64))?; - let raw_val = match value { + fat.write_u16::(raw_value as u16)?; + Ok(()) + } + + fn set(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> { + let raw_value = match value { FatValue::Free => 0, FatValue::Bad => 0xFFF7, FatValue::EndOfChain => 0xFFFF, FatValue::Data(n) => n as u16, }; - fat.write_u16::(raw_val)?; - Ok(()) + Self::set_raw(fat, cluster, raw_value as u32) } fn find_free(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result { @@ -317,9 +341,14 @@ impl FatTrait for Fat32 { }) } + fn set_raw(fat: &mut T, cluster: u32, raw_value: u32) -> io::Result<()> { + fat.seek(io::SeekFrom::Start((cluster * 4) as u64))?; + fat.write_u32::(raw_value)?; + Ok(()) + } + fn set(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> { let old_reserved_bits = Self::get_raw(fat, cluster)? & 0xF0000000; - fat.seek(io::SeekFrom::Start((cluster * 4) as u64))?; if value == FatValue::Free && cluster >= 0x0FFFFFF7 && cluster <= 0x0FFFFFFF { // NOTE: it is technically allowed for them to store FAT chain loops, @@ -338,8 +367,7 @@ impl FatTrait for Fat32 { FatValue::Data(n) => n, }; let raw_val = raw_val | old_reserved_bits; // must preserve original reserved values - fat.write_u32::(raw_val)?; - Ok(()) + Self::set_raw(fat, cluster, raw_val) } fn find_free(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result { diff --git a/tests/format.rs b/tests/format.rs index 52008ea..36b26b2 100644 --- a/tests/format.rs +++ b/tests/format.rs @@ -11,17 +11,9 @@ const KB: u32 = 1024; const MB: u32 = KB * 1024; const TEST_STR: &str = "Hi there Rust programmer!\n"; -#[test] -fn test_format() { - let _ = env_logger::try_init(); - let storage_vec: Vec = Vec::with_capacity((8 * MB) as usize); - let storage_cur = io::Cursor::new(storage_vec); - let mut buffered_stream = BufStream::new(storage_cur); - let mut opts: fatfs::FormatOptions = Default::default(); - opts.total_sectors = 8 * MB / 512; - fatfs::format_volume(&mut buffered_stream, opts).expect("format volume"); +type FileSystem = fatfs::FileSystem>>>; - let fs = fatfs::FileSystem::new(buffered_stream, fatfs::FsOptions::new()).expect("open fs"); +fn basic_fs_test(fs: &FileSystem) { let root_dir = fs.root_dir(); let entries = root_dir.iter().map(|r| r.unwrap()).collect::>(); assert_eq!(entries.len(), 0); @@ -33,3 +25,52 @@ fn test_format() { let entries = root_dir.iter().map(|r| r.unwrap()).collect::>(); assert_eq!(entries.len(), 1); } + +fn test_format_fs(opts: fatfs::FormatOptions, total_bytes: u64) -> FileSystem { + let _ = env_logger::try_init(); + let storage_vec: Vec = Vec::with_capacity(total_bytes as usize); + let storage_cur = io::Cursor::new(storage_vec); + let mut buffered_stream = BufStream::new(storage_cur); + fatfs::format_volume(&mut buffered_stream, opts).expect("format volume"); + + let fs = fatfs::FileSystem::new(buffered_stream, fatfs::FsOptions::new()).expect("open fs"); + basic_fs_test(&fs); + fs +} + +#[test] +fn test_format_1mb() { + let total_bytes = 1 * MB as u64; + let mut opts: fatfs::FormatOptions = Default::default(); + opts.total_sectors = (total_bytes / 512) as u32; + let fs = test_format_fs(opts, total_bytes); + assert_eq!(fs.fat_type(), fatfs::FatType::Fat12); +} + +#[test] +fn test_format_8mb() { + let total_bytes = 8 * MB as u64; + let mut opts: fatfs::FormatOptions = Default::default(); + opts.total_sectors = (total_bytes / 512) as u32; + let fs = test_format_fs(opts, total_bytes); + assert_eq!(fs.fat_type(), fatfs::FatType::Fat16); +} + +#[test] +fn test_format_50mb() { + let total_bytes = 50 * MB as u64; + let mut opts: fatfs::FormatOptions = Default::default(); + opts.total_sectors = (total_bytes / 512) as u32; + let fs = test_format_fs(opts, total_bytes); + assert_eq!(fs.fat_type(), fatfs::FatType::Fat16); +} + + +#[test] +fn test_format_512mb() { + let total_bytes = 2 * 1024 * MB as u64; + let mut opts: fatfs::FormatOptions = Default::default(); + opts.total_sectors = (total_bytes / 512) as u32; + let fs = test_format_fs(opts, total_bytes); + assert_eq!(fs.fat_type(), fatfs::FatType::Fat32); +}