1use strum::{Display, EnumDiscriminants, EnumString};
2use thiserror::Error;
3mod xml;
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumString, Display)]
6#[strum(serialize_all = "lowercase")]
7#[non_exhaustive]
8pub enum HashType {
9 Sha256,
10}
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
13#[non_exhaustive]
14pub enum HashValue {
15 Sha256([u8; 32]),
16}
17
18impl HashValue {
19 pub fn to_type(&self) -> HashType {
20 match self {
21 HashValue::Sha256(_) => HashType::Sha256,
22 }
23 }
24
25 pub fn as_slice(&self) -> &[u8] {
26 match self {
27 HashValue::Sha256(v) => v,
28 }
29 }
30}
31
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct BlockRange {
34 offset: u64,
35 length: u64,
36 checksum: HashValue,
37}
38
39impl BlockRange {
40 pub fn checksum(&self) -> HashValue {
41 self.checksum
42 }
43
44 pub fn offset(&self) -> u64 {
45 self.offset
46 }
47
48 pub fn length(&self) -> u64 {
49 self.length
50 }
51}
52
53#[derive(Clone, Debug)]
54pub struct Bmap {
55 image_size: u64,
56 block_size: u64,
57 blocks: u64,
58 mapped_blocks: u64,
59 checksum_type: HashType,
60 blockmap: Vec<BlockRange>,
61}
62
63impl Bmap {
64 pub fn builder() -> BmapBuilder {
65 BmapBuilder::default()
66 }
67
68 pub fn from_xml(xml: &str) -> Result<Self, xml::XmlError> {
70 xml::from_xml(xml)
71 }
72
73 pub fn image_size(&self) -> u64 {
75 self.image_size
76 }
77
78 pub const fn block_size(&self) -> u64 {
80 self.block_size
81 }
82
83 pub fn blocks(&self) -> u64 {
85 self.blocks
86 }
87
88 pub fn mapped_blocks(&self) -> u64 {
90 self.mapped_blocks
91 }
92
93 pub fn checksum_type(&self) -> HashType {
95 self.checksum_type
96 }
97
98 pub fn block_map(&self) -> impl ExactSizeIterator + Iterator<Item = &BlockRange> {
100 self.blockmap.iter()
101 }
102
103 pub fn total_mapped_size(&self) -> u64 {
105 self.block_size * self.mapped_blocks
106 }
107}
108
109#[derive(Clone, Debug, Error)]
110pub enum BmapBuilderError {
111 #[error("Image size missing")]
112 MissingImageSize,
113 #[error("Block size missing")]
114 MissingBlockSize,
115 #[error("Blocks missing")]
116 MissingBlocks,
117 #[error("Mapped blocks missing")]
118 MissingMappedBlocks,
119 #[error("Checksum type missing")]
120 MissingChecksumType,
121 #[error("No block ranges")]
122 NoBlockRanges,
123}
124
125#[derive(Clone, Debug, Default)]
126pub struct BmapBuilder {
127 image_size: Option<u64>,
128 block_size: Option<u64>,
129 blocks: Option<u64>,
130 checksum_type: Option<HashType>,
131 mapped_blocks: Option<u64>,
132 blockmap: Vec<BlockRange>,
133}
134
135impl BmapBuilder {
136 pub fn image_size(&mut self, size: u64) -> &mut Self {
137 self.image_size = Some(size);
138 self
139 }
140
141 pub fn block_size(&mut self, block_size: u64) -> &mut Self {
142 self.block_size = Some(block_size);
143 self
144 }
145
146 pub fn blocks(&mut self, blocks: u64) -> &mut Self {
147 self.blocks = Some(blocks);
148 self
149 }
150
151 pub fn mapped_blocks(&mut self, blocks: u64) -> &mut Self {
152 self.mapped_blocks = Some(blocks);
153 self
154 }
155
156 pub fn checksum_type(&mut self, checksum_type: HashType) -> &mut Self {
157 self.checksum_type = Some(checksum_type);
158 self
159 }
160
161 pub fn add_block_range(&mut self, start: u64, end: u64, checksum: HashValue) -> &mut Self {
162 let bs = self.block_size.expect("Blocksize needs to be set first");
163 let total = self.image_size.expect("Image size needs to be set first");
164 let offset = start * bs;
165 let length = (total - offset).min((end - start + 1) * bs);
166 self.add_byte_range(offset, length, checksum)
167 }
168
169 pub fn add_byte_range(&mut self, offset: u64, length: u64, checksum: HashValue) -> &mut Self {
170 let range = BlockRange {
171 offset,
172 length,
173 checksum,
174 };
175 self.blockmap.push(range);
176 self
177 }
178
179 pub fn build(self) -> Result<Bmap, BmapBuilderError> {
180 let image_size = self.image_size.ok_or(BmapBuilderError::MissingImageSize)?;
181 let block_size = self.block_size.ok_or(BmapBuilderError::MissingBlockSize)?;
182 let blocks = self.blocks.ok_or(BmapBuilderError::MissingBlocks)?;
183 let mapped_blocks = self
184 .mapped_blocks
185 .ok_or(BmapBuilderError::MissingMappedBlocks)?;
186 let checksum_type = self
187 .checksum_type
188 .ok_or(BmapBuilderError::MissingChecksumType)?;
189 let blockmap = self.blockmap;
190
191 Ok(Bmap {
192 image_size,
193 block_size,
194 blocks,
195 mapped_blocks,
196 checksum_type,
197 blockmap,
198 })
199 }
200}
201
202#[cfg(test)]
203mod test {
204 use super::*;
205 use std::str::FromStr;
206
207 #[test]
208 fn hashes() {
209 assert_eq!("sha256", &HashType::Sha256.to_string());
210 assert_eq!(HashType::Sha256, HashType::from_str("sha256").unwrap());
211 let h = HashValue::Sha256([0; 32]);
212 assert_eq!(HashType::Sha256, h.to_type());
213 }
214}