From f5bf92f488fd92bbb6a947840f127a14cbe05e10 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Tue, 29 Jan 2019 11:37:12 -0500 Subject: [PATCH] function for piping section of a file to a command This makes it possible to calculate sha256sum in place on an image file which has both a header and an appended dm-verity tree. Before this required a message process of extracting the body into a temporary file. --- libcitadel/src/util.rs | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/libcitadel/src/util.rs b/libcitadel/src/util.rs index 6f2c446..9518d1e 100644 --- a/libcitadel/src/util.rs +++ b/libcitadel/src/util.rs @@ -5,6 +5,8 @@ use libc::{self, c_char}; use std::ffi::CStr; use std::str::from_utf8_unchecked; use std::env; +use std::fs::File; +use std::io::{self,Seek,Read,BufReader,SeekFrom}; use failure::ResultExt; @@ -86,6 +88,53 @@ pub fn sha256>(path: P) -> Result { Ok(v[0].trim().to_owned()) } +pub enum FileRange { + All, + Offset(usize), + Range{offset: usize, len: usize}, +} + +fn ranged_reader>(path: P, range: FileRange) -> Result> { + let mut f = File::open(path.as_ref())?; + let offset = match range { + FileRange::All => 0, + FileRange::Offset(n) => n, + FileRange::Range {offset, len: _} => offset, + }; + if offset > 0 { + f.seek(SeekFrom::Start(offset as u64))?; + } + let r = BufReader::new(f); + if let FileRange::Range {offset: _, len} = range { + Ok(Box::new(r.take(len as u64))) + } else { + Ok(Box::new(r)) + } +} + +/// +/// Execute a command, pipe the contents of a file to stdin, return the output as a `String` +/// +pub fn exec_cmdline_pipe_input(cmd_path: &str, args: S, input: P, range: FileRange) -> Result + where S: AsRef, P: AsRef +{ + let mut r = ranged_reader(input.as_ref(), range)?; + ensure_command_exists(cmd_path)?; + let args: Vec<&str> = args.as_ref().split_whitespace().collect::>(); + let mut child = Command::new(cmd_path) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .context(format!("unable to execute {}", cmd_path))?; + + let stdin = child.stdin.as_mut().unwrap(); + io::copy(&mut r, stdin)?; + let output = child.wait_with_output()?; + Ok(String::from_utf8(output.stdout).unwrap().trim().to_owned()) +} + pub fn xz_compress>(path: P) -> Result<()> { exec_cmdline("/usr/bin/xz", format!("-T0 {}", path.as_ref().display())) .context(format!("failed to compress {}", path.as_ref().display()))?;