mirror of
https://github.com/obhq/exfat.git
synced 2024-10-07 00:43:27 +00:00
Revises DiskPartition
This commit is contained in:
parent
5aeb3f7b57
commit
ec3906474f
@ -5,6 +5,7 @@ description = "Pure Rust implementation of exFAT file system"
|
||||
repository = "https://github.com/obhq/exfat"
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
rust-version = "1.81"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
@ -143,7 +143,7 @@ impl<P: DiskPartition> Read for ClustersReader<P> {
|
||||
let amount = min(buf.len(), remaining as usize);
|
||||
|
||||
if let Err(e) = self.exfat.partition.read_exact(offset, &mut buf[..amount]) {
|
||||
return Err(Error::new(ErrorKind::Other, e));
|
||||
return Err(Error::new(ErrorKind::Other, Box::new(e)));
|
||||
}
|
||||
|
||||
self.offset += amount as u64;
|
||||
|
76
src/disk.rs
76
src/disk.rs
@ -1,67 +1,55 @@
|
||||
use core::fmt::Display;
|
||||
use core::error::Error;
|
||||
|
||||
/// Encapsulate a disk partition.
|
||||
pub trait DiskPartition {
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn read(&self, offset: u64, buf: &mut [u8]) -> Result<u64, Box<dyn Display + Send + Sync>>;
|
||||
type Err: PartitionError + 'static;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn read(
|
||||
&self,
|
||||
offset: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<u64, Box<dyn std::error::Error + Send + Sync>>;
|
||||
fn read(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err>;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn read_exact(
|
||||
&self,
|
||||
mut offset: u64,
|
||||
mut buf: &mut [u8],
|
||||
) -> Result<(), Box<dyn Display + Send + Sync>> {
|
||||
fn read_exact(&self, mut offset: u64, mut buf: &mut [u8]) -> Result<(), Self::Err> {
|
||||
while !buf.is_empty() {
|
||||
let n = self.read(offset, buf)?;
|
||||
|
||||
if n == 0 {
|
||||
return Err(Box::new(UnexpectedEop));
|
||||
return Err(PartitionError::unexpected_eop());
|
||||
}
|
||||
|
||||
offset += n;
|
||||
buf = &mut buf[n.try_into().unwrap()..];
|
||||
}
|
||||
offset = n
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|n| offset.checked_add(n))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn read_exact(
|
||||
&self,
|
||||
mut offset: u64,
|
||||
mut buf: &mut [u8],
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
while !buf.is_empty() {
|
||||
let n = self.read(offset, buf)?;
|
||||
|
||||
if n == 0 {
|
||||
return Err(Box::new(UnexpectedEop));
|
||||
}
|
||||
|
||||
offset += n;
|
||||
buf = &mut buf[n.try_into().unwrap()..];
|
||||
buf = &mut buf[n..];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An error for unexpected end of partition.
|
||||
#[derive(Debug)]
|
||||
struct UnexpectedEop;
|
||||
/// Represents an error when an operation on [`DiskPartition`] fails.
|
||||
pub trait PartitionError: Error + Send + Sync {
|
||||
fn unexpected_eop() -> Self;
|
||||
}
|
||||
|
||||
impl Display for UnexpectedEop {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.write_str("end of partition has been reached")
|
||||
#[cfg(feature = "std")]
|
||||
impl DiskPartition for std::fs::File {
|
||||
type Err = std::io::Error;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn read(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
|
||||
std::os::unix::fs::FileExt::read_at(self, buf, offset)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn read(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
|
||||
std::os::windows::fs::FileExt::seek_read(self, buf, offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for UnexpectedEop {}
|
||||
impl PartitionError for std::io::Error {
|
||||
fn unexpected_eop() -> Self {
|
||||
std::io::Error::from(std::io::ErrorKind::UnexpectedEof)
|
||||
}
|
||||
}
|
||||
|
41
src/fat.rs
41
src/fat.rs
@ -1,7 +1,8 @@
|
||||
use crate::disk::DiskPartition;
|
||||
use crate::param::Params;
|
||||
use byteorder::{ByteOrder, LE};
|
||||
use core::fmt::Display;
|
||||
use core::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) struct Fat {
|
||||
entries: Vec<u32>,
|
||||
@ -12,7 +13,7 @@ impl Fat {
|
||||
params: &Params,
|
||||
partition: &P,
|
||||
index: usize,
|
||||
) -> Result<Self, LoadError> {
|
||||
) -> Result<Self, LoadError<P>> {
|
||||
// Get FAT region offset.
|
||||
let sector = match params.fat_length.checked_mul(index as u64) {
|
||||
Some(v) => match params.fat_offset.checked_add(v) {
|
||||
@ -76,34 +77,26 @@ impl<'fat> Iterator for ClusterChain<'fat> {
|
||||
}
|
||||
|
||||
/// Represents an error for [`Fat::load()`].
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
#[derive(Error)]
|
||||
pub enum LoadError<P: DiskPartition> {
|
||||
#[error("invalid FatLength")]
|
||||
InvalidFatLength,
|
||||
|
||||
#[error("invalid FatOffset")]
|
||||
InvalidFatOffset,
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
ReadFailed(u64, Box<dyn Display + Send + Sync>),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
ReadFailed(u64, Box<dyn std::error::Error + Send + Sync>),
|
||||
#[error("cannot read the data at {0:#x}")]
|
||||
ReadFailed(u64, #[source] P::Err),
|
||||
}
|
||||
|
||||
impl Display for LoadError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
impl<P: DiskPartition> Debug for LoadError<P> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidFatLength => f.write_str("invalid FatLength"),
|
||||
Self::InvalidFatOffset => f.write_str("invalid FatOffset"),
|
||||
Self::ReadFailed(offset, _) => write!(f, "cannot read the data at {offset:#018x}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for LoadError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::ReadFailed(_, e) => Some(e.as_ref()),
|
||||
_ => None,
|
||||
Self::InvalidFatLength => write!(f, "InvalidFatLength"),
|
||||
Self::InvalidFatOffset => write!(f, "InvalidFatOffset"),
|
||||
Self::ReadFailed(arg0, arg1) => {
|
||||
f.debug_tuple("ReadFailed").field(arg0).field(arg1).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
74
src/image.rs
74
src/image.rs
@ -1,74 +0,0 @@
|
||||
use crate::disk::DiskPartition;
|
||||
use std::error::Error;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::sync::Mutex;
|
||||
use thiserror::Error;
|
||||
|
||||
/// An implementation of [`DiskPartition`] backed by an exFAT image.
|
||||
pub struct Image<F: Read + Seek> {
|
||||
file: Mutex<(F, u64)>,
|
||||
}
|
||||
|
||||
impl<F: Read + Seek> Image<F> {
|
||||
pub fn open(mut file: F) -> Result<Self, OpenError> {
|
||||
let offset = match file.stream_position() {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(OpenError::GetStreamPositionFailed(e)),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
file: Mutex::new((file, offset)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Read + Seek> DiskPartition for Image<F> {
|
||||
fn read(&self, offset: u64, buf: &mut [u8]) -> Result<u64, Box<dyn Error + Send + Sync>> {
|
||||
let mut file = self
|
||||
.file
|
||||
.lock()
|
||||
.expect("the mutex that protect the inner file is poisoned");
|
||||
|
||||
// Seek the file.
|
||||
if offset != file.1 {
|
||||
match file.0.seek(SeekFrom::Start(offset)) {
|
||||
Ok(v) => {
|
||||
// The specified offset is out of range.
|
||||
if v != offset {
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(ReadError::SeekFailed(e).into()),
|
||||
}
|
||||
|
||||
file.1 = offset;
|
||||
}
|
||||
|
||||
// Read the file.
|
||||
let read = match file.0.read(buf) {
|
||||
Ok(v) => v.try_into().unwrap(),
|
||||
Err(e) => return Err(ReadError::ReadFailed(e).into()),
|
||||
};
|
||||
|
||||
file.1 += read;
|
||||
|
||||
Ok(read)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an error for [`Image::open()`].
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OpenError {
|
||||
#[error("cannot get the current seek position of the file")]
|
||||
GetStreamPositionFailed(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
/// Represents an error for [`Image::read()`].
|
||||
#[derive(Debug, Error)]
|
||||
enum ReadError {
|
||||
#[error("cannot seek the image to the target offset")]
|
||||
SeekFailed(#[source] std::io::Error),
|
||||
|
||||
#[error("cannot read the image")]
|
||||
ReadFailed(#[source] std::io::Error),
|
||||
}
|
74
src/lib.rs
74
src/lib.rs
@ -1,22 +1,22 @@
|
||||
use self::cluster::ClustersReader;
|
||||
use self::directory::{Directory, Item};
|
||||
use self::disk::DiskPartition;
|
||||
use self::entries::{ClusterAllocation, EntriesReader, EntryType, FileEntry};
|
||||
use self::fat::Fat;
|
||||
use self::file::File;
|
||||
use self::param::Params;
|
||||
use byteorder::{ByteOrder, LE};
|
||||
use std::error::Error;
|
||||
use core::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use self::disk::*;
|
||||
|
||||
pub mod cluster;
|
||||
pub mod directory;
|
||||
pub mod disk;
|
||||
mod disk;
|
||||
pub mod entries;
|
||||
pub mod fat;
|
||||
pub mod file;
|
||||
pub mod image;
|
||||
pub mod param;
|
||||
pub mod timestamp;
|
||||
|
||||
@ -30,7 +30,7 @@ pub struct Root<P: DiskPartition> {
|
||||
}
|
||||
|
||||
impl<P: DiskPartition> Root<P> {
|
||||
pub fn open(partition: P) -> Result<Self, OpenError> {
|
||||
pub fn open(partition: P) -> Result<Self, OpenError<P>> {
|
||||
// Read boot sector.
|
||||
let mut boot = [0u8; 512];
|
||||
|
||||
@ -300,11 +300,11 @@ pub(crate) struct ExFat<P: DiskPartition> {
|
||||
fat: Fat,
|
||||
}
|
||||
|
||||
/// Represents an error for [`Root::open()`].
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OpenError {
|
||||
/// Represents an error when [`Root::open()`] fails.
|
||||
#[derive(Error)]
|
||||
pub enum OpenError<P: DiskPartition> {
|
||||
#[error("cannot read main boot region")]
|
||||
ReadMainBootFailed(#[source] Box<dyn Error + Send + Sync>),
|
||||
ReadMainBootFailed(#[source] P::Err),
|
||||
|
||||
#[error("image is not exFAT")]
|
||||
NotExFat,
|
||||
@ -319,7 +319,7 @@ pub enum OpenError {
|
||||
InvalidNumberOfFats,
|
||||
|
||||
#[error("cannot read FAT region")]
|
||||
ReadFatRegionFailed(#[source] fat::LoadError),
|
||||
ReadFatRegionFailed(#[source] self::fat::LoadError<P>),
|
||||
|
||||
#[error("cannot create a clusters reader")]
|
||||
CreateClustersReaderFailed(#[source] cluster::NewError),
|
||||
@ -363,3 +363,57 @@ pub enum OpenError {
|
||||
#[error("no Up-case Table available")]
|
||||
NoUpcaseTable,
|
||||
}
|
||||
|
||||
impl<P: DiskPartition> Debug for OpenError<P> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ReadMainBootFailed(arg0) => {
|
||||
f.debug_tuple("ReadMainBootFailed").field(arg0).finish()
|
||||
}
|
||||
Self::NotExFat => write!(f, "NotExFat"),
|
||||
Self::InvalidBytesPerSectorShift => write!(f, "InvalidBytesPerSectorShift"),
|
||||
Self::InvalidSectorsPerClusterShift => write!(f, "InvalidSectorsPerClusterShift"),
|
||||
Self::InvalidNumberOfFats => write!(f, "InvalidNumberOfFats"),
|
||||
Self::ReadFatRegionFailed(arg0) => {
|
||||
f.debug_tuple("ReadFatRegionFailed").field(arg0).finish()
|
||||
}
|
||||
Self::CreateClustersReaderFailed(arg0) => f
|
||||
.debug_tuple("CreateClustersReaderFailed")
|
||||
.field(arg0)
|
||||
.finish(),
|
||||
Self::ReadEntryFailed(arg0) => f.debug_tuple("ReadEntryFailed").field(arg0).finish(),
|
||||
Self::NotPrimaryEntry(arg0, arg1) => f
|
||||
.debug_tuple("NotPrimaryEntry")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.finish(),
|
||||
Self::TooManyAllocationBitmap => write!(f, "TooManyAllocationBitmap"),
|
||||
Self::WrongAllocationBitmap => write!(f, "WrongAllocationBitmap"),
|
||||
Self::MultipleUpcaseTable => write!(f, "MultipleUpcaseTable"),
|
||||
Self::MultipleVolumeLabel => write!(f, "MultipleVolumeLabel"),
|
||||
Self::InvalidVolumeLabel => write!(f, "InvalidVolumeLabel"),
|
||||
Self::LoadFileEntryFailed(arg0) => {
|
||||
f.debug_tuple("LoadFileEntryFailed").field(arg0).finish()
|
||||
}
|
||||
Self::CreateFileObjectFailed(arg0, arg1, arg2) => f
|
||||
.debug_tuple("CreateFileObjectFailed")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.field(arg2)
|
||||
.finish(),
|
||||
Self::ReadClusterAllocationFailed(arg0, arg1, arg2) => f
|
||||
.debug_tuple("ReadClusterAllocationFailed")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.field(arg2)
|
||||
.finish(),
|
||||
Self::UnknownEntry(arg0, arg1) => f
|
||||
.debug_tuple("UnknownEntry")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.finish(),
|
||||
Self::NoAllocationBitmap => write!(f, "NoAllocationBitmap"),
|
||||
Self::NoUpcaseTable => write!(f, "NoUpcaseTable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use exfat::directory::Item;
|
||||
use exfat::image::Image;
|
||||
use exfat::timestamp::Timestamp;
|
||||
use exfat::Root;
|
||||
use std::fs::File;
|
||||
@ -30,7 +29,6 @@ fn read_image() {
|
||||
// Open the image.
|
||||
let image: PathBuf = ["tests", "exfat.img"].iter().collect();
|
||||
let image = File::open(image).expect("cannot open exfat.img");
|
||||
let image = Image::open(image).expect("cannot open exFAT image from exfat.img");
|
||||
|
||||
// Open root directory.
|
||||
let root = Root::open(image).expect("cannot open the root directory");
|
||||
|
Loading…
Reference in New Issue
Block a user