exfat-fs 0.1.3

A comprehensive implementation of the exFAT filesystem
Documentation
use crate::{
    boot_sector::UnixEpochDuration,
    disk::{ReadOffset, WriteSeek},
};
use alloc::sync::Arc;

#[derive(Debug, thiserror::Error)]
pub enum ExfatFormatError<T: UnixEpochDuration> {
    #[error("Invalid bytes per sector. Must be a power of `2` and between `512` and `4096`: {0}.")]
    InvalidBytesPerSector(u16),
    #[error("Invalid volume size: {0}.")]
    InvalidSize(u64),
    #[error("Invalid partition offset: {0}.")]
    InvalidPartitionOffset(u64),
    #[error("Invalid number of FATs (must be 1 or 2): {0}.")]
    InvalidNumberOfFats(u8),
    #[error("Invalid cluster size: {0}. Must be a power of `2` and at most 32MB: {0}")]
    InvlaidClusterSize(u32),
    #[error("Boundary alignment is too big: {0}")]
    BoundaryAlignemntTooBig(u32),
    #[error("Unable to generate unique serial number. Error: {0}")]
    NoSerial(#[source] T::Err),
    #[error("Unable to pack bitmap.")]
    CannotPackBitmap,
    #[error("File size does not match exFAT size.")]
    InvalidFileSize,
}

#[derive(Debug, thiserror::Error)]
pub enum ExfatError<T: UnixEpochDuration, O: WriteSeek>
where
    T::Err: core::fmt::Debug,
{
    #[error("{0}")]
    Format(#[from] ExfatFormatError<T>),
    #[error("I/O error: {0}.")]
    Io(#[source] O::Err),
}

#[derive(Debug, thiserror::Error)]
pub enum RootError<O: ReadOffset> {
    #[error("I/O error: {0}.")]
    Io(O::Err),
    #[error("The provided volume is not an exFAT filesystem.")]
    WrongFs,
    #[error("Invalid bytes per sector shift detected: {0}. Must be between `9` and `12`")]
    InvalidBytesPerSectorShift(u8),
    #[error("Invalid sectors per cluster shift detected: {0}.")]
    InvalidSectorsPerClusterShift(u8),
    #[error("Invalid number of FATs detected: {0}. Must be either `1` or `2`.")]
    InvalidNumberOfFats(u8),
    #[error("Fat could not be parsed: {0}.")]
    Fat(#[from] FatLoadError<Arc<O>>),
    #[error(
        "Invalid index of root directory cluster detected: {0}. Must be bigger than `2` and at most `cluster_count + 1`"
    )]
    InvalidRootDirectoryClusterIndex(u32),
    #[error("Cluster chain could not be parsed: {0}.")]
    ClusterChain(#[from] ClusterChainError),
    #[error("Entry Reader Error: {0}.")]
    DirEntry(#[from] EntryReaderError<Arc<O>>),
    #[error(
        "All directory entries of the root directory must be of type `PRIMARY`. Detected entry type: {0}"
    )]
    RootEntryNotPrimary(u8),
    #[error("More than 2 allocation bitmap root entry fields detected.")]
    InvalidNumberOfAllocationBitmaps,
    #[error("Corrupt allocation bitmap entry.")]
    InvalidAllocationBitmap,
    #[error("More than 1 upcase table root entry field detected.")]
    InvalidNumberOfUpcaseTables,
    #[error("Corrupt upcase table entry.")]
    InvalidUpcaseTable,
    #[error("More than 1 volume label root entry field detected.")]
    InvalidNumberOfVolumeLabels,
    #[error("Corrupt volume label entry.")]
    InvalidVolumeLabel,
    #[error("Unable to parse file entry: {0}")]
    InvalidFileEntry(#[from] FileParserError<Arc<O>>),
    #[error("Unexpected directory entry in root directory. Detected entry type: {0}")]
    UnexpectedRootEntry(u8),
}

#[derive(Debug, thiserror::Error)]
pub enum FatLoadError<O: ReadOffset> {
    #[error("FAT starts at invalid offset.")]
    InvalidOffset,
    #[error("Read failed at: {0:#x}.")]
    ReadFailed(u64, #[source] O::Err),
}

#[derive(Debug, thiserror::Error)]
pub enum ClusterChainError {
    #[error("Invalid starting cluster.")]
    InvalidFirstCluster,
    #[error("Invalid data length for cluster chain.")]
    InvalidDataLength,
}

#[derive(Debug, thiserror::Error)]
pub enum EntryReaderError<O: ReadOffset> {
    #[error("Cannot read entry #{0} on cluster #{1}.")]
    ReadFailed(usize, u32, #[source] O::Err),
    #[error("{0}")]
    Entry(#[from] DirEntryError),
}

#[derive(Debug, thiserror::Error)]
pub enum DirEntryError {
    #[error("Invalid directory entry detected: {0}.")]
    InvalidEntry(u8),
}

#[derive(Debug, thiserror::Error)]
pub enum FileParserError<O: ReadOffset>
where
    O::Err: core::fmt::Debug,
{
    #[error("File entry is missing stream extension.")]
    NoStreamExtension,
    #[error("File entry is missing file name.")]
    NoFileName,
    #[error("{0}")]
    ReadFailed(#[from] EntryReaderError<O>),
    #[error("Invalid stream extension entry detected.")]
    InvalidStreamExtension,
    #[error("Wrong number of file name entries detected.")]
    WrongFileNameEntries,
    #[error("Invalid file name entry detected.")]
    InvalidFileName,
}

#[derive(Debug, thiserror::Error)]
pub enum DirectoryError<O: ReadOffset>
where
    O::Err: core::fmt::Debug,
{
    #[error("Cannot create a clusters reader for allocation: {0}")]
    CreateClustersReaderFailed(#[from] ClusterChainError),
    #[error("Cannot read an entry: {0}")]
    ReadEntryFailed(#[from] EntryReaderError<Arc<O>>),
    #[error("Detected directory entry that is not `PRIMARY`. Detected entry type: {0}")]
    NotPrimaryEntry(u8),
    #[error("Detected directory entry that is not a file entry. Detected entry type: {0}")]
    NotFileEntry(u8),
    #[error("Unable to parse file entry: {0}")]
    InvalidFileEntry(#[from] FileParserError<Arc<O>>),
}