diff --git a/Cargo.lock b/Cargo.lock index 761163b..219c98a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -803,6 +803,7 @@ dependencies = [ "thiserror", "threadpool", "tui", + "typenum", ] [[package]] @@ -856,6 +857,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-segmentation" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index a98b956..9221f2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ threadpool = "1.8" thiserror = "1.0" tui = "0.10" termion = "1.5" +typenum = "1.12.0" [dev-dependencies] json = "0.12" diff --git a/src/cache/hint.rs b/src/cache/hint.rs new file mode 100644 index 0000000..ef0d83f --- /dev/null +++ b/src/cache/hint.rs @@ -0,0 +1,33 @@ +use nom::IResult; +use std::marker::PhantomData; +use std::convert::TryInto; + +use crate::pdata::unpack::*; + +//------------------------------------------ + +#[derive(Clone, Copy)] +pub struct Hint { + pub hint: [u8; 4], // FIXME: support various hint sizes + _not_used: PhantomData, +} + +impl Unpack for Hint { + fn disk_size() -> u32 { + Width::to_u32() + } + + // FIXME: support different width + fn unpack(i: &[u8]) -> IResult<&[u8], Hint> { + let size = Width::to_usize(); + Ok(( + &i[size..], + Hint { + hint: i[0..size].try_into().unwrap(), + _not_used: PhantomData, + }, + )) + } +} + +//------------------------------------------ diff --git a/src/cache/mapping.rs b/src/cache/mapping.rs new file mode 100644 index 0000000..c18325a --- /dev/null +++ b/src/cache/mapping.rs @@ -0,0 +1,50 @@ +use nom::IResult; +use nom::number::complete::*; + +use crate::pdata::unpack::*; + +//------------------------------------------ + +static FLAGS_MASK: u64 = (1 << 16) - 1; + +//------------------------------------------ + +pub enum MappingFlags { + Valid = 1, + Dirty = 2, +} + +#[derive(Clone, Copy)] +pub struct Mapping { + pub oblock: u64, + pub flags: u32, +} + +impl Mapping { + pub fn is_valid(&self) -> bool { + return (self.flags & MappingFlags::Valid as u32) != 0; + } +} + + +impl Unpack for Mapping { + fn disk_size() -> u32 { + 8 + } + + fn unpack(i: &[u8]) -> IResult<&[u8], Mapping> { + let (i, n) = le_u64(i)?; + let oblock = n >> 16; + let flags = n & FLAGS_MASK; + + Ok(( + i, + Mapping { + oblock, + flags: flags as u32, + }, + )) + } +} + +//------------------------------------------ diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 2910ec6..f1cf219 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -1 +1,4 @@ +pub mod hint; +pub mod mapping; +pub mod superblock; pub mod xml; diff --git a/src/cache/superblock.rs b/src/cache/superblock.rs new file mode 100644 index 0000000..e98a3e9 --- /dev/null +++ b/src/cache/superblock.rs @@ -0,0 +1,139 @@ +use anyhow::{anyhow, Result}; + +use crate::io_engine::*; +use nom::{bytes::complete::*, number::complete::*, IResult}; + +//------------------------------------------ + +pub const SUPERBLOCK_LOCATION: u64 = 0; + +const POLICY_NAME_SIZE: usize = 16; +const SPACE_MAP_ROOT_SIZE: usize = 128; + +//------------------------------------------ + +#[derive(Debug, Clone)] +pub struct SuperblockFlags { + pub needs_check: bool, +} + +#[derive(Debug, Clone)] +pub struct Superblock { + pub flags: SuperblockFlags, + pub block: u64, + pub version: u32, + + pub policy_name: Vec, + pub policy_version: Vec, + pub policy_hint_size: u32, + + pub metadata_sm_root: Vec, + pub mapping_root: u64, + pub dirty_root: Option, // format 2 only + pub hint_root: u64, + + pub discard_root: u64, + pub discard_block_size: u64, + pub discard_nr_blocks: u64, + + pub data_block_size: u32, + pub metadata_block_size: u32, + pub cache_blocks: u32, + + pub compat_flags: u32, + pub compat_ro_flags: u32, + pub incompat_flags: u32, + + pub read_hits: u32, + pub read_misses: u32, + pub write_hits: u32, + pub write_misses: u32, +} + +fn unpack(data: &[u8]) -> IResult<&[u8], Superblock> { + let (i, _csum) = le_u32(data)?; + let (i, flags) = le_u32(i)?; + let (i, block) = le_u64(i)?; + let (i, _uuid) = take(16usize)(i)?; + let (i, _magic) = le_u64(i)?; + let (i, version) = le_u32(i)?; + + let (i, policy_name) = take(POLICY_NAME_SIZE)(i)?; + let (i, policy_hint_size) = le_u32(i)?; + + let (i, metadata_sm_root) = take(SPACE_MAP_ROOT_SIZE)(i)?; + let (i, mapping_root) = le_u64(i)?; + let (i, hint_root) = le_u64(i)?; + + let (i, discard_root) = le_u64(i)?; + let (i, discard_block_size) = le_u64(i)?; + let (i, discard_nr_blocks) = le_u64(i)?; + + let (i, data_block_size) = le_u32(i)?; + let (i, metadata_block_size) = le_u32(i)?; + let (i, cache_blocks) = le_u32(i)?; + + let (i, compat_flags) = le_u32(i)?; + let (i, compat_ro_flags) = le_u32(i)?; + let (i, incompat_flags) = le_u32(i)?; + + let (i, read_hits) = le_u32(i)?; + let (i, read_misses) = le_u32(i)?; + let (i, write_hits) = le_u32(i)?; + let (i, write_misses) = le_u32(i)?; + + let (i, vsn_major) = le_u32(i)?; + let (i, vsn_minor) = le_u32(i)?; + let (i, vsn_patch) = le_u32(i)?; + + let mut i = i; + let mut dirty_root = None; + if version >= 2 { + let (m, root) = le_u64(i)?; + dirty_root = Some(root); + i = &m; + } + + Ok(( + i, + Superblock { + flags: SuperblockFlags { + needs_check: (flags | 0x1) != 0, + }, + block, + version, + policy_name: policy_name.to_vec(), + policy_version: vec![vsn_major, vsn_minor, vsn_patch], + policy_hint_size, + metadata_sm_root: metadata_sm_root.to_vec(), + mapping_root, + dirty_root, + hint_root, + discard_root, + discard_block_size, + discard_nr_blocks, + data_block_size, + metadata_block_size, + cache_blocks, + compat_flags, + compat_ro_flags, + incompat_flags, + read_hits, + read_misses, + write_hits, + write_misses, + }, + )) +} + +pub fn read_superblock(engine: &dyn IoEngine, loc: u64) -> Result { + let b = engine.read(loc)?; + + if let Ok((_, sb)) = unpack(&b.get_data()) { + Ok(sb) + } else { + Err(anyhow!("couldn't unpack superblock")) + } +} + +//------------------------------------------