Skip to content

Commit

Permalink
Removes ExFat struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Sep 28, 2024
1 parent ef9a8cd commit b930023
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 112 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# exFAT in pure Rust
[![Crates.io](https://img.shields.io/crates/v/exfat)](https://crates.io/crates/exfat)

This is an implementation of exFAT in pure Rust. Currently it is supports only reading, not writing; and not all features is implemented but if all you need is listing the directories and read the files then you are good to go.
This is an implementation of exFAT in pure Rust. Currently it is supports only reading, not writing; and not all features are implemented but if all you need is listing the directories and read the files then you are good to go.

This implementation require a global allocator.

## Usage

Expand Down
74 changes: 42 additions & 32 deletions src/cluster.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
use crate::disk::DiskPartition;
use crate::ExFat;
use crate::fat::Fat;
use crate::param::Params;
use std::cmp::min;
use std::sync::Arc;
use thiserror::Error;

/// Struct to read all data in a cluster chain.
pub struct ClustersReader<P: DiskPartition> {
exfat: Arc<ExFat<P>>,
pub struct ClustersReader<D, P> {
disk: D,
params: P,
chain: Vec<usize>,
data_length: u64,
offset: u64,
}

impl<P: DiskPartition> ClustersReader<P> {
impl<D, P: AsRef<Params>> ClustersReader<D, P> {
pub fn new(
exfat: Arc<ExFat<P>>,
disk: D,
params: P,
fat: &Fat,
first_cluster: usize,
data_length: Option<u64>,
no_fat_chain: Option<bool>,
Expand All @@ -24,9 +27,7 @@ impl<P: DiskPartition> ClustersReader<P> {
}

// Get cluster chain.
let params = &exfat.params;
let fat = &exfat.fat;
let cluster_size = params.cluster_size();
let cluster_size = params.as_ref().cluster_size();
let (chain, data_length) = if no_fat_chain.unwrap_or(false) {
// If the NoFatChain bit is 1 then DataLength must not be zero.
let data_length = match data_length {
Expand Down Expand Up @@ -54,28 +55,53 @@ impl<P: DiskPartition> ClustersReader<P> {
v
}
}
None => params.bytes_per_sector * (params.sectors_per_cluster * chain.len() as u64),
None => {
params.as_ref().bytes_per_sector
* (params.as_ref().sectors_per_cluster * chain.len() as u64)
}
};

(chain, data_length)
};

Ok(Self {
exfat,
disk,
params,
chain,
data_length,
offset: 0,
})
}

pub fn cluster(&self) -> usize {
self.chain[(self.offset / self.exfat.params.cluster_size()) as usize]
self.chain[(self.offset / self.params.as_ref().cluster_size()) as usize]
}
}

impl<D, P> ClustersReader<D, P> {
pub fn data_length(&self) -> u64 {
self.data_length
}

pub fn seek(&mut self, off: u64) -> bool {
if off > self.data_length {
return false;
}

self.offset = off;
true
}

pub fn rewind(&mut self) {
self.offset = 0;
}

pub fn stream_position(&self) -> u64 {
self.offset
}
}

impl<D: DiskPartition, P: AsRef<Params>> ClustersReader<D, P> {
pub fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
use std::io::{Error, ErrorKind};

Expand All @@ -85,13 +111,14 @@ impl<P: DiskPartition> ClustersReader<P> {
}

// Get remaining data in the current cluster.
let cluster_size = self.exfat.params.cluster_size();
let params = self.params.as_ref();
let cluster_size = params.cluster_size();
let cluster_remaining = cluster_size - self.offset % cluster_size;
let remaining = min(cluster_remaining, self.data_length - self.offset);

// Get the offset in the partition.
let cluster = self.chain[(self.offset / cluster_size) as usize];
let offset = match self.exfat.params.cluster_offset(cluster) {
let offset = match params.cluster_offset(cluster) {
Some(v) => v + self.offset % cluster_size,
None => {
return Err(Error::new(
Expand All @@ -104,7 +131,7 @@ impl<P: DiskPartition> ClustersReader<P> {
// Read image.
let amount = min(buf.len(), remaining as usize);

if let Err(e) = self.exfat.partition.read_exact(offset, &mut buf[..amount]) {
if let Err(e) = self.disk.read_exact(offset, &mut buf[..amount]) {
return Err(Error::new(ErrorKind::Other, Box::new(e)));
}

Expand All @@ -126,23 +153,6 @@ impl<P: DiskPartition> ClustersReader<P> {

Ok(())
}

pub fn seek(&mut self, off: u64) -> bool {
if off > self.data_length {
return false;
}

self.offset = off;
true
}

pub fn rewind(&mut self) {
self.offset = 0;
}

pub fn stream_position(&self) -> u64 {
self.offset
}
}

/// Represents an error for [`new()`][ClustersReader::new()].
Expand Down
55 changes: 40 additions & 15 deletions src/directory.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
use crate::cluster::ClustersReader;
use crate::disk::DiskPartition;
use crate::entries::{ClusterAllocation, EntriesReader, EntryType, FileEntry, StreamEntry};
use crate::fat::Fat;
use crate::file::File;
use crate::param::Params;
use crate::timestamp::Timestamps;
use crate::ExFat;
use std::sync::Arc;
use alloc::sync::Arc;
use thiserror::Error;

/// Represents a directory in an exFAT filesystem.
pub struct Directory<P: DiskPartition> {
exfat: Arc<ExFat<P>>,
pub struct Directory<D> {
disk: Arc<D>,
params: Arc<Params>,
fat: Arc<Fat>,
name: String,
stream: StreamEntry,
timestamps: Timestamps,
}

impl<P: DiskPartition> Directory<P> {
impl<D> Directory<D> {
pub(crate) fn new(
exfat: Arc<ExFat<P>>,
disk: Arc<D>,
params: Arc<Params>,
fat: Arc<Fat>,
name: String,
stream: StreamEntry,
timestamps: Timestamps,
) -> Self {
Self {
exfat,
disk,
params,
fat,
name,
stream,
timestamps,
Expand All @@ -37,12 +44,16 @@ impl<P: DiskPartition> Directory<P> {
pub fn timestamps(&self) -> &Timestamps {
&self.timestamps
}
}

pub fn open(&self) -> Result<Vec<Item<P>>, DirectoryError> {
impl<D: DiskPartition> Directory<D> {
pub fn open(&self) -> Result<Vec<Item<D>>, DirectoryError> {
// Create an entries reader.
let alloc = self.stream.allocation();
let mut reader = match ClustersReader::new(
self.exfat.clone(),
&self.disk,
&self.params,
&self.fat,
alloc.first_cluster(),
Some(alloc.data_length()),
Some(self.stream.no_fat_chain()),
Expand All @@ -52,7 +63,7 @@ impl<P: DiskPartition> Directory<P> {
};

// Read file entries.
let mut items: Vec<Item<P>> = Vec::new();
let mut items: Vec<Item<D>> = Vec::new();

loop {
// Read primary entry.
Expand Down Expand Up @@ -88,9 +99,23 @@ impl<P: DiskPartition> Directory<P> {
let timestamps = file.timestamps;

items.push(if attrs.is_directory() {
Item::Directory(Directory::new(self.exfat.clone(), name, stream, timestamps))
Item::Directory(Self {
disk: self.disk.clone(),
params: self.params.clone(),
fat: self.fat.clone(),
name,
stream,
timestamps,
})
} else {
match File::new(self.exfat.clone(), name, stream, timestamps) {
match File::new(
&self.disk,
&self.params,
&self.fat,
name,
stream,
timestamps,
) {
Ok(v) => Item::File(v),
Err(e) => {
return Err(DirectoryError::CreateFileObjectFailed(
Expand All @@ -108,9 +133,9 @@ impl<P: DiskPartition> Directory<P> {
}

/// Represents an item in the directory.
pub enum Item<P: DiskPartition> {
Directory(Directory<P>),
File(File<P>),
pub enum Item<D> {
Directory(Directory<D>),
File(File<D>),
}

/// Represents an error when [`Directory::open()`] fails.
Expand Down
18 changes: 18 additions & 0 deletions src/disk.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use alloc::sync::Arc;
use core::error::Error;
use core::ops::Deref;

/// Encapsulate a disk partition.
pub trait DiskPartition {
Expand Down Expand Up @@ -32,6 +34,22 @@ pub trait PartitionError: Error + Send + Sync {
fn unexpected_eop() -> Self;
}

impl<T: DiskPartition> DiskPartition for &T {
type Err = T::Err;

fn read(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
(*self).read(offset, buf)
}
}

impl<T: DiskPartition> DiskPartition for Arc<T> {
type Err = T::Err;

fn read(&self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
self.deref().read(offset, buf)
}
}

#[cfg(feature = "std")]
impl DiskPartition for std::fs::File {
type Err = std::io::Error;
Expand Down
21 changes: 12 additions & 9 deletions src/entries.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
use crate::cluster::ClustersReader;
use crate::disk::DiskPartition;
use crate::param::Params;
use crate::timestamp::{Timestamp, Timestamps};
use crate::FileAttributes;
use byteorder::{ByteOrder, LE};
use std::cmp::min;
use std::fmt::{Display, Formatter};
use thiserror::Error;

/// A struct to read directory entries.
pub(crate) struct EntriesReader<P: DiskPartition> {
cluster_reader: ClustersReader<P>,
/// Struct to read directory entries.
pub struct EntriesReader<D, P> {
cluster_reader: ClustersReader<D, P>,
entry_index: usize,
}

impl<P: DiskPartition> EntriesReader<P> {
pub fn new(cluster_reader: ClustersReader<P>) -> Self {
impl<D, P> EntriesReader<D, P> {
pub fn new(cluster_reader: ClustersReader<D, P>) -> Self {
Self {
cluster_reader,
entry_index: 0,
}
}
}

impl<D: DiskPartition, P: AsRef<Params>> EntriesReader<D, P> {
pub fn read(&mut self) -> Result<RawEntry, ReaderError> {
// Get current cluster and entry index.
let cluster = self.cluster_reader.cluster();
Expand Down Expand Up @@ -82,10 +85,10 @@ pub(crate) struct FileEntry {
}

impl FileEntry {
pub fn load<P>(raw: &RawEntry, reader: &mut EntriesReader<P>) -> Result<Self, FileEntryError>
where
P: DiskPartition,
{
pub fn load<D: DiskPartition, P: AsRef<Params>>(
raw: &RawEntry,
reader: &mut EntriesReader<D, P>,
) -> Result<Self, FileEntryError> {
// Load fields.
let data = &raw.data;
let secondary_count = data[1] as usize;
Expand Down
Loading

0 comments on commit b930023

Please sign in to comment.