Properly initialize file allocation table when formatting a volume
This commit is contained in:
parent
447c9dda35
commit
b9753248f7
@ -11,7 +11,7 @@ use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileA
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN};
|
use dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN};
|
||||||
use file::File;
|
use file::File;
|
||||||
use fs::{DiskSlice, FileSystem, ReadWriteSeek};
|
use fs::{DiskSlice, FsIoAdapter, FileSystem, ReadWriteSeek};
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
type LfnUtf16 = Vec<u16>;
|
type LfnUtf16 = Vec<u16>;
|
||||||
@ -20,7 +20,7 @@ type LfnUtf16 = ();
|
|||||||
|
|
||||||
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
|
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
|
||||||
File(File<'a, T>),
|
File(File<'a, T>),
|
||||||
Root(DiskSlice<'a, T>),
|
Root(DiskSlice<FsIoAdapter<'a, T>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ReadWriteSeek> DirRawStream<'a, T> {
|
impl<'a, T: ReadWriteSeek> DirRawStream<'a, T> {
|
||||||
|
135
src/fs.rs
135
src/fs.rs
@ -788,7 +788,8 @@ impl<T: ReadWriteSeek> FileSystem<T> {
|
|||||||
self.first_data_sector - self.root_dir_sectors,
|
self.first_data_sector - self.root_dir_sectors,
|
||||||
self.root_dir_sectors,
|
self.root_dir_sectors,
|
||||||
1,
|
1,
|
||||||
self,
|
&self.bpb,
|
||||||
|
FsIoAdapter { fs: self },
|
||||||
)),
|
)),
|
||||||
_ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, 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))
|
self.offset_from_sector(self.sector_from_cluster(cluser))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fat_slice<'b>(&'b self) -> DiskSlice<'b, T> {
|
fn fat_slice<'b>(&'b self) -> DiskSlice<FsIoAdapter<'b, T>> {
|
||||||
let sectors_per_fat = self.bpb.sectors_per_fat();
|
let io = FsIoAdapter {
|
||||||
let mirroring_enabled = self.bpb.mirroring_enabled();
|
fs: self,
|
||||||
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)
|
|
||||||
};
|
};
|
||||||
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();
|
let disk_slice = self.fat_slice();
|
||||||
ClusterIterator::new(disk_slice, self.fat_type, cluster)
|
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,
|
begin: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
mirrors: u8,
|
mirrors: u8,
|
||||||
fs: &'a FileSystem<T>,
|
inner: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ReadWriteSeek> DiskSlice<'a, T> {
|
impl<T> DiskSlice<T> {
|
||||||
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: &'a FileSystem<T>) -> Self {
|
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, inner: T) -> Self {
|
||||||
DiskSlice {
|
DiskSlice {
|
||||||
begin,
|
begin,
|
||||||
size,
|
size,
|
||||||
mirrors,
|
mirrors,
|
||||||
fs,
|
inner,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, fs: &'a FileSystem<T>) -> Self {
|
fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, bpb: &BiosParameterBlock, inner: T) -> Self {
|
||||||
let bytes_per_sector = fs.bpb.bytes_per_sector as u64;
|
let bytes_per_sector = bpb.bytes_per_sector as u64;
|
||||||
Self::new(
|
Self::new(
|
||||||
first_sector as u64 * bytes_per_sector,
|
first_sector as u64 * bytes_per_sector,
|
||||||
sector_count as u64 * bytes_per_sector,
|
sector_count as u64 * bytes_per_sector,
|
||||||
mirrors,
|
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
|
// 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 {
|
fn clone(&self) -> Self {
|
||||||
DiskSlice {
|
DiskSlice {
|
||||||
begin: self.begin,
|
begin: self.begin,
|
||||||
size: self.size,
|
size: self.size,
|
||||||
offset: self.offset,
|
offset: self.offset,
|
||||||
mirrors: self.mirrors,
|
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> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let offset = self.begin + self.offset;
|
let offset = self.begin + self.offset;
|
||||||
let read_size = cmp::min((self.size - self.offset) as usize, buf.len());
|
let read_size = cmp::min((self.size - self.offset) as usize, buf.len());
|
||||||
let mut disk = self.fs.disk.borrow_mut();
|
self.inner.seek(SeekFrom::Start(offset))?;
|
||||||
disk.seek(SeekFrom::Start(offset))?;
|
let size = self.inner.read(&mut buf[..read_size])?;
|
||||||
let size = disk.read(&mut buf[..read_size])?;
|
|
||||||
self.offset += size as u64;
|
self.offset += size as u64;
|
||||||
Ok(size)
|
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> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
let offset = self.begin + self.offset;
|
let offset = self.begin + self.offset;
|
||||||
let write_size = cmp::min((self.size - self.offset) as usize, buf.len());
|
let write_size = cmp::min((self.size - self.offset) as usize, buf.len());
|
||||||
if write_size == 0 {
|
if write_size == 0 {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
// Mark the volume 'dirty'
|
|
||||||
self.fs.set_dirty_flag(true)?;
|
|
||||||
// Write data
|
// Write data
|
||||||
let mut disk = self.fs.disk.borrow_mut();
|
|
||||||
for i in 0..self.mirrors {
|
for i in 0..self.mirrors {
|
||||||
disk.seek(SeekFrom::Start(offset + i as u64 * self.size))?;
|
self.inner.seek(SeekFrom::Start(offset + i as u64 * self.size))?;
|
||||||
disk.write_all(&buf[..write_size])?;
|
self.inner.write_all(&buf[..write_size])?;
|
||||||
}
|
}
|
||||||
self.offset += write_size as u64;
|
self.offset += write_size as u64;
|
||||||
Ok(write_size)
|
Ok(write_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
let mut disk = self.fs.disk.borrow_mut();
|
self.inner.flush()
|
||||||
disk.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> {
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
let new_offset = match pos {
|
let new_offset = match pos {
|
||||||
SeekFrom::Current(x) => self.offset as i64 + x,
|
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 sectors_per_fat: u32 = boot.bpb.sectors_per_fat();
|
||||||
let bytes_per_fat: u32 = sectors_per_fat * bytes_per_sector as u32;
|
let bytes_per_fat: u32 = sectors_per_fat * bytes_per_sector as u32;
|
||||||
let reserved_sectors = boot.bpb.reserved_sectors;
|
let reserved_sectors = boot.bpb.reserved_sectors;
|
||||||
let mut fat_pos = reserved_sectors as u64 * bytes_per_sector as u64;
|
let fat_pos = reserved_sectors as u64 * bytes_per_sector as u64;
|
||||||
for _ in 0..boot.bpb.fats {
|
disk.seek(SeekFrom::Start(fat_pos))?;
|
||||||
disk.seek(SeekFrom::Start(fat_pos))?;
|
write_zeros(&mut disk, bytes_per_fat as usize * boot.bpb.fats as usize)?;
|
||||||
write_zeros(&mut disk, bytes_per_fat as usize)?;
|
{
|
||||||
disk.seek(SeekFrom::Start(fat_pos))?;
|
let mut fat_slice = fat_slice(&mut disk, &boot.bpb);
|
||||||
format_fat(&mut disk, fat_type, boot.bpb.media)?;
|
format_fat(&mut fat_slice, fat_type, boot.bpb.media, bytes_per_fat, boot.bpb.total_clusters())?;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root directory
|
// 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))?;
|
disk.seek(SeekFrom::Start(root_dir_pos))?;
|
||||||
let root_dir_sectors: u32 = boot.bpb.root_dir_sectors();
|
let root_dir_sectors: u32 = boot.bpb.root_dir_sectors();
|
||||||
write_zeros(&mut disk, root_dir_sectors as usize * bytes_per_sector as usize)?;
|
write_zeros(&mut disk, root_dir_sectors as usize * bytes_per_sector as usize)?;
|
||||||
if fat_type == FatType::Fat32 {
|
if fat_type == FatType::Fat32 {
|
||||||
// FIXME: alloc_cluster needs FAT stream, not entire disk
|
let root_dir_first_cluster = {
|
||||||
let root_dir_first_cluster = alloc_cluster(&mut disk, fat_type, None, None, 1)?;
|
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);
|
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 first_data_sector = reserved_sectors as u32 + sectors_per_fat + root_dir_sectors;
|
||||||
let sectors_per_cluster = boot.bpb.sectors_per_cluster;
|
let sectors_per_cluster = boot.bpb.sectors_per_cluster;
|
||||||
|
68
src/table.rs
68
src/table.rs
@ -1,5 +1,6 @@
|
|||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
|
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
|
||||||
|
use core::cmp;
|
||||||
use io;
|
use io;
|
||||||
|
|
||||||
use fs::{FatType, FsStatusFlags, ReadSeek, ReadWriteSeek};
|
use fs::{FatType, FsStatusFlags, ReadSeek, ReadWriteSeek};
|
||||||
@ -26,6 +27,7 @@ enum FatValue {
|
|||||||
trait FatTrait {
|
trait FatTrait {
|
||||||
fn get_raw<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32>;
|
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 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 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 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>;
|
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 })
|
Ok(FsStatusFlags { dirty, io_error })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn format_fat<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, media: u8) -> io::Result<()> {
|
pub(crate) fn count_free_clusters<T: ReadSeek>(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
|
||||||
// Note: cannot use any seeking function here because fat parameter is not a disk slice
|
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 {
|
match fat_type {
|
||||||
FatType::Fat12 => {
|
FatType::Fat12 => {
|
||||||
fat.write_u8(media)?;
|
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)?;
|
fat.write_u32::<LittleEndian>(0xFFFFFFFF)?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Ok(())
|
// 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;
|
||||||
pub(crate) fn count_free_clusters<T: ReadSeek>(fat: &mut T, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
|
let end_cluster = bytes_per_fat * BITS_PER_BYTE / fat_type.bits_per_fat_entry();
|
||||||
let end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
|
for cluster in start_cluster..end_cluster {
|
||||||
match fat_type {
|
write_fat(fat, fat_type, cluster, FatValue::EndOfChain)?;
|
||||||
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 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 {
|
impl FatTrait for Fat12 {
|
||||||
@ -165,13 +181,17 @@ impl FatTrait for Fat12 {
|
|||||||
FatValue::EndOfChain => 0xFFF,
|
FatValue::EndOfChain => 0xFFF,
|
||||||
FatValue::Data(n) => n as u16,
|
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);
|
let fat_offset = cluster + (cluster / 2);
|
||||||
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
let old_packed = fat.read_u16::<LittleEndian>()?;
|
let old_packed = fat.read_u16::<LittleEndian>()?;
|
||||||
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
|
||||||
let new_packed = match cluster & 1 {
|
let new_packed = match cluster & 1 {
|
||||||
0 => (old_packed & 0xF000) | raw_val,
|
0 => (old_packed & 0xF000) | raw_val as u16,
|
||||||
_ => (old_packed & 0x000F) | (raw_val << 4),
|
_ => (old_packed & 0x000F) | ((raw_val as u16) << 4),
|
||||||
};
|
};
|
||||||
fat.write_u16::<LittleEndian>(new_packed)?;
|
fat.write_u16::<LittleEndian>(new_packed)?;
|
||||||
Ok(())
|
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))?;
|
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::Free => 0,
|
||||||
FatValue::Bad => 0xFFF7,
|
FatValue::Bad => 0xFFF7,
|
||||||
FatValue::EndOfChain => 0xFFFF,
|
FatValue::EndOfChain => 0xFFFF,
|
||||||
FatValue::Data(n) => n as u16,
|
FatValue::Data(n) => n as u16,
|
||||||
};
|
};
|
||||||
fat.write_u16::<LittleEndian>(raw_val)?;
|
Self::set_raw(fat, cluster, raw_value as u32)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<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<()> {
|
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> {
|
||||||
let old_reserved_bits = Self::get_raw(fat, cluster)? & 0xF0000000;
|
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 {
|
if value == FatValue::Free && cluster >= 0x0FFFFFF7 && cluster <= 0x0FFFFFFF {
|
||||||
// NOTE: it is technically allowed for them to store FAT chain loops,
|
// NOTE: it is technically allowed for them to store FAT chain loops,
|
||||||
@ -338,8 +367,7 @@ impl FatTrait for Fat32 {
|
|||||||
FatValue::Data(n) => n,
|
FatValue::Data(n) => n,
|
||||||
};
|
};
|
||||||
let raw_val = raw_val | old_reserved_bits; // must preserve original reserved values
|
let raw_val = raw_val | old_reserved_bits; // must preserve original reserved values
|
||||||
fat.write_u32::<LittleEndian>(raw_val)?;
|
Self::set_raw(fat, cluster, raw_val)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
|
fn find_free<T: ReadSeek>(fat: &mut T, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
|
||||||
|
@ -11,17 +11,9 @@ const KB: u32 = 1024;
|
|||||||
const MB: u32 = KB * 1024;
|
const MB: u32 = KB * 1024;
|
||||||
const TEST_STR: &str = "Hi there Rust programmer!\n";
|
const TEST_STR: &str = "Hi there Rust programmer!\n";
|
||||||
|
|
||||||
#[test]
|
type FileSystem = fatfs::FileSystem<BufStream<io::Cursor<Vec<u8>>>>;
|
||||||
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");
|
|
||||||
|
|
||||||
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 root_dir = fs.root_dir();
|
||||||
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
|
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
|
||||||
assert_eq!(entries.len(), 0);
|
assert_eq!(entries.len(), 0);
|
||||||
@ -33,3 +25,52 @@ fn test_format() {
|
|||||||
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
|
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
|
||||||
assert_eq!(entries.len(), 1);
|
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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user