lamexfat 0.1.0

no_std read-only exFAT reader for UEFI bootloaders (removable media)
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0
//! File data reads over the cluster chain, honoring `ValidDataLength` (bytes
//! between `ValidDataLength` and `DataLength` read as zeros, per the exFAT spec).

use crate::{block_read::BlockRead, dir::EntrySet, error::Result, fat::read_stream, vbr::Geometry};

/// Read up to `buf.len()` bytes of `es`'s data starting at `off`, clamped to
/// `DataLength`. Returns the number of bytes produced (0 at/after EOF).
pub(crate) fn read_at(
    reader: &mut impl BlockRead,
    geo: &Geometry,
    es: &EntrySet,
    off: u64,
    buf: &mut [u8],
) -> Result<usize> {
    if off >= es.data_length || buf.is_empty() {
        return Ok(0);
    }
    // `data_length` is attacker-controlled and uncapped on this path (only `read`
    // enforces MAX_FILE_BYTES), so `off + buf.len()` must not wrap. With a
    // saturating add, `end - off` is always <= buf.len(): if the add saturates
    // then `off + buf.len() > u64::MAX >= data_length`, forcing `data_length - off
    // < buf.len()`, and `end` is then `data_length`.
    let end = off.saturating_add(buf.len() as u64).min(es.data_length);
    let total = (end - off) as usize;
    let dst = &mut buf[..total];

    // Bytes at/after ValidDataLength are defined to read as zeros.
    let valid = core::cmp::min(es.valid_data_length, es.data_length);
    if off >= valid {
        dst.fill(0);
        return Ok(total);
    }
    let read_end = core::cmp::min(end, valid);
    let read_len = (read_end - off) as usize;
    read_stream(
        reader,
        geo,
        es.first_cluster,
        es.contiguous,
        off,
        &mut dst[..read_len],
    )?;
    if read_len < total {
        dst[read_len..].fill(0);
    }
    Ok(total)
}