lamexfat 0.1.0

no_std read-only exFAT reader for UEFI bootloaders (removable media)
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Typed error surface with stable `&'static str` tokens.
//!
//! Every error carries a stable vocabulary token (see [`Error::token`]) so a
//! bootloader's trust log can record *why* a read failed without string
//! formatting and without the token drifting across releases.

/// A read or mount failure.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
    /// An underlying [`BlockRead`](crate::BlockRead) call failed.
    Io { token: &'static str, offset: u64 },
    /// The boot sector / VBR was missing, malformed, or failed its checksum.
    BadVbr(VbrReason),
    /// The up-case table directory entry or data was malformed.
    BadUpcaseTable,
    /// A directory entry set failed its `SetChecksum` or secondary-count check.
    BadEntrySet { token: &'static str },
    /// A structural inconsistency mid-walk (cluster out of range, FAT cycle,
    /// non-UTF-16 name, entry past the directory end, …).
    Inconsistent { token: &'static str },
    /// A feature this read-only reader does not implement (e.g. texFAT).
    UnsupportedFeature(&'static str),
    /// Path resolution found no such component.
    NotFound { component: &'static str },
    /// The target exists but is a directory, not a regular file.
    NotARegularFile,
    /// A file's declared size exceeds the read cap
    /// ([`MAX_FILE_BYTES`](crate::MAX_FILE_BYTES)) — refused before allocating so
    /// a hostile `DataLength` cannot drive a denial-of-boot allocation.
    FileTooLarge { size: u64, max: u64 },
    /// An allocation failed at the named site. `lamexfat` never falls back.
    OutOfMemory { site: &'static str },
}

/// Why a boot sector / VBR was rejected.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VbrReason {
    BadMagic,
    BadBootSignature,
    BadGeometry,
    BadBootChecksum,
}

impl Error {
    /// The stable vocabulary token for this error (trust-log friendly).
    pub fn token(&self) -> &'static str {
        match self {
            Error::Io { token, .. } => token,
            Error::BadVbr(r) => r.token(),
            Error::BadUpcaseTable => "upcase_bad",
            Error::BadEntrySet { token } => token,
            Error::Inconsistent { token } => token,
            Error::UnsupportedFeature(t) => t,
            Error::NotFound { .. } => "not_found",
            Error::NotARegularFile => "not_a_regular_file",
            Error::FileTooLarge { .. } => "file_too_large",
            Error::OutOfMemory { site } => site,
        }
    }
}

impl VbrReason {
    pub(crate) fn token(self) -> &'static str {
        match self {
            VbrReason::BadMagic => "vbr_bad_magic",
            VbrReason::BadBootSignature => "vbr_bad_boot_sig",
            VbrReason::BadGeometry => "vbr_bad_geometry",
            VbrReason::BadBootChecksum => "vbr_bad_checksum",
        }
    }
}

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "lamexfat: {}", self.token())
    }
}

#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
impl std::error::Error for Error {}

pub(crate) type Result<T> = core::result::Result<T, Error>;

#[cfg(test)]
mod tests {
    use super::*;

    /// The token vocabulary is a stable contract for trust logs — pin it so a
    /// rename is a deliberate, reviewed change rather than an accident.
    #[test]
    fn tokens_are_stable() {
        assert_eq!(
            Error::Io {
                token: "io_vbr",
                offset: 0
            }
            .token(),
            "io_vbr"
        );
        assert_eq!(Error::BadVbr(VbrReason::BadMagic).token(), "vbr_bad_magic");
        assert_eq!(
            Error::BadVbr(VbrReason::BadBootSignature).token(),
            "vbr_bad_boot_sig"
        );
        assert_eq!(
            Error::BadVbr(VbrReason::BadGeometry).token(),
            "vbr_bad_geometry"
        );
        assert_eq!(
            Error::BadVbr(VbrReason::BadBootChecksum).token(),
            "vbr_bad_checksum"
        );
        assert_eq!(Error::BadUpcaseTable.token(), "upcase_bad");
        assert_eq!(
            Error::Inconsistent { token: "fat_cycle" }.token(),
            "fat_cycle"
        );
        assert_eq!(Error::NotFound { component: "x" }.token(), "not_found");
        assert_eq!(Error::NotARegularFile.token(), "not_a_regular_file");
        assert_eq!(
            Error::FileTooLarge { size: 1, max: 2 }.token(),
            "file_too_large"
        );
    }
}