use winnow::binary::{le_u8, le_u16, le_u32};
use winnow::error::StrContext;
use winnow::prelude::*;
use winnow::token::literal;
use super::helpers::varint_size;
const FORMAT_SIGNATURE: [u8; 8] = [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a];
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct SuperBlockVersion(u8);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct SuperBlockVersionExt {
space_storage: u8,
root_group: u8,
shared_header: u8,
}
#[derive(Clone, Copy, Debug)]
pub struct SuperBlock {
pub size_of_offsets: u8,
pub size_of_lengths: u8,
pub base_address: u64,
pub end_of_file_address: u64,
pub root_group_object_header_address: u64,
pub superblock_extension_address: Option<u64>,
}
fn version(input: &mut &[u8]) -> ModalResult<SuperBlockVersion> {
le_u8
.verify_map(|ver| match ver {
0..3 => Some(SuperBlockVersion(ver)),
_ => None,
})
.context(StrContext::Label("Super block version"))
.context(StrContext::Expected("0, 1, 2 or 3".into()))
.parse_next(input)
}
fn version_ext(input: &mut &[u8]) -> ModalResult<SuperBlockVersionExt> {
(le_u8, le_u8, le_u8, le_u8)
.map(
|(space_storage, root_group, _reserved, shared_header)| SuperBlockVersionExt {
space_storage,
root_group,
shared_header,
},
)
.parse_next(input)
}
fn size_of_offsets(input: &mut &[u8]) -> ModalResult<u8> {
le_u8
.verify(|sz| matches!(sz, 2..=8))
.context(StrContext::Label("Size of Offsets"))
.context(StrContext::Expected("range 2 to 8".into()))
.parse_next(input)
}
fn size_of_lengths(input: &mut &[u8]) -> ModalResult<u8> {
le_u8
.verify(|sz| matches!(sz, 2..=8))
.context(StrContext::Label("Size of Lengths"))
.context(StrContext::Expected("range 2 to 8".into()))
.parse_next(input)
}
fn indexed_storage(ver: SuperBlockVersion) -> impl FnMut(&mut &[u8]) -> ModalResult<Option<u16>> {
move |input| match ver.0 {
1 => {
let indexed_storage_internal_node_k = le_u16.parse_next(input)?;
let _reserved = le_u32.parse_next(input)?;
Ok(Some(indexed_storage_internal_node_k))
}
_ => Ok(None),
}
}
fn super_block_ver_0_or_1(
ver: SuperBlockVersion,
) -> impl FnMut(&mut &[u8]) -> ModalResult<SuperBlock> {
move |input: &mut &[u8]| {
let _version_ext = version_ext.parse_next(input)?;
let size_of_offsets = size_of_offsets.parse_next(input)?;
let size_of_lengths = size_of_lengths.parse_next(input)?;
let _reserved = le_u8.verify(|r| *r == 0).parse_next(input)?;
let _group_leaf_node_k = le_u16.parse_next(input)?;
let _group_internal_node_k = le_u16.parse_next(input)?;
let _file_consistency_flags = le_u32.verify(|f| *f == 0).parse_next(input)?;
let _indexed_storage = indexed_storage(ver).parse_next(input)?;
let base_address = varint_size(size_of_offsets)
.verify(|a| *a == 0)
.parse_next(input)?;
let _address_of_file_free_space = varint_size(size_of_offsets).parse_next(input)?;
let end_of_file_address = varint_size(size_of_offsets).parse_next(input)?;
let _driver_info_block_address = varint_size(size_of_offsets).parse_next(input)?;
let _link_name_offset = varint_size(size_of_offsets).parse_next(input)?;
let root_group_object_header_address = varint_size(size_of_offsets).parse_next(input)?;
let _cache_type = le_u32.verify(|t| *t <= 2).parse_next(input)?;
Ok(SuperBlock {
size_of_offsets,
size_of_lengths,
base_address,
end_of_file_address,
root_group_object_header_address,
superblock_extension_address: None,
})
}
}
fn super_block_ver_2_or_3(input: &mut &[u8]) -> ModalResult<SuperBlock> {
let size_of_offsets = size_of_offsets.parse_next(input)?;
let size_of_lengths = size_of_lengths.parse_next(input)?;
let _file_consistency_flags = le_u8.parse_next(input)?;
let base_address = varint_size(size_of_offsets)
.verify(|a| *a == 0)
.parse_next(input)?;
let super_block_extension_address = varint_size(size_of_offsets).parse_next(input)?;
let end_of_file_address = varint_size(size_of_offsets).parse_next(input)?;
let root_group_object_header_address = varint_size(size_of_offsets).parse_next(input)?;
Ok(SuperBlock {
size_of_offsets,
size_of_lengths,
base_address,
end_of_file_address,
root_group_object_header_address,
superblock_extension_address: Some(super_block_extension_address),
})
}
pub(crate) fn super_block(input: &mut &[u8]) -> ModalResult<SuperBlock> {
let _signature = literal(FORMAT_SIGNATURE).parse_next(input)?;
let ver = version.parse_next(input)?;
match ver.0 {
0 | 1 => super_block_ver_0_or_1(ver).parse_next(input),
2 | 3 => super_block_ver_2_or_3.parse_next(input),
_ => unreachable!(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_signature_rejected() {
let mut input: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert!(super_block(&mut input).is_err());
}
#[test]
fn test_invalid_version_rejected() {
let mut input: &[u8] = &[0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a, 0x04];
assert!(super_block(&mut input).is_err());
}
#[test]
fn test_size_of_offsets_valid_range() {
let mut input: &[u8] = &[1]; assert!(size_of_offsets(&mut input).is_err());
let mut input: &[u8] = &[9]; assert!(size_of_offsets(&mut input).is_err());
let mut input: &[u8] = &[4]; assert!(size_of_offsets(&mut input).is_ok());
}
}