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:
parent
b1894a435e
commit
217b6046f1
@ -33,8 +33,8 @@ Put this in your crate root:
|
|||||||
You can start using library now:
|
You can start using library now:
|
||||||
|
|
||||||
let img_file = File::open("fat.img")?;
|
let img_file = File::open("fat.img")?;
|
||||||
let mut buf_stream = fatfs::BufStream::new(img_file);
|
let buf_stream = fatfs::BufStream::new(img_file);
|
||||||
let fs = fatfs::FileSystem::new(&mut buf_stream, fatfs::FsOptions::new())?;
|
let fs = fatfs::FileSystem::new(buf_stream, fatfs::FsOptions::new())?;
|
||||||
let root_dir = fs.root_dir();
|
let root_dir = fs.root_dir();
|
||||||
let mut file = root_dir.create_file("hello.txt")?;
|
let mut file = root_dir.create_file("hello.txt")?;
|
||||||
file.write_all(b"Hello World!")?;
|
file.write_all(b"Hello World!")?;
|
||||||
|
95
src/dir.rs
95
src/dir.rs
@ -6,7 +6,7 @@ use io::prelude::*;
|
|||||||
use io;
|
use io;
|
||||||
use io::{ErrorKind, SeekFrom};
|
use io::{ErrorKind, SeekFrom};
|
||||||
|
|
||||||
use fs::{FileSystemRef, DiskSlice};
|
use fs::{FileSystem, DiskSlice, ReadWriteSeek};
|
||||||
use file::File;
|
use file::File;
|
||||||
use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes, ShortName, DIR_ENTRY_SIZE};
|
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"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::Vec;
|
use alloc::Vec;
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub(crate) enum DirRawStream<'a, 'b: 'a> {
|
File(File<'a, T>),
|
||||||
File(File<'a, 'b>),
|
Root(DiskSlice<'a, T>),
|
||||||
Root(DiskSlice<'a, 'b>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> DirRawStream<'a, 'b> {
|
impl <'a, T: ReadWriteSeek> DirRawStream<'a, T> {
|
||||||
fn abs_pos(&self) -> Option<u64> {
|
fn abs_pos(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
&DirRawStream::File(ref file) => file.abs_pos(),
|
&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> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
match self {
|
match self {
|
||||||
&mut DirRawStream::File(ref mut file) => file.read(buf),
|
&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> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
match self {
|
match self {
|
||||||
&mut DirRawStream::File(ref mut file) => file.write(buf),
|
&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> {
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
match self {
|
match self {
|
||||||
&mut DirRawStream::File(ref mut file) => file.seek(pos),
|
&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
|
/// FAT directory
|
||||||
#[derive(Clone)]
|
pub struct Dir<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub struct Dir<'a, 'b: 'a> {
|
stream: DirRawStream<'a, T>,
|
||||||
stream: DirRawStream<'a, 'b>,
|
fs: &'a FileSystem<T>,
|
||||||
fs: FileSystemRef<'a, 'b>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> Dir<'a, 'b> {
|
impl <'a, T: ReadWriteSeek + 'a> Dir<'a, T> {
|
||||||
pub(crate) fn new(stream: DirRawStream<'a, 'b>, fs: FileSystemRef<'a, 'b>) -> Self {
|
pub(crate) fn new(stream: DirRawStream<'a, T>, fs: &'a FileSystem<T>) -> Self {
|
||||||
Dir { stream, fs }
|
Dir { stream, fs }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates directory entries iterator
|
/// Creates directory entries iterator
|
||||||
pub fn iter(&self) -> DirIter<'a, 'b> {
|
pub fn iter(&self) -> DirIter<'a, T> {
|
||||||
|
self.stream.clone();
|
||||||
DirIter {
|
DirIter {
|
||||||
stream: self.stream.clone(),
|
stream: self.stream.clone(),
|
||||||
fs: self.fs.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() {
|
for r in self.iter() {
|
||||||
let e = r?;
|
let e = r?;
|
||||||
// compare name ignoring case
|
// compare name ignoring case
|
||||||
@ -130,7 +139,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Opens existing file.
|
/// 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
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
@ -143,7 +152,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new file or opens existing without truncating.
|
/// 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
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
if let Some(rest) = rest_opt {
|
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.
|
/// 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
|
/// Make sure there is no reference to this file (no File instance) or filesystem corruption
|
||||||
/// can happen.
|
/// 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
|
// traverse source path
|
||||||
let (name, rest_opt) = split_path(src_path);
|
let (name, rest_opt) = split_path(src_path);
|
||||||
if let Some(rest) = rest_opt {
|
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)
|
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);
|
trace!("moving {} to {}", src_name, dst_name);
|
||||||
// find existing file
|
// find existing file
|
||||||
let e = self.find_entry(src_name, None, None)?;
|
let e = self.find_entry(src_name, None, None)?;
|
||||||
@ -300,7 +309,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
Ok(())
|
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 stream = self.stream.clone();
|
||||||
let mut first_free = 0;
|
let mut first_free = 0;
|
||||||
let mut num_free = 0;
|
let mut num_free = 0;
|
||||||
@ -334,7 +343,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[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
|
// get short name checksum
|
||||||
let lfn_chsum = lfn_checksum(&short_name);
|
let lfn_chsum = lfn_checksum(&short_name);
|
||||||
// convert long name to UTF-16
|
// convert long name to UTF-16
|
||||||
@ -351,7 +360,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
Ok((stream, start_pos))
|
Ok((stream, start_pos))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "alloc"))]
|
#[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 mut stream = self.find_free_entries(1)?;
|
||||||
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
|
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
|
||||||
Ok((stream, start_pos))
|
Ok((stream, start_pos))
|
||||||
@ -366,7 +375,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
raw_entry
|
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);
|
trace!("write_entry {}", name);
|
||||||
// check if name doesn't contain unsupported characters
|
// check if name doesn't contain unsupported characters
|
||||||
validate_long_name(name)?;
|
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.
|
/// Directory entries iterator.
|
||||||
#[derive(Clone)]
|
pub struct DirIter<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub struct DirIter<'a, 'b: 'a> {
|
stream: DirRawStream<'a, T>,
|
||||||
stream: DirRawStream<'a, 'b>,
|
fs: &'a FileSystem<T>,
|
||||||
fs: FileSystemRef<'a, 'b>,
|
|
||||||
err: bool,
|
err: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> DirIter<'a, 'b> {
|
impl <'a, T: ReadWriteSeek> DirIter<'a, T> {
|
||||||
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, 'b>>> {
|
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, T>>> {
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
let mut lfn_buf = LongNameBuilder::new();
|
let mut lfn_buf = LongNameBuilder::new();
|
||||||
let mut offset = self.stream.seek(SeekFrom::Current(0))?;
|
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> {
|
// Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925
|
||||||
type Item = io::Result<DirEntry<'a, 'b>>;
|
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> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.err {
|
if self.err {
|
||||||
|
@ -13,7 +13,7 @@ use chrono;
|
|||||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::{Vec, String, string::ToString};
|
use alloc::{Vec, String, string::ToString};
|
||||||
|
|
||||||
use fs::{FileSystemRef, FatType};
|
use fs::{FileSystem, FatType, ReadWriteSeek};
|
||||||
use file::File;
|
use file::File;
|
||||||
use dir::{Dir, DirRawStream};
|
use dir::{Dir, DirRawStream};
|
||||||
|
|
||||||
@ -589,7 +589,7 @@ impl DirEntryEditor {
|
|||||||
self.dirty = true;
|
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 {
|
if self.dirty {
|
||||||
self.write(fs)?;
|
self.write(fs)?;
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
@ -597,7 +597,7 @@ impl DirEntryEditor {
|
|||||||
Ok(())
|
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();
|
let mut disk = fs.disk.borrow_mut();
|
||||||
disk.seek(io::SeekFrom::Start(self.pos))?;
|
disk.seek(io::SeekFrom::Start(self.pos))?;
|
||||||
self.data.serialize(&mut *disk)
|
self.data.serialize(&mut *disk)
|
||||||
@ -608,17 +608,17 @@ impl DirEntryEditor {
|
|||||||
///
|
///
|
||||||
/// Returned by DirIter.
|
/// Returned by DirIter.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DirEntry<'a, 'b: 'a> {
|
pub struct DirEntry<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub(crate) data: DirFileEntryData,
|
pub(crate) data: DirFileEntryData,
|
||||||
pub(crate) short_name: ShortName,
|
pub(crate) short_name: ShortName,
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub(crate) lfn: Vec<u16>,
|
pub(crate) lfn: Vec<u16>,
|
||||||
pub(crate) entry_pos: u64,
|
pub(crate) entry_pos: u64,
|
||||||
pub(crate) offset_range: (u64, 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
|
/// Returns short file name
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn short_file_name(&self) -> String {
|
pub fn short_file_name(&self) -> String {
|
||||||
@ -669,7 +669,7 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
|||||||
/// Returns File struct for this entry.
|
/// Returns File struct for this entry.
|
||||||
///
|
///
|
||||||
/// Panics if this is not a file.
|
/// 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");
|
assert!(!self.is_dir(), "Not a file entry");
|
||||||
File::new(self.first_cluster(), Some(self.editor()), self.fs)
|
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.
|
/// Returns Dir struct for this entry.
|
||||||
///
|
///
|
||||||
/// Panics if this is not a directory.
|
/// 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");
|
assert!(self.is_dir(), "Not a directory entry");
|
||||||
match self.first_cluster() {
|
match self.first_cluster() {
|
||||||
Some(n) => {
|
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> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
self.data.fmt(f)
|
self.data.fmt(f)
|
||||||
}
|
}
|
||||||
|
32
src/file.rs
32
src/file.rs
@ -4,14 +4,13 @@ use io::prelude::*;
|
|||||||
use io::{SeekFrom, ErrorKind};
|
use io::{SeekFrom, ErrorKind};
|
||||||
use io;
|
use io;
|
||||||
|
|
||||||
use fs::FileSystemRef;
|
use fs::{FileSystem, ReadWriteSeek};
|
||||||
use dir_entry::{DirEntryEditor, DateTime, Date};
|
use dir_entry::{DirEntryEditor, DateTime, Date};
|
||||||
|
|
||||||
const MAX_FILE_SIZE: u32 = core::u32::MAX;
|
const MAX_FILE_SIZE: u32 = core::u32::MAX;
|
||||||
|
|
||||||
/// FAT file used for reading and writing.
|
/// FAT file used for reading and writing.
|
||||||
#[derive(Clone)]
|
pub struct File<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub struct File<'a, 'b: 'a> {
|
|
||||||
// Note first_cluster is None if file is empty
|
// Note first_cluster is None if file is empty
|
||||||
first_cluster: Option<u32>,
|
first_cluster: Option<u32>,
|
||||||
// Note: if offset points between clusters current_cluster is the previous cluster
|
// 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
|
// file dir entry editor - None for root dir
|
||||||
entry: Option<DirEntryEditor>,
|
entry: Option<DirEntryEditor>,
|
||||||
// file-system reference
|
// file-system reference
|
||||||
fs: FileSystemRef<'a, 'b>,
|
fs: &'a FileSystem<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> File<'a, 'b> {
|
impl <'a, T: ReadWriteSeek> File<'a, T> {
|
||||||
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: FileSystemRef<'a, 'b>) -> Self {
|
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: &'a FileSystem<T>) -> Self {
|
||||||
File {
|
File {
|
||||||
first_cluster, entry, fs,
|
first_cluster, entry, fs,
|
||||||
current_cluster: None, // cluster before first one
|
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) {
|
fn drop(&mut self) {
|
||||||
if let Err(err) = self.flush() {
|
if let Err(err) = self.flush() {
|
||||||
error!("flush failed {}", err);
|
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> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let cluster_size = self.fs.cluster_size();
|
let cluster_size = self.fs.cluster_size();
|
||||||
let current_cluster_opt = if self.offset % cluster_size == 0 {
|
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> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
let cluster_size = self.fs.cluster_size();
|
let cluster_size = self.fs.cluster_size();
|
||||||
let offset_in_cluster = self.offset % 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> {
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
let mut new_pos = match pos {
|
let mut new_pos = match pos {
|
||||||
SeekFrom::Current(x) => self.offset as i64 + x,
|
SeekFrom::Current(x) => self.offset as i64 + x,
|
||||||
|
62
src/fs.rs
62
src/fs.rs
@ -100,7 +100,7 @@ struct BiosParameterBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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();
|
let mut bpb: BiosParameterBlock = Default::default();
|
||||||
bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?;
|
bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?;
|
||||||
bpb.sectors_per_cluster = rdr.read_u8()?;
|
bpb.sectors_per_cluster = rdr.read_u8()?;
|
||||||
@ -188,7 +188,7 @@ struct BootRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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();
|
let mut boot: BootRecord = Default::default();
|
||||||
rdr.read_exact(&mut boot.bootjmp)?;
|
rdr.read_exact(&mut boot.bootjmp)?;
|
||||||
rdr.read_exact(&mut boot.oem_name)?;
|
rdr.read_exact(&mut boot.oem_name)?;
|
||||||
@ -228,7 +228,7 @@ impl FsInfoSector {
|
|||||||
const STRUC_SIG: u32 = 0x61417272;
|
const STRUC_SIG: u32 = 0x61417272;
|
||||||
const TRAIL_SIG: u32 = 0xAA550000;
|
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>()?;
|
let lead_sig = rdr.read_u32::<LittleEndian>()?;
|
||||||
if lead_sig != Self::LEAD_SIG {
|
if lead_sig != Self::LEAD_SIG {
|
||||||
return Err(Error::new(ErrorKind::Other, "invalid lead_sig in FsInfo sector"));
|
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)?;
|
wrt.write_u32::<LittleEndian>(Self::LEAD_SIG)?;
|
||||||
let reserved = [0u8; 480];
|
let reserved = [0u8; 480];
|
||||||
wrt.write(&reserved)?;
|
wrt.write(&reserved)?;
|
||||||
@ -343,11 +343,9 @@ impl FileSystemStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type FileSystemRef<'a, 'b> = &'a FileSystem<'b>;
|
|
||||||
|
|
||||||
/// FAT filesystem main struct.
|
/// FAT filesystem main struct.
|
||||||
pub struct FileSystem<'a> {
|
pub struct FileSystem<T: ReadWriteSeek> {
|
||||||
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
|
pub(crate) disk: RefCell<T>,
|
||||||
pub(crate) options: FsOptions,
|
pub(crate) options: FsOptions,
|
||||||
fat_type: FatType,
|
fat_type: FatType,
|
||||||
bpb: BiosParameterBlock,
|
bpb: BiosParameterBlock,
|
||||||
@ -357,7 +355,7 @@ pub struct FileSystem<'a> {
|
|||||||
fs_info: RefCell<FsInfoSector>,
|
fs_info: RefCell<FsInfoSector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> FileSystem<'a> {
|
impl <T: ReadWriteSeek> FileSystem<T> {
|
||||||
/// Creates new filesystem object instance.
|
/// 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)
|
/// 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
|
/// Note: creating multiple filesystem objects with one underlying device/disk image can
|
||||||
/// cause filesystem corruption.
|
/// cause filesystem corruption.
|
||||||
pub fn new<T: ReadWriteSeek>(disk: &'a mut T, options: FsOptions) -> io::Result<Self> {
|
pub fn new(mut disk: T, options: FsOptions) -> io::Result<Self> {
|
||||||
// make sure given image is not seeked
|
// Make sure given image is not seeked
|
||||||
debug_assert!(disk.seek(SeekFrom::Current(0))? == 0);
|
debug_assert!(disk.seek(SeekFrom::Current(0))? == 0);
|
||||||
|
|
||||||
// read boot sector
|
// read boot sector
|
||||||
let bpb = {
|
let bpb = {
|
||||||
let boot = BootRecord::deserialize(disk)?;
|
let boot = BootRecord::deserialize(&mut disk)?;
|
||||||
if boot.boot_sig != [0x55, 0xAA] {
|
if boot.boot_sig != [0x55, 0xAA] {
|
||||||
return Err(Error::new(ErrorKind::Other, "invalid signature"));
|
return Err(Error::new(ErrorKind::Other, "invalid signature"));
|
||||||
}
|
}
|
||||||
@ -395,7 +393,7 @@ impl <'a> FileSystem<'a> {
|
|||||||
// read FSInfo sector if this is FAT32
|
// read FSInfo sector if this is FAT32
|
||||||
let mut fs_info = if fat_type == FatType::Fat32 {
|
let mut fs_info = if fat_type == FatType::Fat32 {
|
||||||
disk.seek(SeekFrom::Start(bpb.fs_info_sector as u64 * 512))?;
|
disk.seek(SeekFrom::Start(bpb.fs_info_sector as u64 * 512))?;
|
||||||
FsInfoSector::deserialize(disk)?
|
FsInfoSector::deserialize(&mut disk)?
|
||||||
} else {
|
} else {
|
||||||
FsInfoSector::default()
|
FsInfoSector::default()
|
||||||
};
|
};
|
||||||
@ -442,7 +440,7 @@ impl <'a> FileSystem<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns root directory object allowing futher penetration of filesystem structure.
|
/// 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 = {
|
let root_rdr = {
|
||||||
match self.fat_type {
|
match self.fat_type {
|
||||||
FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
|
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))
|
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 =
|
let sectors_per_fat =
|
||||||
if self.bpb.sectors_per_fat_16 == 0 { self.bpb.sectors_per_fat_32 }
|
if self.bpb.sectors_per_fat_16 == 0 { self.bpb.sectors_per_fat_32 }
|
||||||
else { self.bpb.sectors_per_fat_16 as u32 };
|
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)
|
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();
|
let disk_slice = self.fat_slice();
|
||||||
ClusterIterator::new(disk_slice, self.fat_type, cluster)
|
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) {
|
fn drop(&mut self) {
|
||||||
if let Err(err) = self.unmount_internal() {
|
if let Err(err) = self.unmount_internal() {
|
||||||
error!("unmount failed {}", err);
|
error!("unmount failed {}", err);
|
||||||
@ -582,21 +580,20 @@ impl<'a> Drop for FileSystem<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub(crate) struct DiskSlice<'a, T: ReadWriteSeek + 'a> {
|
||||||
pub(crate) struct DiskSlice<'a, 'b: 'a> {
|
|
||||||
begin: u64,
|
begin: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
mirrors: u8,
|
mirrors: u8,
|
||||||
fs: &'a FileSystem<'b>,
|
fs: &'a FileSystem<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> DiskSlice<'a, 'b> {
|
impl <'a, T: ReadWriteSeek> DiskSlice<'a, T> {
|
||||||
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: FileSystemRef<'a, 'b>) -> Self {
|
pub(crate) fn new(begin: u64, size: u64, mirrors: u8, fs: &'a FileSystem<T>) -> Self {
|
||||||
DiskSlice { begin, size, mirrors, fs, offset: 0 }
|
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;
|
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)
|
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> {
|
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());
|
||||||
@ -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> {
|
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());
|
||||||
@ -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> {
|
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,
|
||||||
|
67
src/table.rs
67
src/table.rs
@ -1,8 +1,7 @@
|
|||||||
use io;
|
use io;
|
||||||
use io::prelude::*;
|
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
|
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
|
||||||
use fs::{FatType, FsStatusFlags, DiskSlice, ReadSeek};
|
use fs::{ReadWriteSeek, ReadSeek, FatType, FsStatusFlags};
|
||||||
|
|
||||||
struct Fat<T> {
|
struct Fat<T> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -24,14 +23,14 @@ enum FatValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait FatTrait {
|
trait FatTrait {
|
||||||
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
|
fn get_raw<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<u32>;
|
||||||
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
|
fn get<T: ReadSeek>(fat: &mut T, cluster: u32) -> io::Result<FatValue>;
|
||||||
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
|
fn set<T: ReadWriteSeek>(fat: &mut T, cluster: u32, value: FatValue) -> io::Result<()>;
|
||||||
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>;
|
||||||
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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
match fat_type {
|
||||||
FatType::Fat12 => Fat12::get(fat, cluster),
|
FatType::Fat12 => Fat12::get(fat, cluster),
|
||||||
FatType::Fat16 => Fat16::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);
|
trace!("write FAT - cluster {} value {:?}", cluster, value);
|
||||||
match fat_type {
|
match fat_type {
|
||||||
FatType::Fat12 => Fat12::set(fat, cluster, value),
|
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)?;
|
let val = read_fat(fat, fat_type, cluster)?;
|
||||||
match val {
|
match val {
|
||||||
FatValue::Data(n) => Ok(Some(n)),
|
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 {
|
match fat_type {
|
||||||
FatType::Fat12 => Fat12::find_free(fat, start_cluster, end_cluster),
|
FatType::Fat12 => Fat12::find_free(fat, start_cluster, end_cluster),
|
||||||
FatType::Fat16 => Fat16::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 end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
|
||||||
let start_cluster = match hint {
|
let start_cluster = match hint {
|
||||||
Some(n) if n < end_cluster => n,
|
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)
|
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)
|
// check MSB (except in FAT12)
|
||||||
let val = match fat_type {
|
let val = match fat_type {
|
||||||
FatType::Fat12 => 0xFFF,
|
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;
|
let end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
|
||||||
match fat_type {
|
match fat_type {
|
||||||
FatType::Fat12 => Fat12::count_free(fat, end_cluster),
|
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 {
|
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);
|
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 packed_val = fat.read_u16::<LittleEndian>()?;
|
let packed_val = fat.read_u16::<LittleEndian>()?;
|
||||||
@ -125,7 +124,7 @@ impl FatTrait for Fat12 {
|
|||||||
} as u32)
|
} 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)?;
|
let val = Self::get_raw(fat, cluster)?;
|
||||||
Ok(match val {
|
Ok(match val {
|
||||||
0 => FatValue::Free,
|
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 {
|
let raw_val = match value {
|
||||||
FatValue::Free => 0,
|
FatValue::Free => 0,
|
||||||
FatValue::Bad => 0xFF7,
|
FatValue::Bad => 0xFF7,
|
||||||
@ -154,7 +153,7 @@ impl FatTrait for Fat12 {
|
|||||||
Ok(())
|
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 mut cluster = start_cluster;
|
||||||
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))?;
|
||||||
@ -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 count = 0;
|
||||||
let mut cluster = RESERVED_FAT_ENTRIES;
|
let mut cluster = RESERVED_FAT_ENTRIES;
|
||||||
fat.seek(io::SeekFrom::Start((cluster*3/2) as u64))?;
|
fat.seek(io::SeekFrom::Start((cluster*3/2) as u64))?;
|
||||||
@ -211,12 +210,12 @@ impl FatTrait for Fat12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FatTrait for Fat16 {
|
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))?;
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
Ok(fat.read_u16::<LittleEndian>()? as u32)
|
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)?;
|
let val = Self::get_raw(fat, cluster)?;
|
||||||
Ok(match val {
|
Ok(match val {
|
||||||
0 => FatValue::Free,
|
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))?;
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
let raw_val = match value {
|
let raw_val = match value {
|
||||||
FatValue::Free => 0,
|
FatValue::Free => 0,
|
||||||
@ -238,7 +237,7 @@ impl FatTrait for Fat16 {
|
|||||||
Ok(())
|
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 mut cluster = start_cluster;
|
||||||
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
while cluster < end_cluster {
|
while cluster < end_cluster {
|
||||||
@ -251,7 +250,7 @@ impl FatTrait for Fat16 {
|
|||||||
Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"))
|
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 count = 0;
|
||||||
let mut cluster = RESERVED_FAT_ENTRIES;
|
let mut cluster = RESERVED_FAT_ENTRIES;
|
||||||
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
|
||||||
@ -269,12 +268,12 @@ impl FatTrait for Fat16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FatTrait for Fat32 {
|
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))?;
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
Ok(fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF)
|
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)?;
|
let val = Self::get_raw(fat, cluster)?;
|
||||||
Ok(match val {
|
Ok(match val {
|
||||||
0 => FatValue::Free,
|
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))?;
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
let raw_val = match value {
|
let raw_val = match value {
|
||||||
FatValue::Free => 0,
|
FatValue::Free => 0,
|
||||||
@ -296,7 +295,7 @@ impl FatTrait for Fat32 {
|
|||||||
Ok(())
|
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 mut cluster = start_cluster;
|
||||||
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
while cluster < end_cluster {
|
while cluster < end_cluster {
|
||||||
@ -309,7 +308,7 @@ impl FatTrait for Fat32 {
|
|||||||
Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"))
|
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 count = 0;
|
||||||
let mut cluster = RESERVED_FAT_ENTRIES;
|
let mut cluster = RESERVED_FAT_ENTRIES;
|
||||||
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
|
||||||
@ -326,15 +325,15 @@ impl FatTrait for Fat32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ClusterIterator<'a, 'b: 'a> {
|
pub(crate) struct ClusterIterator<T: ReadWriteSeek> {
|
||||||
fat: DiskSlice<'a, 'b>,
|
fat: T,
|
||||||
fat_type: FatType,
|
fat_type: FatType,
|
||||||
cluster: Option<u32>,
|
cluster: Option<u32>,
|
||||||
err: bool,
|
err: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> ClusterIterator<'a, 'b> {
|
impl <T: ReadWriteSeek> ClusterIterator<T> {
|
||||||
pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32) -> Self {
|
pub(crate) fn new(fat: T, fat_type: FatType, cluster: u32) -> Self {
|
||||||
ClusterIterator {
|
ClusterIterator {
|
||||||
fat,
|
fat,
|
||||||
fat_type,
|
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>;
|
type Item = io::Result<u32>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -6,24 +6,26 @@ use std::io::SeekFrom;
|
|||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use fatfs::{FileSystem, FsOptions, FatType, DirEntry, BufStream};
|
use fatfs::{FsOptions, FatType, BufStream};
|
||||||
|
|
||||||
const TEST_TEXT: &str = "Rust is cool!\n";
|
const TEST_TEXT: &str = "Rust is cool!\n";
|
||||||
const FAT12_IMG: &str = "resources/fat12.img";
|
const FAT12_IMG: &str = "resources/fat12.img";
|
||||||
const FAT16_IMG: &str = "resources/fat16.img";
|
const FAT16_IMG: &str = "resources/fat16.img";
|
||||||
const FAT32_IMG: &str = "resources/fat32.img";
|
const FAT32_IMG: &str = "resources/fat32.img";
|
||||||
|
|
||||||
|
type FileSystem = fatfs::FileSystem<BufStream<fs::File>>;
|
||||||
|
|
||||||
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) {
|
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
let file = fs::File::open(filename).unwrap();
|
let file = fs::File::open(filename).unwrap();
|
||||||
let mut buf_file = BufStream::new(file);
|
let buf_file = BufStream::new(file);
|
||||||
let fs = FileSystem::new(&mut buf_file, FsOptions::new()).unwrap();
|
let fs = FileSystem::new(buf_file, FsOptions::new()).unwrap();
|
||||||
f(fs);
|
f(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_root_dir(fs: FileSystem) {
|
fn test_root_dir(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<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>>();
|
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"]);
|
assert_eq!(short_names, ["LONG.TXT", "SHORT.TXT", "VERY", "VERY-L~1"]);
|
||||||
let names = entries.iter().map(|e| e.file_name()).collect::<Vec<String>>();
|
let names = entries.iter().map(|e| e.file_name()).collect::<Vec<String>>();
|
||||||
|
@ -6,7 +6,7 @@ use std::io::prelude::*;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use fatfs::{FileSystem, FsOptions, BufStream};
|
use fatfs::{FsOptions, BufStream};
|
||||||
|
|
||||||
const FAT12_IMG: &str = "fat12.img";
|
const FAT12_IMG: &str = "fat12.img";
|
||||||
const FAT16_IMG: &str = "fat16.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_STR: &str = "Hi there Rust programmer!\n";
|
||||||
const TEST_STR2: &str = "Rust is cool!\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) {
|
fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str, test_seq: u32) {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
let img_path = format!("{}/{}", IMG_DIR, filename);
|
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();
|
fs::copy(&img_path, &tmp_path).unwrap();
|
||||||
{
|
{
|
||||||
let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||||
let mut buf_file = BufStream::new(file);
|
let buf_file = BufStream::new(file);
|
||||||
let options = FsOptions::new().update_accessed_date(true).update_fs_info(true);
|
let options = FsOptions::new().update_accessed_date(true);
|
||||||
let fs = FileSystem::new(&mut buf_file, options).unwrap();
|
let fs = FileSystem::new(buf_file, options).unwrap();
|
||||||
f(fs);
|
f(fs);
|
||||||
}
|
}
|
||||||
fs::remove_file(tmp_path).unwrap();
|
fs::remove_file(tmp_path).unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user