#![forbid(unsafe_code)]
use crate::core::extent::ChunkId;
use crate::format::superblock::Superblock;
use crate::store::root::Root;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RootVerifyError {
RootIdMismatch {
expected: ChunkId,
actual: ChunkId,
},
RootDecode,
GenerationMismatch {
sb: u64,
root: u64,
},
RootObjectMissing,
}
impl std::fmt::Display for RootVerifyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for RootVerifyError {}
pub fn verify_root_payload(sb: &Superblock, root_payload: &[u8]) -> Result<(), RootVerifyError> {
let actual = ChunkId::of(root_payload);
if actual != sb.root_object_id {
return Err(RootVerifyError::RootIdMismatch {
expected: sb.root_object_id,
actual,
});
}
Ok(())
}
pub fn verify_root(sb: &Superblock, root_payload: &[u8]) -> Result<Root, RootVerifyError> {
verify_root_payload(sb, root_payload)?;
let root = Root::decode(root_payload).map_err(|_| RootVerifyError::RootDecode)?;
if root.generation != sb.generation {
return Err(RootVerifyError::GenerationMismatch {
sb: sb.generation,
root: root.generation,
});
}
Ok(root)
}
pub fn verify_snapshot_root(
expected_id: &ChunkId,
root_payload: &[u8],
) -> Result<Root, RootVerifyError> {
let actual = ChunkId::of(root_payload);
if &actual != expected_id {
return Err(RootVerifyError::RootIdMismatch {
expected: *expected_id,
actual,
});
}
Root::decode(root_payload).map_err(|_| RootVerifyError::RootDecode)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn root_payload_binding() {
let root = Root {
generation: 7,
..Default::default()
};
let payload = root.encode();
let mut sb = Superblock {
generation: 7,
..Default::default()
};
sb.root_object_id = ChunkId::of(&payload);
assert!(verify_root(&sb, &payload).is_ok());
sb.generation = 8;
assert!(matches!(
verify_root(&sb, &payload),
Err(RootVerifyError::GenerationMismatch { .. })
));
}
#[test]
fn id_mismatch_detected() {
let root = Root::default();
let payload = root.encode();
let sb = Superblock {
root_object_id: ChunkId::of(b"other"),
..Default::default()
};
assert!(matches!(
verify_root(&sb, &payload),
Err(RootVerifyError::RootIdMismatch { .. })
));
}
}