From ec3906474fc22572583bcbb69b1525d694533921 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Sun, 22 Sep 2024 19:37:59 +0700 Subject: [PATCH] Revises DiskPartition --- Cargo.toml | 1 + src/cluster.rs | 2 +- src/disk.rs | 76 +++++++++++++++++---------------------- src/fat.rs | 41 +++++++++------------ src/image.rs | 74 -------------------------------------- src/lib.rs | 74 ++++++++++++++++++++++++++++++++------ tests/integration_test.rs | 2 -- 7 files changed, 115 insertions(+), 155 deletions(-) delete mode 100644 src/image.rs diff --git a/Cargo.toml b/Cargo.toml index 953077d..eea8ebb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/cluster.rs b/src/cluster.rs index fe7f370..4fe54db 100644 --- a/src/cluster.rs +++ b/src/cluster.rs @@ -143,7 +143,7 @@ impl Read for ClustersReader

{ 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; diff --git a/src/disk.rs b/src/disk.rs index a1822f2..1bf81ae 100644 --- a/src/disk.rs +++ b/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>; + type Err: PartitionError + 'static; - #[cfg(feature = "std")] - fn read( - &self, - offset: u64, - buf: &mut [u8], - ) -> Result>; + fn read(&self, offset: u64, buf: &mut [u8]) -> Result; - #[cfg(not(feature = "std"))] - fn read_exact( - &self, - mut offset: u64, - mut buf: &mut [u8], - ) -> Result<(), Box> { + 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> { - 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 { + std::os::unix::fs::FileExt::read_at(self, buf, offset) + } + + #[cfg(windows)] + fn read(&self, offset: u64, buf: &mut [u8]) -> Result { + 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) + } +} diff --git a/src/fat.rs b/src/fat.rs index 1e752ed..9eddfd0 100644 --- a/src/fat.rs +++ b/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, @@ -12,7 +13,7 @@ impl Fat { params: &Params, partition: &P, index: usize, - ) -> Result { + ) -> Result> { // 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 { + #[error("invalid FatLength")] InvalidFatLength, + + #[error("invalid FatOffset")] InvalidFatOffset, - #[cfg(not(feature = "std"))] - ReadFailed(u64, Box), - - #[cfg(feature = "std")] - ReadFailed(u64, Box), + #[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 Debug for LoadError

{ + 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() + } } } } diff --git a/src/image.rs b/src/image.rs deleted file mode 100644 index 344ac63..0000000 --- a/src/image.rs +++ /dev/null @@ -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 { - file: Mutex<(F, u64)>, -} - -impl Image { - pub fn open(mut file: F) -> Result { - let offset = match file.stream_position() { - Ok(v) => v, - Err(e) => return Err(OpenError::GetStreamPositionFailed(e)), - }; - - Ok(Self { - file: Mutex::new((file, offset)), - }) - } -} - -impl DiskPartition for Image { - fn read(&self, offset: u64, buf: &mut [u8]) -> Result> { - 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), -} diff --git a/src/lib.rs b/src/lib.rs index e0aa9ac..633b015 100644 --- a/src/lib.rs +++ b/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 { } impl Root

{ - pub fn open(partition: P) -> Result { + pub fn open(partition: P) -> Result> { // Read boot sector. let mut boot = [0u8; 512]; @@ -300,11 +300,11 @@ pub(crate) struct ExFat { 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 { #[error("cannot read main boot region")] - ReadMainBootFailed(#[source] Box), + 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

), #[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 Debug for OpenError

{ + 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"), + } + } +} diff --git a/tests/integration_test.rs b/tests/integration_test.rs index e7b34ab..c9ea357 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -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");