Properly initialize file allocation table when formatting a volume

This commit is contained in:
Rafał Harabień 2018-12-04 00:06:41 +01:00 committed by Rafał
parent 447c9dda35
commit b9753248f7
4 changed files with 189 additions and 79 deletions

View File

@ -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<u16>;
@ -20,7 +20,7 @@ type LfnUtf16 = ();
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
File(File<'a, T>),
Root(DiskSlice<'a, T>),
Root(DiskSlice<FsIoAdapter<'a, T>>),
}
impl<'a, T: ReadWriteSeek> DirRawStream<'a, T> {

135
src/fs.rs
View File

@ -788,7 +788,8 @@ impl<T: ReadWriteSeek> FileSystem<T> {
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<T: ReadWriteSeek> FileSystem<T> {
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<FsIoAdapter<'b, T>> {
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<DiskSlice<'b, T>> {
pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator<DiskSlice<FsIoAdapter<'b, T>>> {
let disk_slice = self.fat_slice();
ClusterIterator::new(disk_slice, self.fat_type, cluster)
}
@ -946,32 +941,84 @@ impl<T: ReadWriteSeek> Drop for FileSystem<T> {
}
}
pub(crate) struct DiskSlice<'a, T: ReadWriteSeek + 'a> {
pub(crate) struct FsIoAdapter<'a, T: ReadWriteSeek + 'a> {
fs: &'a FileSystem<T>,
}
impl<'a, T: ReadWriteSeek> Read for FsIoAdapter<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.fs.disk.borrow_mut().read(buf)
}
}
impl<'a, T: ReadWriteSeek> Write for FsIoAdapter<'a, T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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<u64> {
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<T: ReadWriteSeek>(io: T, bpb: &BiosParameterBlock) -> DiskSlice<T> {
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<T> {
begin: u64,
size: u64,
offset: u64,
mirrors: u8,
fs: &'a FileSystem<T>,
inner: T,
}
impl<'a, T: ReadWriteSeek> DiskSlice<'a, T> {
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: &'a FileSystem<T>) -> Self {
impl<T> DiskSlice<T> {
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<T>) -> 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<T: Clone> Clone for DiskSlice<T> {
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<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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<T> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let new_offset = match pos {
SeekFrom::Current(x) => self.offset as i64 + x,
@ -1325,25 +1367,24 @@ pub fn format_volume<T: ReadWriteSeek>(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;

View File

@ -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<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32>;
fn get<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<FatValue>;
fn set_raw<T: ReadWriteSeek>(fat: &mut T, cluster: u32, raw_value: u32) -> io::Result<()>;
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()>;
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<u32>;
fn count_free<T: ReadSeek>(fat: &mut T, end_cluster: u32) -> io::Result<u32>;
@ -109,8 +111,17 @@ pub(crate) fn read_fat_flags<T: ReadSeek>(fat: &mut T, fat_type: FatType) -> io:
Ok(FsStatusFlags { dirty, io_error })
}
pub(crate) fn format_fat<T: ReadWriteSeek>(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<T: ReadSeek>(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
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<T: ReadWriteSeek>(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<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, media
fat.write_u32::<LittleEndian>(0xFFFFFFFF)?;
},
};
Ok(())
}
pub(crate) fn count_free_clusters<T: ReadSeek>(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
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<T: ReadWriteSeek>(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::<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),
0 => (old_packed & 0xF000) | raw_val as u16,
_ => (old_packed & 0x000F) | ((raw_val as u16) << 4),
};
fat.write_u16::<LittleEndian>(new_packed)?;
Ok(())
@ -248,16 +268,20 @@ impl FatTrait for Fat16 {
})
}
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> {
fn set_raw<T: ReadWriteSeek>(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::<LittleEndian>(raw_value as u16)?;
Ok(())
}
fn set<T: ReadWriteSeek>(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::<LittleEndian>(raw_val)?;
Ok(())
Self::set_raw(fat, cluster, raw_value as u32)
}
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
@ -317,9 +341,14 @@ impl FatTrait for Fat32 {
})
}
fn set_raw<T: ReadWriteSeek>(fat: &mut T, cluster: u32, raw_value: u32) -> io::Result<()> {
fat.seek(io::SeekFrom::Start((cluster * 4) as u64))?;
fat.write_u32::<LittleEndian>(raw_value)?;
Ok(())
}
fn set<T: ReadWriteSeek>(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::<LittleEndian>(raw_val)?;
Ok(())
Self::set_raw(fat, cluster, raw_val)
}
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {

View File

@ -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<u8> = 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<BufStream<io::Cursor<Vec<u8>>>>;
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::<Vec<_>>();
assert_eq!(entries.len(), 0);
@ -33,3 +25,52 @@ fn test_format() {
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
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<u8> = 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);
}