bmap_parser/
bmap.rs

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    /// Build from a .bmap xml file
69    pub fn from_xml(xml: &str) -> Result<Self, xml::XmlError> {
70        xml::from_xml(xml)
71    }
72
73    /// Image size in bytes
74    pub fn image_size(&self) -> u64 {
75        self.image_size
76    }
77
78    /// block size in bytes
79    pub const fn block_size(&self) -> u64 {
80        self.block_size
81    }
82
83    /// number of blocks in the image
84    pub fn blocks(&self) -> u64 {
85        self.blocks
86    }
87
88    /// number of mapped blocks in the image
89    pub fn mapped_blocks(&self) -> u64 {
90        self.mapped_blocks
91    }
92
93    /// checksum type used
94    pub fn checksum_type(&self) -> HashType {
95        self.checksum_type
96    }
97
98    /// Iterator over the block map
99    pub fn block_map(&self) -> impl ExactSizeIterator + Iterator<Item = &BlockRange> {
100        self.blockmap.iter()
101    }
102
103    /// Total mapped size in bytes
104    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}