use super::data_model::DataInit;
use anyhow::{bail, Context, Result};
use std::{io::Write, mem::size_of, path::Path};
use super::DmTargetSpec;
pub struct DmVerityTarget(pub Box<[u8]>);
pub enum DmVerityVersion {
V1,
}
#[allow(dead_code)]
pub enum DmVerityHashAlgorithm {
SHA256,
SHA512,
}
pub struct DmVerityTargetBuilder<'a> {
version: DmVerityVersion,
data_device: Option<&'a Path>,
data_size: u64,
data_block_size: u64,
hash_device: Option<&'a Path>,
hash_block_size: u64,
hash_algorithm: DmVerityHashAlgorithm,
root_digest: Option<&'a str>,
salt: Option<&'a [u8]>,
}
impl DmVerityTarget {
pub fn as_slice(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Default for DmVerityTargetBuilder<'_> {
fn default() -> Self {
DmVerityTargetBuilder {
version: DmVerityVersion::V1,
data_device: None,
data_size: 0,
data_block_size: 0,
hash_device: None,
hash_block_size: 0,
hash_algorithm: DmVerityHashAlgorithm::SHA256,
root_digest: None,
salt: None,
}
}
}
impl<'a> DmVerityTargetBuilder<'a> {
pub fn data_device(&mut self, p: &'a Path, size: u64, block_size: u64) -> &mut Self {
self.data_device = Some(p);
self.data_size = size;
self.data_block_size = block_size;
self
}
pub fn hash_device(&mut self, p: &'a Path, block_size: u64) -> &mut Self {
self.hash_device = Some(p);
self.hash_block_size = block_size;
self
}
pub fn hash_algorithm(&mut self, algo: DmVerityHashAlgorithm) -> &mut Self {
self.hash_algorithm = algo;
self
}
pub fn root_digest(&mut self, digest: &'a str) -> &mut Self {
self.root_digest = Some(digest);
self
}
pub fn salt(&mut self, salt: &'a [u8]) -> &mut Self {
self.salt = Some(salt);
self
}
pub fn build(&self) -> Result<DmVerityTarget> {
let version = match self.version {
DmVerityVersion::V1 => 1,
};
let data_device_path = self
.data_device
.context("data device is not set")?
.to_str()
.context("data device path is not encoded in utf8")?;
let data_block_size = self.data_block_size;
let data_size = self.data_size;
let num_data_blocks = data_size / data_block_size;
let hash_device_path = self
.hash_device
.context("hash device is not set")?
.to_str()
.context("hash device path is not encoded in utf8")?;
let hash_block_size = self.hash_block_size;
let hash_algorithm = match self.hash_algorithm {
DmVerityHashAlgorithm::SHA256 => "sha256",
DmVerityHashAlgorithm::SHA512 => "sha512",
};
let root_digest = if let Some(root_digest) = self.root_digest {
root_digest
} else {
bail!("root digest is not set")
};
let salt = match self.salt {
Some(salt) if salt.is_empty() => hex::encode(salt),
Some(salt) => hex::encode(salt),
None => "-".into(),
};
let mut body = String::with_capacity(4096);
use std::fmt::Write;
write!(&mut body, "{} ", version)?;
write!(&mut body, "{} ", data_device_path)?;
write!(&mut body, "{} ", hash_device_path)?;
write!(&mut body, "{} ", data_block_size)?;
write!(&mut body, "{} ", hash_block_size)?;
write!(&mut body, "{} ", num_data_blocks)?;
write!(&mut body, "{} ", num_data_blocks + 1)?; write!(&mut body, "{} ", hash_algorithm)?;
write!(&mut body, "{} ", root_digest)?;
write!(&mut body, "{}", salt)?;
write!(&mut body, "\0")?;
let size = size_of::<DmTargetSpec>() + body.len();
let aligned_size = (size + 7) & !7; let padding = aligned_size - size;
let mut header = DmTargetSpec::new("verity")?;
header.sector_start = 0;
header.length = data_size / 512; header.next = aligned_size as u32;
let mut buf = Vec::with_capacity(aligned_size);
buf.write_all(header.as_slice())?;
buf.write_all(body.as_bytes())?;
buf.write_all(vec![0; padding].as_slice())?;
Ok(DmVerityTarget(buf.into_boxed_slice()))
}
}