forked from brl/citadel-tools
refactored scattered functions into a class
This commit is contained in:
parent
81e9e224fc
commit
8a65aa1708
@ -1,117 +1,118 @@
|
|||||||
use std::path::{Path,PathBuf};
|
use std::path::{Path,PathBuf};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
use std::fs::{self, OpenOptions,File};
|
use std::fs::{self, OpenOptions,File};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use failure::ResultExt;
|
use crate::{Result, MetaInfo, Partition, LoopDevice, Mountpoint};
|
||||||
use crate::{Result,MetaInfo,Partition,util};
|
|
||||||
|
|
||||||
const VERITYSETUP: &str = "/sbin/veritysetup";
|
|
||||||
const LOSETUP: &str = "/sbin/losetup";
|
|
||||||
|
|
||||||
/// Generate dm-verity hashtree for a disk image and store in external file.
|
pub struct Verity {
|
||||||
/// Parse output from veritysetup command and return as `VerityOutput`.
|
image: PathBuf,
|
||||||
pub fn generate_initial_hashtree<P: AsRef<Path>, Q:AsRef<Path>>(source: P, hashtree: Q) -> Result<VerityOutput> {
|
|
||||||
let args = format!("format {} {}", source.as_ref().display(), hashtree.as_ref().display());
|
|
||||||
// Don't use absolute path to veritysetup so that the build will correctly find the version from cryptsetup-native
|
|
||||||
let output = util::exec_cmdline_with_output("veritysetup", args)
|
|
||||||
.context("creating initial hashtree with veritysetup format failed")?;
|
|
||||||
Ok(VerityOutput::parse(&output))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_image_hashtree<P: AsRef<Path>>(image: P, metainfo: &MetaInfo) -> Result<VerityOutput> {
|
impl Verity {
|
||||||
let verityfile = image.as_ref().with_extension("verity");
|
const VERITYSETUP: &'static str = "/sbin/veritysetup";
|
||||||
|
|
||||||
|
pub fn new(image: impl AsRef<Path>) -> Self {
|
||||||
|
let image = image.as_ref().to_path_buf();
|
||||||
|
Verity { image }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_initial_hashtree(&self, output: impl AsRef<Path>) -> Result<VerityOutput> {
|
||||||
|
let output = output.as_ref();
|
||||||
|
// Don't use absolute path to veritysetup so that the build will correctly find the version from cryptsetup-native
|
||||||
|
let output = cmd_with_output!("veritysetup", "format {} {}", self.path_str(), output.display())?;
|
||||||
|
Ok(VerityOutput::parse(&output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_image_hashtree(&self, metainfo: &MetaInfo) -> Result<VerityOutput> {
|
||||||
|
let verity_salt = metainfo.verity_salt();
|
||||||
|
self.generate_image_hashtree_with_salt(metainfo, verity_salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_image_hashtree_with_salt(&self, metainfo: &MetaInfo, salt: &str) -> Result<VerityOutput> {
|
||||||
|
|
||||||
|
let verityfile = self.image.with_extension("verity");
|
||||||
|
let nblocks = metainfo.nblocks();
|
||||||
|
|
||||||
// Make sure file size is correct or else verity tree will be appended in wrong place
|
// Make sure file size is correct or else verity tree will be appended in wrong place
|
||||||
let meta = image.as_ref().metadata()?;
|
let meta = self.image.metadata()?;
|
||||||
let len = meta.len() as usize;
|
let len = meta.len() as usize;
|
||||||
let expected = (metainfo.nblocks() + 1) * 4096;
|
let expected = (nblocks + 1) * 4096;
|
||||||
if len != expected {
|
if len != expected {
|
||||||
bail!("Actual file size ({}) does not match expected size ({})", len, expected);
|
bail!("Actual file size ({}) does not match expected size ({})", len, expected);
|
||||||
}
|
}
|
||||||
|
let vout = LoopDevice::with_loop(self.path(), Some(4096), true, |loopdev| {
|
||||||
let vout = with_loopdev(image.as_ref(), |loopdev| {
|
let output = cmd_with_output!(Self::VERITYSETUP, "--data-blocks={} --salt={} format {} {}",
|
||||||
let args = format!("--data-blocks={} --salt={} format {} {}",
|
nblocks, salt, loopdev, verityfile.display())?;
|
||||||
metainfo.nblocks(), metainfo.verity_salt(),
|
|
||||||
loopdev, verityfile.display());
|
|
||||||
|
|
||||||
let output = util::exec_cmdline_with_output(VERITYSETUP, args)?;
|
|
||||||
Ok(VerityOutput::parse(&output))
|
Ok(VerityOutput::parse(&output))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut input = File::open(&verityfile)?;
|
let mut input = File::open(&verityfile)?;
|
||||||
let mut output = OpenOptions::new().append(true).open(image.as_ref())?;
|
let mut output = OpenOptions::new().append(true).open(self.path())?;
|
||||||
io::copy(&mut input, &mut output)?;
|
io::copy(&mut input, &mut output)?;
|
||||||
fs::remove_file(&verityfile)?;
|
fs::remove_file(&verityfile)?;
|
||||||
|
|
||||||
Ok(vout)
|
Ok(vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_image<P: AsRef<Path>>(image: P, metainfo: &MetaInfo) -> Result<bool> {
|
pub fn verify(&self, metainfo: &MetaInfo) -> Result<bool> {
|
||||||
with_loopdev(image.as_ref(), |loopdev| {
|
LoopDevice::with_loop(self.path(), Some(4096), true, |loopdev| {
|
||||||
let status = Command::new(VERITYSETUP)
|
cmd_ok!(Self::VERITYSETUP, "--hash-offset={} verify {} {} {}",
|
||||||
.arg(format!("--hash-offset={}", metainfo.nblocks() * 4096))
|
metainfo.nblocks() * 4096,
|
||||||
.arg("verify")
|
loopdev, loopdev, metainfo.verity_root())
|
||||||
.arg(&loopdev)
|
|
||||||
.arg(&loopdev)
|
|
||||||
.arg(metainfo.verity_root())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status()?;
|
|
||||||
Ok(status.success())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup(&self, metainfo: &MetaInfo) -> Result<String> {
|
||||||
|
LoopDevice::with_loop(self.path(), Some(4096), true, |loopdev| {
|
||||||
|
let devname = Self::device_name(metainfo);
|
||||||
|
let srcdev = loopdev.to_string();
|
||||||
|
Self::setup_device(&srcdev, &devname, metainfo)?;
|
||||||
|
Ok(devname)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_partition(partition: &Partition) -> Result<()> {
|
||||||
|
let metainfo = partition.header().metainfo();
|
||||||
|
let srcdev = partition.path().to_str().unwrap();
|
||||||
|
Self::setup_device(srcdev, "rootfs", &metainfo)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setup_image_device<P: AsRef<Path>>(image: P, metainfo: &MetaInfo) -> Result<PathBuf> {
|
pub fn close_device(device_name: &str) -> Result<()> {
|
||||||
let devname = if metainfo.image_type() == "rootfs" {
|
info!("Removing verity device {}", device_name);
|
||||||
|
cmd!(Self::VERITYSETUP, "close {}", device_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_name(metainfo: &MetaInfo) -> String {
|
||||||
|
if metainfo.image_type() == "rootfs" {
|
||||||
String::from("rootfs")
|
String::from("rootfs")
|
||||||
} else if metainfo.image_type() == "realmfs" {
|
} else if metainfo.image_type() == "realmfs" {
|
||||||
let name = metainfo.realmfs_name()
|
let name = metainfo.realmfs_name().unwrap_or("unknown");
|
||||||
.ok_or(format_err!("Cannot set up dm-verity on a realmfs '{}' because it has no name field in metainfo",
|
format!("verity-realmfs-{}-{}", name, metainfo.verity_tag())
|
||||||
image.as_ref().display()))?;
|
|
||||||
format!("verity-realmfs-{}", name)
|
|
||||||
} else {
|
} else {
|
||||||
format!("verity-{}", metainfo.image_type())
|
format!("verity-{}", metainfo.image_type())
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
with_loopdev(image.as_ref(), |loopdev| {
|
pub fn device_name_for_mountpoint(mountpoint: &Mountpoint) -> String {
|
||||||
setup_device(&loopdev, &devname, metainfo.nblocks(), metainfo.verity_root())
|
format!("verity-realmfs-{}-{}", mountpoint.realmfs(), mountpoint.tag())
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn with_loopdev<F,R>(image: &Path, f: F) -> Result<R>
|
fn setup_device(srcdev: &str, devname: &str, metainfo: &MetaInfo) -> Result<()> {
|
||||||
where F: FnOnce(&str) -> Result<R>
|
let nblocks = metainfo.nblocks();
|
||||||
{
|
let verity_root = metainfo.verity_root();
|
||||||
let loopdev = create_image_loop_device(image.as_ref())?;
|
cmd!(Self::VERITYSETUP, "--hash-offset={} --data-blocks={} create {} {} {} {}",
|
||||||
let result = f(&loopdev);
|
nblocks * 4096, nblocks, devname, srcdev, srcdev, verity_root)?;
|
||||||
let result_losetup = util::exec_cmdline(LOSETUP, format!("-d {}", loopdev));
|
|
||||||
let r = result?;
|
|
||||||
result_losetup.context("Error removing loop device created to generate verity tree")?;
|
|
||||||
Ok(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setup_partition_device(partition: &Partition) -> Result<PathBuf> {
|
fn path(&self) -> &Path {
|
||||||
let metainfo = partition.header().metainfo()?;
|
&self.image
|
||||||
let srcdev = partition.path().to_str().unwrap();
|
}
|
||||||
setup_device(srcdev, "rootfs", metainfo.nblocks(), metainfo.verity_root())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_device(srcdev: &str, devname: &str, nblocks: usize, roothash: &str) -> Result<PathBuf> {
|
fn path_str(&self) -> &str {
|
||||||
let args = format!("--hash-offset={} --data-blocks={} create {} {} {} {}",
|
self.image.to_str().unwrap()
|
||||||
nblocks * 4096, nblocks, devname, srcdev, srcdev, roothash);
|
}
|
||||||
util::exec_cmdline(VERITYSETUP, args)
|
|
||||||
.context("Failed to set up verity device")?;
|
|
||||||
|
|
||||||
Ok(PathBuf::from(format!("/dev/mapper/{}", devname)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_image_loop_device(file: &Path) -> Result<String> {
|
|
||||||
let args = format!("--offset 4096 --read-only -f --show {}", file.display());
|
|
||||||
let output = util::exec_cmdline_with_output(LOSETUP, args)?;
|
|
||||||
Ok(output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The output from the `veritysetup format` command can be parsed as key/value
|
/// The output from the `veritysetup format` command can be parsed as key/value
|
||||||
|
Loading…
Reference in New Issue
Block a user