Make disk object type generic (breaking change)

This change allows for moving an object ownership to FileSystem object
instead of borrowing it. It makes usage of library easier in some cases.
Unfortunately it is a breaking change.
This commit is contained in:
Rafał Harabień 2018-06-13 23:24:08 +02:00
parent b1894a435e
commit 217b6046f1
8 changed files with 176 additions and 122 deletions

View File

@ -33,8 +33,8 @@ Put this in your crate root:
You can start using library now:
let img_file = File::open("fat.img")?;
let mut buf_stream = fatfs::BufStream::new(img_file);
let fs = fatfs::FileSystem::new(&mut buf_stream, fatfs::FsOptions::new())?;
let buf_stream = fatfs::BufStream::new(img_file);
let fs = fatfs::FileSystem::new(buf_stream, fatfs::FsOptions::new())?;
let root_dir = fs.root_dir();
let mut file = root_dir.create_file("hello.txt")?;
file.write_all(b"Hello World!")?;

View File

@ -6,7 +6,7 @@ use io::prelude::*;
use io;
use io::{ErrorKind, SeekFrom};
use fs::{FileSystemRef, DiskSlice};
use fs::{FileSystem, DiskSlice, ReadWriteSeek};
use file::File;
use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes, ShortName, DIR_ENTRY_SIZE};
@ -16,13 +16,12 @@ use dir_entry::{LFN_PART_LEN, LFN_ENTRY_LAST_FLAG};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::Vec;
#[derive(Clone)]
pub(crate) enum DirRawStream<'a, 'b: 'a> {
File(File<'a, 'b>),
Root(DiskSlice<'a, 'b>),
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
File(File<'a, T>),
Root(DiskSlice<'a, T>),
}
impl <'a, 'b> DirRawStream<'a, 'b> {
impl <'a, T: ReadWriteSeek> DirRawStream<'a, T> {
fn abs_pos(&self) -> Option<u64> {
match self {
&DirRawStream::File(ref file) => file.abs_pos(),
@ -38,7 +37,17 @@ impl <'a, 'b> DirRawStream<'a, 'b> {
}
}
impl <'a, 'b> Read for DirRawStream<'a, 'b> {
// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
impl <'a, T: ReadWriteSeek> Clone for DirRawStream<'a, T> {
fn clone(&self) -> Self {
match self {
&DirRawStream::File(ref file) => DirRawStream::File(file.clone()),
&DirRawStream::Root(ref raw) => DirRawStream::Root(raw.clone()),
}
}
}
impl <'a, T: ReadWriteSeek> Read for DirRawStream<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
&mut DirRawStream::File(ref mut file) => file.read(buf),
@ -47,7 +56,7 @@ impl <'a, 'b> Read for DirRawStream<'a, 'b> {
}
}
impl <'a, 'b> Write for DirRawStream<'a, 'b> {
impl <'a, T: ReadWriteSeek> Write for DirRawStream<'a, T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
&mut DirRawStream::File(ref mut file) => file.write(buf),
@ -62,7 +71,7 @@ impl <'a, 'b> Write for DirRawStream<'a, 'b> {
}
}
impl <'a, 'b> Seek for DirRawStream<'a, 'b> {
impl <'a, T: ReadWriteSeek> Seek for DirRawStream<'a, T> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match self {
&mut DirRawStream::File(ref mut file) => file.seek(pos),
@ -80,19 +89,19 @@ fn split_path<'c>(path: &'c str) -> (&'c str, Option<&'c str>) {
}
/// FAT directory
#[derive(Clone)]
pub struct Dir<'a, 'b: 'a> {
stream: DirRawStream<'a, 'b>,
fs: FileSystemRef<'a, 'b>,
pub struct Dir<'a, T: ReadWriteSeek + 'a> {
stream: DirRawStream<'a, T>,
fs: &'a FileSystem<T>,
}
impl <'a, 'b> Dir<'a, 'b> {
pub(crate) fn new(stream: DirRawStream<'a, 'b>, fs: FileSystemRef<'a, 'b>) -> Self {
impl <'a, T: ReadWriteSeek + 'a> Dir<'a, T> {
pub(crate) fn new(stream: DirRawStream<'a, T>, fs: &'a FileSystem<T>) -> Self {
Dir { stream, fs }
}
/// Creates directory entries iterator
pub fn iter(&self) -> DirIter<'a, 'b> {
pub fn iter(&self) -> DirIter<'a, T> {
self.stream.clone();
DirIter {
stream: self.stream.clone(),
fs: self.fs.clone(),
@ -100,7 +109,7 @@ impl <'a, 'b> Dir<'a, 'b> {
}
}
fn find_entry(&self, name: &str, is_dir: Option<bool>, mut short_name_gen: Option<&mut ShortNameGenerator>) -> io::Result<DirEntry<'a, 'b>> {
fn find_entry(&self, name: &str, is_dir: Option<bool>, mut short_name_gen: Option<&mut ShortNameGenerator>) -> io::Result<DirEntry<'a, T>> {
for r in self.iter() {
let e = r?;
// compare name ignoring case
@ -130,7 +139,7 @@ impl <'a, 'b> Dir<'a, 'b> {
}
/// Opens existing file.
pub fn open_file(&self, path: &str) -> io::Result<File<'a, 'b>> {
pub fn open_file(&self, path: &str) -> io::Result<File<'a, T>> {
// traverse path
let (name, rest_opt) = split_path(path);
if let Some(rest) = rest_opt {
@ -143,7 +152,7 @@ impl <'a, 'b> Dir<'a, 'b> {
}
/// Creates new file or opens existing without truncating.
pub fn create_file(&self, path: &str) -> io::Result<File<'a, 'b>> {
pub fn create_file(&self, path: &str) -> io::Result<File<'a, T>> {
// traverse path
let (name, rest_opt) = split_path(path);
if let Some(rest) = rest_opt {
@ -255,7 +264,7 @@ impl <'a, 'b> Dir<'a, 'b> {
/// Destination directory can be cloned source directory in case of rename without moving operation.
/// Make sure there is no reference to this file (no File instance) or filesystem corruption
/// can happen.
pub fn rename(&self, src_path: &str, dst_dir: &Dir, dst_path: &str) -> io::Result<()> {
pub fn rename(&self, src_path: &str, dst_dir: &Dir<T>, dst_path: &str) -> io::Result<()> {
// traverse source path
let (name, rest_opt) = split_path(src_path);
if let Some(rest) = rest_opt {
@ -272,7 +281,7 @@ impl <'a, 'b> Dir<'a, 'b> {
self.rename_internal(src_path, dst_dir, dst_path)
}
fn rename_internal(&self, src_name: &str, dst_dir: &Dir, dst_name: &str) -> io::Result<()> {
fn rename_internal(&self, src_name: &str, dst_dir: &Dir<T>, dst_name: &str) -> io::Result<()> {
trace!("moving {} to {}", src_name, dst_name);
// find existing file
let e = self.find_entry(src_name, None, None)?;
@ -300,7 +309,7 @@ impl <'a, 'b> Dir<'a, 'b> {
Ok(())
}
fn find_free_entries(&self, num_entries: usize) -> io::Result<DirRawStream<'a, 'b>> {
fn find_free_entries(&self, num_entries: usize) -> io::Result<DirRawStream<'a, T>> {
let mut stream = self.stream.clone();
let mut first_free = 0;
let mut num_free = 0;
@ -334,7 +343,7 @@ impl <'a, 'b> Dir<'a, 'b> {
}
#[cfg(feature = "alloc")]
fn create_lfn_entries(&self, name: &str, short_name: &[u8]) -> io::Result<(DirRawStream<'a, 'b>, u64)> {
fn create_lfn_entries(&self, name: &str, short_name: &[u8]) -> io::Result<(DirRawStream<'a, T>, u64)> {
// get short name checksum
let lfn_chsum = lfn_checksum(&short_name);
// convert long name to UTF-16
@ -351,7 +360,7 @@ impl <'a, 'b> Dir<'a, 'b> {
Ok((stream, start_pos))
}
#[cfg(not(feature = "alloc"))]
fn create_lfn_entries(&self, _name: &str, _short_name: &[u8]) -> io::Result<(DirRawStream<'a, 'b>, u64)> {
fn create_lfn_entries(&self, _name: &str, _short_name: &[u8]) -> io::Result<(DirRawStream<'a, T>, u64)> {
let mut stream = self.find_free_entries(1)?;
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
Ok((stream, start_pos))
@ -366,7 +375,7 @@ impl <'a, 'b> Dir<'a, 'b> {
raw_entry
}
fn write_entry(&self, name: &str, raw_entry: DirFileEntryData) -> io::Result<DirEntry<'a, 'b>> {
fn write_entry(&self, name: &str, raw_entry: DirFileEntryData) -> io::Result<DirEntry<'a, T>> {
trace!("write_entry {}", name);
// check if name doesn't contain unsupported characters
validate_long_name(name)?;
@ -390,16 +399,25 @@ impl <'a, 'b> Dir<'a, 'b> {
}
}
// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
impl <'a, T: ReadWriteSeek> Clone for Dir<'a, T> {
fn clone(&self) -> Self {
Self {
stream: self.stream.clone(),
fs: self.fs,
}
}
}
/// Directory entries iterator.
#[derive(Clone)]
pub struct DirIter<'a, 'b: 'a> {
stream: DirRawStream<'a, 'b>,
fs: FileSystemRef<'a, 'b>,
pub struct DirIter<'a, T: ReadWriteSeek + 'a> {
stream: DirRawStream<'a, T>,
fs: &'a FileSystem<T>,
err: bool,
}
impl <'a, 'b> DirIter<'a, 'b> {
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, 'b>>> {
impl <'a, T: ReadWriteSeek> DirIter<'a, T> {
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, T>>> {
#[cfg(feature = "alloc")]
let mut lfn_buf = LongNameBuilder::new();
let mut offset = self.stream.seek(SeekFrom::Current(0))?;
@ -454,8 +472,19 @@ impl <'a, 'b> DirIter<'a, 'b> {
}
}
impl <'a, 'b> Iterator for DirIter<'a, 'b> {
type Item = io::Result<DirEntry<'a, 'b>>;
// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
impl <'a, T: ReadWriteSeek> Clone for DirIter<'a, T> {
fn clone(&self) -> Self {
Self {
stream: self.stream.clone(),
fs: self.fs,
err: self.err,
}
}
}
impl <'a, T: ReadWriteSeek> Iterator for DirIter<'a, T> {
type Item = io::Result<DirEntry<'a, T>>;
fn next(&mut self) -> Option<Self::Item> {
if self.err {

View File

@ -13,7 +13,7 @@ use chrono;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{Vec, String, string::ToString};
use fs::{FileSystemRef, FatType};
use fs::{FileSystem, FatType, ReadWriteSeek};
use file::File;
use dir::{Dir, DirRawStream};
@ -589,7 +589,7 @@ impl DirEntryEditor {
self.dirty = true;
}
pub(crate) fn flush(&mut self, fs: FileSystemRef) -> io::Result<()> {
pub(crate) fn flush<T: ReadWriteSeek>(&mut self, fs: &FileSystem<T>) -> io::Result<()> {
if self.dirty {
self.write(fs)?;
self.dirty = false;
@ -597,7 +597,7 @@ impl DirEntryEditor {
Ok(())
}
fn write(&self, fs: FileSystemRef) -> io::Result<()> {
fn write<T: ReadWriteSeek>(&self, fs: &FileSystem<T>) -> io::Result<()> {
let mut disk = fs.disk.borrow_mut();
disk.seek(io::SeekFrom::Start(self.pos))?;
self.data.serialize(&mut *disk)
@ -608,17 +608,17 @@ impl DirEntryEditor {
///
/// Returned by DirIter.
#[derive(Clone)]
pub struct DirEntry<'a, 'b: 'a> {
pub struct DirEntry<'a, T: ReadWriteSeek + 'a> {
pub(crate) data: DirFileEntryData,
pub(crate) short_name: ShortName,
#[cfg(feature = "alloc")]
pub(crate) lfn: Vec<u16>,
pub(crate) entry_pos: u64,
pub(crate) offset_range: (u64, u64),
pub(crate) fs: FileSystemRef<'a, 'b>,
pub(crate) fs: &'a FileSystem<T>,
}
impl <'a, 'b> DirEntry<'a, 'b> {
impl <'a, T: ReadWriteSeek> DirEntry<'a, T> {
/// Returns short file name
#[cfg(feature = "alloc")]
pub fn short_file_name(&self) -> String {
@ -669,7 +669,7 @@ impl <'a, 'b> DirEntry<'a, 'b> {
/// Returns File struct for this entry.
///
/// Panics if this is not a file.
pub fn to_file(&self) -> File<'a, 'b> {
pub fn to_file(&self) -> File<'a, T> {
assert!(!self.is_dir(), "Not a file entry");
File::new(self.first_cluster(), Some(self.editor()), self.fs)
}
@ -677,7 +677,7 @@ impl <'a, 'b> DirEntry<'a, 'b> {
/// Returns Dir struct for this entry.
///
/// Panics if this is not a directory.
pub fn to_dir(&self) -> Dir<'a, 'b> {
pub fn to_dir(&self) -> Dir<'a, T> {
assert!(self.is_dir(), "Not a directory entry");
match self.first_cluster() {
Some(n) => {
@ -713,7 +713,7 @@ impl <'a, 'b> DirEntry<'a, 'b> {
}
}
impl <'a, 'b> fmt::Debug for DirEntry<'a, 'b> {
impl <'a, T: ReadWriteSeek> fmt::Debug for DirEntry<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.data.fmt(f)
}

View File

@ -4,14 +4,13 @@ use io::prelude::*;
use io::{SeekFrom, ErrorKind};
use io;
use fs::FileSystemRef;
use fs::{FileSystem, ReadWriteSeek};
use dir_entry::{DirEntryEditor, DateTime, Date};
const MAX_FILE_SIZE: u32 = core::u32::MAX;
/// FAT file used for reading and writing.
#[derive(Clone)]
pub struct File<'a, 'b: 'a> {
pub struct File<'a, T: ReadWriteSeek + 'a> {
// Note first_cluster is None if file is empty
first_cluster: Option<u32>,
// Note: if offset points between clusters current_cluster is the previous cluster
@ -21,11 +20,11 @@ pub struct File<'a, 'b: 'a> {
// file dir entry editor - None for root dir
entry: Option<DirEntryEditor>,
// file-system reference
fs: FileSystemRef<'a, 'b>,
fs: &'a FileSystem<T>,
}
impl <'a, 'b> File<'a, 'b> {
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: FileSystemRef<'a, 'b>) -> Self {
impl <'a, T: ReadWriteSeek> File<'a, T> {
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: &'a FileSystem<T>) -> Self {
File {
first_cluster, entry, fs,
current_cluster: None, // cluster before first one
@ -132,7 +131,7 @@ impl <'a, 'b> File<'a, 'b> {
}
}
impl<'a, 'b> Drop for File<'a, 'b> {
impl<'a, T: ReadWriteSeek> Drop for File<'a, T> {
fn drop(&mut self) {
if let Err(err) = self.flush() {
error!("flush failed {}", err);
@ -140,7 +139,20 @@ impl<'a, 'b> Drop for File<'a, 'b> {
}
}
impl<'a, 'b> Read for File<'a, 'b> {
// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
impl <'a, T: ReadWriteSeek> Clone for File<'a, T> {
fn clone(&self) -> Self {
File {
first_cluster: self.first_cluster,
current_cluster: self.current_cluster,
offset: self.offset,
entry: self.entry.clone(),
fs: self.fs,
}
}
}
impl<'a, T: ReadWriteSeek> Read for File<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let cluster_size = self.fs.cluster_size();
let current_cluster_opt = if self.offset % cluster_size == 0 {
@ -191,7 +203,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
}
}
impl<'a, 'b> Write for File<'a, 'b> {
impl<'a, T: ReadWriteSeek> Write for File<'a, T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let cluster_size = self.fs.cluster_size();
let offset_in_cluster = self.offset % cluster_size;
@ -271,7 +283,7 @@ impl<'a, 'b> Write for File<'a, 'b> {
}
}
impl<'a, 'b> Seek for File<'a, 'b> {
impl<'a, T: ReadWriteSeek> Seek for File<'a, T> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let mut new_pos = match pos {
SeekFrom::Current(x) => self.offset as i64 + x,

View File

@ -100,7 +100,7 @@ struct BiosParameterBlock {
}
impl BiosParameterBlock {
fn deserialize(rdr: &mut Read) -> io::Result<BiosParameterBlock> {
fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BiosParameterBlock> {
let mut bpb: BiosParameterBlock = Default::default();
bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?;
bpb.sectors_per_cluster = rdr.read_u8()?;
@ -188,7 +188,7 @@ struct BootRecord {
}
impl BootRecord {
fn deserialize(rdr: &mut Read) -> io::Result<BootRecord> {
fn deserialize<T: Read>(rdr: &mut T) -> io::Result<BootRecord> {
let mut boot: BootRecord = Default::default();
rdr.read_exact(&mut boot.bootjmp)?;
rdr.read_exact(&mut boot.oem_name)?;
@ -228,7 +228,7 @@ impl FsInfoSector {
const STRUC_SIG: u32 = 0x61417272;
const TRAIL_SIG: u32 = 0xAA550000;
fn deserialize(rdr: &mut Read) -> io::Result<FsInfoSector> {
fn deserialize<T: Read>(rdr: &mut T) -> io::Result<FsInfoSector> {
let lead_sig = rdr.read_u32::<LittleEndian>()?;
if lead_sig != Self::LEAD_SIG {
return Err(Error::new(ErrorKind::Other, "invalid lead_sig in FsInfo sector"));
@ -259,7 +259,7 @@ impl FsInfoSector {
})
}
fn serialize(&self, wrt: &mut Write) -> io::Result<()> {
fn serialize<T: Write>(&self, wrt: &mut T) -> io::Result<()> {
wrt.write_u32::<LittleEndian>(Self::LEAD_SIG)?;
let reserved = [0u8; 480];
wrt.write(&reserved)?;
@ -343,11 +343,9 @@ impl FileSystemStats {
}
}
pub(crate) type FileSystemRef<'a, 'b> = &'a FileSystem<'b>;
/// FAT filesystem main struct.
pub struct FileSystem<'a> {
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
pub struct FileSystem<T: ReadWriteSeek> {
pub(crate) disk: RefCell<T>,
pub(crate) options: FsOptions,
fat_type: FatType,
bpb: BiosParameterBlock,
@ -357,7 +355,7 @@ pub struct FileSystem<'a> {
fs_info: RefCell<FsInfoSector>,
}
impl <'a> FileSystem<'a> {
impl <T: ReadWriteSeek> FileSystem<T> {
/// Creates new filesystem object instance.
///
/// Supplied disk parameter cannot be seeked. If there is a need to read a fragment of disk image (e.g. partition)
@ -365,13 +363,13 @@ impl <'a> FileSystem<'a> {
///
/// Note: creating multiple filesystem objects with one underlying device/disk image can
/// cause filesystem corruption.
pub fn new<T: ReadWriteSeek>(disk: &'a mut T, options: FsOptions) -> io::Result<Self> {
// make sure given image is not seeked
pub fn new(mut disk: T, options: FsOptions) -> io::Result<Self> {
// Make sure given image is not seeked
debug_assert!(disk.seek(SeekFrom::Current(0))? == 0);
// read boot sector
let bpb = {
let boot = BootRecord::deserialize(disk)?;
let boot = BootRecord::deserialize(&mut disk)?;
if boot.boot_sig != [0x55, 0xAA] {
return Err(Error::new(ErrorKind::Other, "invalid signature"));
}
@ -395,7 +393,7 @@ impl <'a> FileSystem<'a> {
// read FSInfo sector if this is FAT32
let mut fs_info = if fat_type == FatType::Fat32 {
disk.seek(SeekFrom::Start(bpb.fs_info_sector as u64 * 512))?;
FsInfoSector::deserialize(disk)?
FsInfoSector::deserialize(&mut disk)?
} else {
FsInfoSector::default()
};
@ -442,7 +440,7 @@ impl <'a> FileSystem<'a> {
}
/// Returns root directory object allowing futher penetration of filesystem structure.
pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
pub fn root_dir<'b>(&'b self) -> Dir<'b, T> {
let root_rdr = {
match self.fat_type {
FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
@ -469,7 +467,7 @@ impl <'a> FileSystem<'a> {
self.offset_from_sector(self.sector_from_cluster(cluser))
}
fn fat_slice<'b>(&'b self) -> DiskSlice<'b, 'a> {
fn fat_slice<'b>(&'b self) -> DiskSlice<'b, T> {
let sectors_per_fat =
if self.bpb.sectors_per_fat_16 == 0 { self.bpb.sectors_per_fat_32 }
else { self.bpb.sectors_per_fat_16 as u32 };
@ -484,7 +482,7 @@ impl <'a> FileSystem<'a> {
DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, self)
}
pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator<'b, 'a> {
pub(crate) fn cluster_iter<'b>(&'b self, cluster: u32) -> ClusterIterator<DiskSlice<'b, T>> {
let disk_slice = self.fat_slice();
ClusterIterator::new(disk_slice, self.fat_type, cluster)
}
@ -574,7 +572,7 @@ impl <'a> FileSystem<'a> {
}
}
impl<'a> Drop for FileSystem<'a> {
impl<T: ReadWriteSeek> Drop for FileSystem<T> {
fn drop(&mut self) {
if let Err(err) = self.unmount_internal() {
error!("unmount failed {}", err);
@ -582,21 +580,20 @@ impl<'a> Drop for FileSystem<'a> {
}
}
#[derive(Clone)]
pub(crate) struct DiskSlice<'a, 'b: 'a> {
pub(crate) struct DiskSlice<'a, T: ReadWriteSeek + 'a> {
begin: u64,
size: u64,
offset: u64,
mirrors: u8,
fs: &'a FileSystem<'b>,
fs: &'a FileSystem<T>,
}
impl <'a, 'b> DiskSlice<'a, 'b> {
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: FileSystemRef<'a, 'b>) -> Self {
impl <'a, T: ReadWriteSeek> DiskSlice<'a, T> {
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: &'a FileSystem<T>) -> Self {
DiskSlice { begin, size, mirrors, fs, offset: 0 }
}
pub(crate) fn from_sectors(first_sector: u32, sector_count: u32, mirrors: u8, fs: FileSystemRef<'a, 'b>) -> Self {
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;
Self::new(first_sector as u64 * bytes_per_sector, sector_count as u64 * bytes_per_sector, mirrors, fs)
}
@ -606,7 +603,20 @@ impl <'a, 'b> DiskSlice<'a, 'b> {
}
}
impl <'a, 'b> Read for DiskSlice<'a, 'b> {
// 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> {
fn clone(&self) -> Self {
DiskSlice {
begin: self.begin,
size: self.size,
offset: self.offset,
mirrors: self.mirrors,
fs: self.fs,
}
}
}
impl <'a, T: ReadWriteSeek> Read for DiskSlice<'a, 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());
@ -618,7 +628,7 @@ impl <'a, 'b> Read for DiskSlice<'a, 'b> {
}
}
impl <'a, 'b> Write for DiskSlice<'a, 'b> {
impl <'a, T: ReadWriteSeek> Write for DiskSlice<'a, 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());
@ -637,7 +647,7 @@ impl <'a, 'b> Write for DiskSlice<'a, 'b> {
}
}
impl <'a, 'b> Seek for DiskSlice<'a, 'b> {
impl <'a, T: ReadWriteSeek> Seek for DiskSlice<'a, T> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let new_offset = match pos {
SeekFrom::Current(x) => self.offset as i64 + x,

View File

@ -1,8 +1,7 @@
use io;
use io::prelude::*;
use byteorder::LittleEndian;
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
use fs::{FatType, FsStatusFlags, DiskSlice, ReadSeek};
use fs::{ReadWriteSeek, ReadSeek, FatType, FsStatusFlags};
struct Fat<T> {
#[allow(dead_code)]
@ -24,14 +23,14 @@ enum FatValue {
}
trait FatTrait {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
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, start_cluster: u32, end_cluster: u32) -> io::Result<u32>;
fn count_free(fat: &mut ReadSeek, end_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 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>;
}
fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
fn read_fat<T: ReadSeek>(fat: &mut T, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
match fat_type {
FatType::Fat12 => Fat12::get(fat, cluster),
FatType::Fat16 => Fat16::get(fat, cluster),
@ -39,7 +38,7 @@ fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<F
}
}
fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
fn write_fat<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
trace!("write FAT - cluster {} value {:?}", cluster, value);
match fat_type {
FatType::Fat12 => Fat12::set(fat, cluster, value),
@ -48,7 +47,7 @@ fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatVal
}
}
fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
fn get_next_cluster<T: ReadSeek>(fat: &mut T, 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)),
@ -56,7 +55,7 @@ fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::
}
}
fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
fn find_free_cluster<T: ReadSeek>(fat: &mut T, fat_type: FatType, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
match fat_type {
FatType::Fat12 => Fat12::find_free(fat, start_cluster, end_cluster),
FatType::Fat16 => Fat16::find_free(fat, start_cluster, end_cluster),
@ -64,7 +63,7 @@ fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, start_cluster: u32,
}
}
pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster: Option<u32>, hint: Option<u32>, total_clusters: u32) -> io::Result<u32> {
pub(crate) fn alloc_cluster<T: ReadWriteSeek>(fat: &mut T, fat_type: FatType, prev_cluster: Option<u32>, hint: Option<u32>, total_clusters: u32) -> io::Result<u32> {
let end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
let start_cluster = match hint {
Some(n) if n < end_cluster => n,
@ -83,7 +82,7 @@ pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster
Ok(new_cluster)
}
pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
pub(crate) fn read_fat_flags<T: ReadSeek>(fat: &mut T, fat_type: FatType) -> io::Result<FsStatusFlags> {
// check MSB (except in FAT12)
let val = match fat_type {
FatType::Fat12 => 0xFFF,
@ -105,7 +104,7 @@ pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Resu
})
}
pub(crate) fn count_free_clusters(fat: &mut ReadSeek, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
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),
@ -115,7 +114,7 @@ pub(crate) fn count_free_clusters(fat: &mut ReadSeek, fat_type: FatType, total_c
}
impl FatTrait for Fat12 {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
fn get_raw<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32> {
let fat_offset = cluster + (cluster / 2);
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
let packed_val = fat.read_u16::<LittleEndian>()?;
@ -125,7 +124,7 @@ impl FatTrait for Fat12 {
} as u32)
}
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
fn get<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<FatValue> {
let val = Self::get_raw(fat, cluster)?;
Ok(match val {
0 => FatValue::Free,
@ -135,7 +134,7 @@ impl FatTrait for Fat12 {
})
}
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> {
let raw_val = match value {
FatValue::Free => 0,
FatValue::Bad => 0xFF7,
@ -154,7 +153,7 @@ impl FatTrait for Fat12 {
Ok(())
}
fn find_free(fat: &mut ReadSeek, 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> {
let mut cluster = start_cluster;
let fat_offset = cluster + (cluster / 2);
fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
@ -181,7 +180,7 @@ impl FatTrait for Fat12 {
}
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
fn count_free<T: ReadSeek>(fat: &mut T, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*3/2) as u64))?;
@ -211,12 +210,12 @@ impl FatTrait for Fat12 {
}
impl FatTrait for Fat16 {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
fn get_raw<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32> {
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
Ok(fat.read_u16::<LittleEndian>()? as u32)
}
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
fn get<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<FatValue> {
let val = Self::get_raw(fat, cluster)?;
Ok(match val {
0 => FatValue::Free,
@ -226,7 +225,7 @@ impl FatTrait for Fat16 {
})
}
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> {
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
let raw_val = match value {
FatValue::Free => 0,
@ -238,7 +237,7 @@ impl FatTrait for Fat16 {
Ok(())
}
fn find_free(fat: &mut ReadSeek, 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> {
let mut cluster = start_cluster;
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
while cluster < end_cluster {
@ -251,7 +250,7 @@ impl FatTrait for Fat16 {
Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"))
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
fn count_free<T: ReadSeek>(fat: &mut T, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
@ -269,12 +268,12 @@ impl FatTrait for Fat16 {
}
impl FatTrait for Fat32 {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
fn get_raw<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32> {
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
Ok(fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF)
}
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
fn get<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<FatValue> {
let val = Self::get_raw(fat, cluster)?;
Ok(match val {
0 => FatValue::Free,
@ -284,7 +283,7 @@ impl FatTrait for Fat32 {
})
}
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()> {
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
let raw_val = match value {
FatValue::Free => 0,
@ -296,7 +295,7 @@ impl FatTrait for Fat32 {
Ok(())
}
fn find_free(fat: &mut ReadSeek, 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> {
let mut cluster = start_cluster;
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
while cluster < end_cluster {
@ -309,7 +308,7 @@ impl FatTrait for Fat32 {
Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"))
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
fn count_free<T: ReadSeek>(fat: &mut T, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
@ -326,15 +325,15 @@ impl FatTrait for Fat32 {
}
}
pub(crate) struct ClusterIterator<'a, 'b: 'a> {
fat: DiskSlice<'a, 'b>,
pub(crate) struct ClusterIterator<T: ReadWriteSeek> {
fat: T,
fat_type: FatType,
cluster: Option<u32>,
err: bool,
}
impl <'a, 'b> ClusterIterator<'a, 'b> {
pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32) -> Self {
impl <T: ReadWriteSeek> ClusterIterator<T> {
pub(crate) fn new(fat: T, fat_type: FatType, cluster: u32) -> Self {
ClusterIterator {
fat,
fat_type,
@ -368,7 +367,7 @@ impl <'a, 'b> ClusterIterator<'a, 'b> {
}
}
impl <'a, 'b> Iterator for ClusterIterator<'a, 'b> {
impl <T: ReadWriteSeek> Iterator for ClusterIterator<T> {
type Item = io::Result<u32>;
fn next(&mut self) -> Option<Self::Item> {

View File

@ -6,24 +6,26 @@ use std::io::SeekFrom;
use std::io::prelude::*;
use std::str;
use fatfs::{FileSystem, FsOptions, FatType, DirEntry, BufStream};
use fatfs::{FsOptions, FatType, BufStream};
const TEST_TEXT: &str = "Rust is cool!\n";
const FAT12_IMG: &str = "resources/fat12.img";
const FAT16_IMG: &str = "resources/fat16.img";
const FAT32_IMG: &str = "resources/fat32.img";
type FileSystem = fatfs::FileSystem<BufStream<fs::File>>;
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) {
let _ = env_logger::try_init();
let file = fs::File::open(filename).unwrap();
let mut buf_file = BufStream::new(file);
let fs = FileSystem::new(&mut buf_file, FsOptions::new()).unwrap();
let buf_file = BufStream::new(file);
let fs = FileSystem::new(buf_file, FsOptions::new()).unwrap();
f(fs);
}
fn test_root_dir(fs: FileSystem) {
let root_dir = fs.root_dir();
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<DirEntry>>();
let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
let short_names = entries.iter().map(|e| e.short_file_name()).collect::<Vec<String>>();
assert_eq!(short_names, ["LONG.TXT", "SHORT.TXT", "VERY", "VERY-L~1"]);
let names = entries.iter().map(|e| e.file_name()).collect::<Vec<String>>();

View File

@ -6,7 +6,7 @@ use std::io::prelude::*;
use std::io;
use std::str;
use fatfs::{FileSystem, FsOptions, BufStream};
use fatfs::{FsOptions, BufStream};
const FAT12_IMG: &str = "fat12.img";
const FAT16_IMG: &str = "fat16.img";
@ -16,6 +16,8 @@ const TMP_DIR: &str = "tmp";
const TEST_STR: &str = "Hi there Rust programmer!\n";
const TEST_STR2: &str = "Rust is cool!\n";
type FileSystem = fatfs::FileSystem<BufStream<fs::File>>;
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str, test_seq: u32) {
let _ = env_logger::try_init();
let img_path = format!("{}/{}", IMG_DIR, filename);
@ -24,9 +26,9 @@ fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str, test_seq: u32) {
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 options = FsOptions::new().update_accessed_date(true).update_fs_info(true);
let fs = FileSystem::new(&mut buf_file, options).unwrap();
let buf_file = BufStream::new(file);
let options = FsOptions::new().update_accessed_date(true);
let fs = FileSystem::new(buf_file, options).unwrap();
f(fs);
}
fs::remove_file(tmp_path).unwrap();