//! Bounded three-extent, read-only file mapping. pub const BLOCK_BYTES: usize = 512; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { EmptyExtent, OutOfBounds, OutOfOrder, Overlap, FileTooLarge, OutputTooSmall, ReadFailed, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Extent { pub first_block: u64, pub block_count: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct File { extents: [Extent; 3], size: usize, } pub trait BlockReader { fn read_block(&mut self, lba: u64, destination: &mut [u8; BLOCK_BYTES]) -> Result<(), ()>; } impl File { pub fn validate(extents: [Extent; 3], size: usize, disk_blocks: u64) -> Result { let mut previous_end = 0u64; let mut capacity = 0u64; for (index, extent) in extents.iter().enumerate() { if extent.block_count == 0 { return Err(Error::EmptyExtent); } let end = extent.first_block.checked_add(extent.block_count) .ok_or(Error::OutOfBounds)?; if extent.first_block == 0 || end > disk_blocks { return Err(Error::OutOfBounds); } if index != 0 && extent.first_block < previous_end { return Err(if extent.first_block < extents[index - 1].first_block { Error::OutOfOrder } else { Error::Overlap }); } previous_end = end; let bytes = extent.block_count.checked_mul(BLOCK_BYTES as u64) .ok_or(Error::FileTooLarge)?; capacity = capacity.checked_add(bytes).ok_or(Error::FileTooLarge)?; } if size == 0 || u64::try_from(size).map_or(true, |size| size > capacity) { return Err(Error::FileTooLarge); } Ok(Self { extents, size }) } pub const fn size(&self) -> usize { self.size } pub const fn extents(&self) -> [Extent; 3] { self.extents } /// Read logical file bytes only. On transport failure, clear the prefix /// already copied so no partial file data remains in the output buffer. pub fn read_all(&self, reader: &mut R, output: &mut [u8]) -> Result { if output.len() < self.size { return Err(Error::OutputTooSmall); } let mut written = 0; for extent in self.extents { for offset in 0..extent.block_count { if written == self.size { return Ok(written); } let mut block = [0u8; BLOCK_BYTES]; if reader.read_block(extent.first_block + offset, &mut block).is_err() { output[..written].fill(0); return Err(Error::ReadFailed); } let length = (self.size - written).min(BLOCK_BYTES); output[written..written + length].copy_from_slice(&block[..length]); written += length; } } Ok(written) } }