shitbox/hashbox/commands/b2sum/mod.rs

107 lines
3.9 KiB
Rust

use super::Cmd;
use blake2b_simd::Params;
use clap::{value_parser, Arg, Command};
use shitbox::args;
use std::{
fs::File,
io::{self, BufRead, BufReader, Read},
process,
};
#[derive(Debug, Default)]
pub struct B2sum;
impl Cmd for B2sum {
fn cli(&self) -> clap::Command {
Command::new("b2sum")
.about("compute and check MD5 message digest")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([
args::check(),
args::file(),
Arg::new("length")
.help(
"digest length in bits; must not exceed the max for the \
blake2 algorithm and must be a multiple of 8",
)
.short('l')
.long("length")
.default_value("512")
.value_parser(value_parser!(usize)),
])
}
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
if let Some(files) = matches.get_many::<String>("file") {
let mut erred = 0;
let len = matches.get_one("length").unwrap_or(&512);
if *len % 8 != 0 {
let msg = format!("bad hash length: {len}");
return Err(io::Error::new(io::ErrorKind::Other, msg).into());
}
let len = len / 8;
for f in files {
if matches.get_flag("check") {
if f == "-" {
return Err(
io::Error::new(io::ErrorKind::Other, "no file specified").into()
);
}
let fd = File::open(f)?;
let reader = BufReader::new(fd);
for line in reader.lines() {
let line = line?;
let mut split = line.split_whitespace();
let sum = split.next().ok_or::<io::Error>(io::Error::new(
io::ErrorKind::Other,
"invalid checksum file",
))?;
let file = split.next().ok_or::<io::Error>(io::Error::new(
io::ErrorKind::Other,
"invalid checksum file",
))?;
let mut buf = vec![];
let mut fd = File::open(file)?;
fd.read_to_end(&mut buf)?;
let hash = Params::new()
.hash_length(len)
.to_state()
.update(&buf)
.finalize();
if hash.to_hex().as_str() == sum {
println!("{file}: OK");
} else {
println!("{file}: FAILED");
erred += 1;
}
}
} else {
let mut buf = vec![];
if f == "-" {
let _s = io::stdin().read_to_end(&mut buf)?;
} else {
let mut fd = File::open(f)?;
let _s = fd.read_to_end(&mut buf)?;
}
let hash = Params::new()
.hash_length(len)
.to_state()
.update(&buf)
.finalize();
println!("{} {f}", &hash.to_hex());
}
}
if erred > 0 {
println!("b2sum: WARNING: {erred} computed checksum did NOT match");
process::exit(1);
}
}
Ok(())
}
fn path(&self) -> Option<shitbox::Path> {
Some(shitbox::Path::UsrBin)
}
}