|
| 1 | +use strum::{Display, EnumDiscriminants, EnumString}; |
| 2 | +use thiserror::Error; |
| 3 | +mod xml; |
| 4 | + |
| 5 | +#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumString, Display)] |
| 6 | +#[strum(serialize_all = "lowercase")] |
| 7 | +#[non_exhaustive] |
| 8 | +pub enum HashType { |
| 9 | + Sha256, |
| 10 | +} |
| 11 | + |
| 12 | +#[derive(Copy, Clone, Debug, PartialEq, Eq, EnumDiscriminants)] |
| 13 | +#[non_exhaustive] |
| 14 | +pub enum HashValue { |
| 15 | + Sha256([u8; 32]), |
| 16 | +} |
| 17 | + |
| 18 | +impl 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)] |
| 33 | +pub struct BlockRange { |
| 34 | + offset: u64, |
| 35 | + length: u64, |
| 36 | + checksum: HashValue, |
| 37 | +} |
| 38 | + |
| 39 | +impl 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)] |
| 54 | +pub struct Bmap { |
| 55 | + image_size: u64, |
| 56 | + block_size: u64, |
| 57 | + blocks: u64, |
| 58 | + mapped_blocks: u64, |
| 59 | + checksum_type: HashType, |
| 60 | + bmap_file_checksum: String, |
| 61 | + blockmap: Vec<BlockRange>, |
| 62 | +} |
| 63 | + |
| 64 | +impl Bmap { |
| 65 | + pub fn builder() -> BmapBuilder { |
| 66 | + BmapBuilder::default() |
| 67 | + } |
| 68 | + |
| 69 | + pub fn from_xml(xml: &str) -> Result<Self, xml::XmlError> { |
| 70 | + xml::from_xml(xml) |
| 71 | + } |
| 72 | + |
| 73 | + pub fn image_size(&self) -> u64 { |
| 74 | + self.image_size |
| 75 | + } |
| 76 | + |
| 77 | + pub const fn block_size(&self) -> u64 { |
| 78 | + self.block_size |
| 79 | + } |
| 80 | + |
| 81 | + pub fn blocks(&self) -> u64 { |
| 82 | + self.blocks |
| 83 | + } |
| 84 | + |
| 85 | + pub fn mapped_blocks(&self) -> u64 { |
| 86 | + self.mapped_blocks |
| 87 | + } |
| 88 | + |
| 89 | + pub fn checksum_type(&self) -> HashType { |
| 90 | + self.checksum_type |
| 91 | + } |
| 92 | + |
| 93 | + pub fn block_map(&self) -> impl ExactSizeIterator + Iterator<Item = &BlockRange> { |
| 94 | + self.blockmap.iter() |
| 95 | + } |
| 96 | + |
| 97 | + pub fn bmap_file_checksum(&self) -> String { |
| 98 | + self.bmap_file_checksum.clone() |
| 99 | + } |
| 100 | + |
| 101 | + pub fn total_mapped_size(&self) -> u64 { |
| 102 | + self.block_size * self.mapped_blocks |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +#[derive(Clone, Debug, Error)] |
| 107 | +pub enum BmapBuilderError { |
| 108 | + #[error("Image size missing")] |
| 109 | + MissingImageSize, |
| 110 | + #[error("Block size missing")] |
| 111 | + MissingBlockSize, |
| 112 | + #[error("Blocks missing")] |
| 113 | + MissingBlocks, |
| 114 | + #[error("Mapped blocks missing")] |
| 115 | + MissingMappedBlocks, |
| 116 | + #[error("Checksum type missing")] |
| 117 | + MissingChecksumType, |
| 118 | + #[error("Bmap file checksum missing")] |
| 119 | + MissingBmapFileChecksum, |
| 120 | + #[error("No block ranges")] |
| 121 | + NoBlockRanges, |
| 122 | +} |
| 123 | + |
| 124 | +#[derive(Clone, Debug, Default)] |
| 125 | +pub struct BmapBuilder { |
| 126 | + image_size: Option<u64>, |
| 127 | + block_size: Option<u64>, |
| 128 | + blocks: Option<u64>, |
| 129 | + checksum_type: Option<HashType>, |
| 130 | + mapped_blocks: Option<u64>, |
| 131 | + bmap_file_checksum: Option<String>, |
| 132 | + blockmap: Vec<BlockRange>, |
| 133 | +} |
| 134 | + |
| 135 | +impl 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 bmap_file_checksum(&mut self, bmap_file_checksum: String) -> &mut Self { |
| 162 | + self.bmap_file_checksum = Some(bmap_file_checksum); |
| 163 | + self |
| 164 | + } |
| 165 | + |
| 166 | + pub fn add_block_range(&mut self, start: u64, end: u64, checksum: HashValue) -> &mut Self { |
| 167 | + let bs = self.block_size.expect("Blocksize needs to be set first"); |
| 168 | + let total = self.image_size.expect("Image size needs to be set first"); |
| 169 | + let offset = start * bs; |
| 170 | + let length = (total - offset).min((end - start + 1) * bs); |
| 171 | + self.add_byte_range(offset, length, checksum) |
| 172 | + } |
| 173 | + |
| 174 | + pub fn add_byte_range(&mut self, offset: u64, length: u64, checksum: HashValue) -> &mut Self { |
| 175 | + let range = BlockRange { |
| 176 | + offset, |
| 177 | + length, |
| 178 | + checksum, |
| 179 | + }; |
| 180 | + self.blockmap.push(range); |
| 181 | + self |
| 182 | + } |
| 183 | + |
| 184 | + pub fn build(self) -> Result<Bmap, BmapBuilderError> { |
| 185 | + let image_size = self.image_size.ok_or(BmapBuilderError::MissingImageSize)?; |
| 186 | + let block_size = self.block_size.ok_or(BmapBuilderError::MissingBlockSize)?; |
| 187 | + let blocks = self.blocks.ok_or(BmapBuilderError::MissingBlocks)?; |
| 188 | + let mapped_blocks = self |
| 189 | + .mapped_blocks |
| 190 | + .ok_or(BmapBuilderError::MissingMappedBlocks)?; |
| 191 | + let checksum_type = self |
| 192 | + .checksum_type |
| 193 | + .ok_or(BmapBuilderError::MissingChecksumType)?; |
| 194 | + let bmap_file_checksum = self |
| 195 | + .bmap_file_checksum |
| 196 | + .ok_or(BmapBuilderError::MissingBmapFileChecksum)?; |
| 197 | + let blockmap = self.blockmap; |
| 198 | + |
| 199 | + Ok(Bmap { |
| 200 | + image_size, |
| 201 | + block_size, |
| 202 | + blocks, |
| 203 | + mapped_blocks, |
| 204 | + checksum_type, |
| 205 | + bmap_file_checksum, |
| 206 | + blockmap, |
| 207 | + }) |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +#[cfg(test)] |
| 212 | +mod test { |
| 213 | + use super::*; |
| 214 | + use std::str::FromStr; |
| 215 | + |
| 216 | + #[test] |
| 217 | + fn hashes() { |
| 218 | + assert_eq!("sha256", &HashType::Sha256.to_string()); |
| 219 | + assert_eq!(HashType::Sha256, HashType::from_str("sha256").unwrap()); |
| 220 | + let h = HashValue::Sha256([0; 32]); |
| 221 | + assert_eq!(HashType::Sha256, h.to_type()); |
| 222 | + } |
| 223 | +} |
0 commit comments