Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic
- Added `open_long_name_file_in_dir` API, to open files using their Long File Name
- Updated directory iterator callback, to allow bailing out early (breaking change)
- `crc7` function now returns the actual CRC7 value without left-shifting by and XOR-ing by/with 1.
- Bumped `embedded-io` to 0.7

### Added

- Added `CsdV3` data structure.
- `Csd::new` constructor which creates the CSD from a raw `&[u8; 16]` and also performs a CRC7
check.
- Added `verify_crc7` function on CSD structures.
- Added `core::error::Error` bounds to device driver error type

## [Version 0.9.0] - 2025-06-08

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ rust-version = "1.87"
byteorder = {version = "1", default-features = false}
defmt = {version = "1.0.1", optional = true}
embedded-hal = "1.0.0"
embedded-io = "0.6.1"
embedded-io = "0.7"
bitbybit = "2"
arbitrary-int = "2"
bitflags = "2"
Expand Down
2 changes: 1 addition & 1 deletion src/blockdevice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Default for Block {
/// sectors). Only supports devices which are <= 2 TiB in size.
pub trait BlockDevice {
/// The errors that the `BlockDevice` can return. Must be debug formattable.
type Error: core::fmt::Debug;
type Error: core::error::Error + 'static;
/// Read one or more blocks, starting at the given block index.
fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Write one or more blocks, starting at the given block index.
Expand Down
91 changes: 34 additions & 57 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,77 +158,107 @@ macro_rules! warn {

/// All the ways the functions in this crate can fail.
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, thiserror::Error)]
pub enum Error<E>
where
E: core::fmt::Debug,
E: core::error::Error,
{
/// The underlying block device threw an error.
DeviceError(E),
#[error("error from underlying block device: {0}")]
DeviceError(#[from] E),
/// The filesystem is badly formatted (or this code is buggy).
#[error("filesystem is badly formatted: {0}")]
FormatError(&'static str),
/// The given `VolumeIdx` was bad,
#[error("no such volume")]
NoSuchVolume,
/// The given filename was bad
#[error("bad filename")]
FilenameError(FilenameError),
/// Out of memory opening volumes
#[error("too many open volumes")]
TooManyOpenVolumes,
/// Out of memory opening directories
#[error("too many open directories")]
TooManyOpenDirs,
/// Out of memory opening files
#[error("too many open files")]
TooManyOpenFiles,
/// Bad handle given
#[error("bad handle")]
BadHandle,
/// That file or directory doesn't exist
#[error("file or directory does not exist")]
NotFound,
/// You can't open a file twice or delete an open file
#[error("file already open")]
FileAlreadyOpen,
/// You can't open a directory twice
#[error("directory already open")]
DirAlreadyOpen,
/// You can't open a directory as a file
#[error("cannot open directory as file")]
OpenedDirAsFile,
/// You can't open a file as a directory
#[error("cannot open file as directory")]
OpenedFileAsDir,
/// You can't delete a non-empty directory
#[error("cannot delete a non-empty directory")]
DeleteNonEmptyDir,
/// You can't close a volume with open files or directories
#[error("volume is still in use")]
VolumeStillInUse,
/// You can't open a volume twice
#[error("cannot open volume twice")]
VolumeAlreadyOpen,
/// We can't do that yet
#[error("unsupported operation")]
Unsupported,
/// Tried to read beyond end of file
#[error("end of file")]
EndOfFile,
/// Found a bad cluster
#[error("bad cluster")]
BadCluster,
/// Error while converting types
#[error("type conversion failed")]
ConversionError,
/// The device does not have enough space for the operation
#[error("not enough space on device")]
NotEnoughSpace,
/// Cluster was not properly allocated by the library
#[error("cluster not properly allocated")]
AllocationError,
/// Jumped to free space during FAT traversing
#[error("FAT chain unterminated")]
UnterminatedFatChain,
/// Tried to open Read-Only file with write mode
#[error("file is read-only")]
ReadOnly,
/// Tried to create an existing file
#[error("file already exists")]
FileAlreadyExists,
/// Bad block size - only 512 byte blocks supported
#[error("bad block size: {0} (only 512 byte blocks supported)")]
BadBlockSize(u16),
/// Bad offset given when seeking
#[error("invalid seek offset")]
InvalidOffset,
/// Disk is full
#[error("disk full")]
DiskFull,
/// A directory with that name already exists
#[error("directory already exists")]
DirAlreadyExists,
/// The filesystem tried to gain a lock whilst already locked.
///
/// This is either a bug in the filesystem, or you tried to access the
/// filesystem API from inside a directory iterator (that isn't allowed).
#[error("already locked")]
LockError,
}

impl<E: Debug> embedded_io::Error for Error<E> {
impl<E: core::error::Error + 'static> embedded_io::Error for Error<E> {
fn kind(&self) -> ErrorKind {
match self {
Error::DeviceError(_)
Expand Down Expand Up @@ -263,59 +293,6 @@ impl<E: Debug> embedded_io::Error for Error<E> {
}
}

impl<E> From<E> for Error<E>
where
E: core::fmt::Debug,
{
fn from(value: E) -> Error<E> {
Error::DeviceError(value)
}
}

impl<E> core::fmt::Display for Error<E>
where
E: core::fmt::Debug + core::fmt::Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Error::DeviceError(e) => write!(f, "error from underlying block device: {e}"),
Error::FormatError(s) => write!(f, "filesystem is badly formatted: {s}"),
Error::NoSuchVolume => write!(f, "no such volume"),
Error::FilenameError(_) => write!(f, "bad filename"),
Error::TooManyOpenVolumes => write!(f, "too many open volumes"),
Error::TooManyOpenDirs => write!(f, "too many open directories"),
Error::TooManyOpenFiles => write!(f, "too many open files"),
Error::BadHandle => write!(f, "bad handle"),
Error::NotFound => write!(f, "file or directory does not exist"),
Error::FileAlreadyOpen => write!(f, "file already open"),
Error::DirAlreadyOpen => write!(f, "directory already open"),
Error::OpenedDirAsFile => write!(f, "cannot open directory as file"),
Error::OpenedFileAsDir => write!(f, "cannot open file as directory"),
Error::DeleteNonEmptyDir => write!(f, "cannot delete a non-empty directory"),
Error::VolumeStillInUse => write!(f, "volume is still in use"),
Error::VolumeAlreadyOpen => write!(f, "cannot open volume twice"),
Error::Unsupported => write!(f, "unsupported operation"),
Error::EndOfFile => write!(f, "end of file"),
Error::BadCluster => write!(f, "bad cluster"),
Error::ConversionError => write!(f, "type conversion failed"),
Error::NotEnoughSpace => write!(f, "not enough space on device"),
Error::AllocationError => write!(f, "cluster not properly allocated"),
Error::UnterminatedFatChain => write!(f, "FAT chain unterminated"),
Error::ReadOnly => write!(f, "file is read-only"),
Error::FileAlreadyExists => write!(f, "file already exists"),
Error::BadBlockSize(size) => {
write!(f, "bad block size: {size} (only 512 byte blocks supported)")
}
Error::InvalidOffset => write!(f, "invalid seek offset"),
Error::DiskFull => write!(f, "disk full"),
Error::DirAlreadyExists => write!(f, "directory already exists"),
Error::LockError => write!(f, "already locked"),
}
}
}

impl<E> core::error::Error for Error<E> where E: core::fmt::Debug + core::fmt::Display {}

/// A handle to a volume.
///
/// A volume is a partition with a filesystem within it.
Expand Down
28 changes: 12 additions & 16 deletions src/volume_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub struct VolumeManager<
> where
D: BlockDevice,
T: TimeSource,
<D as BlockDevice>::Error: core::fmt::Debug,
{
time_source: T,
data: RefCell<VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>>,
Expand All @@ -46,7 +45,6 @@ impl<D, T> VolumeManager<D, T, 4, 4>
where
D: BlockDevice,
T: TimeSource,
<D as BlockDevice>::Error: core::fmt::Debug,
{
/// Create a new Volume Manager using a generic `BlockDevice`. From this
/// object we can open volumes (partitions) and with those we can open
Expand All @@ -67,7 +65,6 @@ impl<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usi
where
D: BlockDevice,
T: TimeSource,
<D as BlockDevice>::Error: core::fmt::Debug,
{
/// Create a new Volume Manager using a generic `BlockDevice`. From this
/// object we can open volumes (partitions) and with those we can open
Expand Down Expand Up @@ -1430,6 +1427,7 @@ impl<D, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
VolumeManagerData<D, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: BlockDevice,
<D as BlockDevice>::Error: core::error::Error,
{
/// Check if a file is open
///
Expand All @@ -1446,10 +1444,10 @@ where
false
}

fn get_volume_by_id<E>(&self, raw_volume: RawVolume) -> Result<usize, Error<E>>
where
E: core::fmt::Debug,
{
fn get_volume_by_id<E: core::error::Error>(
&self,
raw_volume: RawVolume,
) -> Result<usize, Error<E>> {
for (idx, v) in self.open_volumes.iter().enumerate() {
if v.raw_volume == raw_volume {
return Ok(idx);
Expand All @@ -1458,10 +1456,10 @@ where
Err(Error::BadHandle)
}

fn get_dir_by_id<E>(&self, raw_directory: RawDirectory) -> Result<usize, Error<E>>
where
E: core::fmt::Debug,
{
fn get_dir_by_id<E: core::error::Error>(
&self,
raw_directory: RawDirectory,
) -> Result<usize, Error<E>> {
for (idx, d) in self.open_dirs.iter().enumerate() {
if d.raw_directory == raw_directory {
return Ok(idx);
Expand All @@ -1470,10 +1468,7 @@ where
Err(Error::BadHandle)
}

fn get_file_by_id<E>(&self, raw_file: RawFile) -> Result<usize, Error<E>>
where
E: core::fmt::Debug,
{
fn get_file_by_id<E: core::error::Error>(&self, raw_file: RawFile) -> Result<usize, Error<E>> {
for (idx, f) in self.open_files.iter().enumerate() {
if f.raw_file == raw_file {
return Ok(idx);
Expand Down Expand Up @@ -1570,8 +1565,9 @@ mod tests {

struct Clock;

#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("unknown error")]
Unknown,
}

Expand Down
21 changes: 6 additions & 15 deletions tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,20 @@ use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx};
/// It will unpack to a Vec that is 1048576 * 512 = 512 MiB in size.
pub static DISK_SOURCE: &[u8] = include_bytes!("../disk.img.gz");

#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
#[allow(dead_code)]
pub enum Error {
/// Failed to read the source image
Io(std::io::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
/// Failed to unzip the source image
Decode(flate2::DecompressError),
#[error("flate2 decode error: {0}")]
Decode(#[from] flate2::DecompressError),
/// Asked for a block we don't have
#[error("out of bounds block index: {0:?}")]
OutOfBounds(BlockIdx),
}

impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}

impl From<flate2::DecompressError> for Error {
fn from(value: flate2::DecompressError) -> Self {
Self::Decode(value)
}
}

/// Implements the block device traits for a chunk of bytes in RAM.
///
/// The slice should be a multiple of `embedded_sdmmc::Block::LEN` bytes in
Expand Down