harmos_storage/
file_checkpoint.rs1use serde::{Deserialize, Serialize};
19use sha2::digest::common::hazmat::{SerializableState, SerializedState};
20use sha2::{Digest, Sha256};
21use super::{FileMetadata, FileSession};
23use crate::{Error, MemorySize, Result};
24
25#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(deny_unknown_fields)]
35pub struct FileCheckpoint {
36 pub metadata: FileMetadata,
37 pub session: FileSession,
38 pub length: u64,
39 pub hash_format: u32,
40 pub hash_state: Vec<u8>,
41}
42
43impl FileCheckpoint {
50 pub(super) fn new(metadata: FileMetadata, session: FileSession, length: u64, hash: &Sha256) -> Self {
51 Self {
52 metadata,
53 session,
54 length,
55 hash_format: 1,
56 hash_state: hash.serialize().to_vec(),
57 }
58 }
59
60 pub fn sha256(&self) -> Result<[u8; 32]> {
61 Ok(self.restore_hash()?.finalize().into())
62 }
63
64 pub(super) fn restore_hash(&self) -> Result<Sha256> {
65 if self.hash_format != 1
66 || self.session.generation == 0
67 || self.metadata.id != self.session.id
68 || self.metadata.expected_length.is_some_and(|length| self.length > length)
69 {
70 return Err(invalid());
71 }
72 if self.hash_state.len() != 104 {
75 return Err(invalid());
76 }
77 let blocks = u64::from_le_bytes(self.hash_state[32..40].try_into().map_err(|_| invalid())?);
78 let length = blocks.checked_mul(64).and_then(|length| length.checked_add(u64::from(self.hash_state[40])));
79 if length != Some(self.length) {
80 return Err(invalid());
81 }
82 let encoded = <&SerializedState<Sha256>>::try_from(self.hash_state.as_slice()).map_err(|_| invalid())?;
83 Sha256::deserialize(encoded).map_err(|_| invalid())
84 }
85}
86
87impl MemorySize for FileCheckpoint {
88 fn memory_size(&self) -> usize {
89 std::mem::size_of::<Self>()
90 .saturating_add(self.metadata.memory_size().saturating_sub(std::mem::size_of::<FileMetadata>()))
91 .saturating_add(self.session.memory_size().saturating_sub(std::mem::size_of::<FileSession>()))
92 .saturating_add(self.hash_state.capacity())
93 }
94}
95
96fn invalid() -> Error {
101 Error::InvalidInput {
102 field: "file checkpoint",
103 reason: "invalid metadata, generation or hash state",
104 }
105}
106
107