clean up code, implement all secure sha algorithms

This commit is contained in:
Bryson Steck 2025-07-18 00:08:01 -06:00
parent f06da146a7
commit 35dd6d97b0
Signed by: bryson
SSH key fingerprint: SHA256:XpKABw/nP4z8UVaH+weLaBnEOD86+cVwif+QjuYLGT4
3 changed files with 148 additions and 46 deletions

4
.rustfmt.toml Normal file
View file

@ -0,0 +1,4 @@
edition = "2021"
tab_spaces = 2
match_block_trailing_comma = true
#wrap_comments = true

24
src/hashers.rs Normal file
View file

@ -0,0 +1,24 @@
use sha2::{Digest, Sha256, Sha384, Sha512};
use std::fs::File;
use std::io;
pub fn hash_sha256(mut file: File) -> String {
let mut hasher = Sha256::new();
_ = io::copy(&mut file, &mut hasher);
return format!("{:x}", hasher.finalize());
}
pub fn hash_sha384(mut file: File) -> String {
let mut hasher = Sha384::new();
_ = io::copy(&mut file, &mut hasher);
return format!("{:x}", hasher.finalize());
}
pub fn hash_sha512(mut file: File) -> String {
let mut hasher = Sha512::new();
_ = io::copy(&mut file, &mut hasher);
return format!("{:x}", hasher.finalize());
}

View file

@ -1,44 +1,114 @@
use sha2::{Sha256, Digest};
use std::ops::Deref;
use std::path::PathBuf;
use std::thread;
use std::thread::available_parallelism;
use std::fs::File;
use std::io;
use clap::Parser;
use std::sync::Arc;
use std::sync::Mutex;
use std::collections::VecDeque;
use std::fs;
use std::fs::{self, File};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::thread::{self, available_parallelism};
mod hashers;
#[derive(Parser)]
#[command(name = "psha")]
#[command(version = option_env!("CARGO_PKG_VERSION"))]
#[command(about = "A parallel checksum tool for various algorithms")]
#[command(long_about = None)]
struct Args {
#[arg(
trailing_var_arg = true,
short,
long,
help = "Use at most this number of threads, 0 means as many as there are processor cores",
default_value = "0"
)]
threads: usize,
#[arg(
short,
long,
help = "Enable debug output (thread info, algorithm used/detected)"
)]
debug: bool,
#[arg(
short,
long,
help = "Specify an algorithm for hashing",
default_value = "sha256",
value_parser = clap::builder::PossibleValuesParser::new(["sha256", "sha512"])
)]
algorithm: String,
#[arg(
short = 'f',
long,
help = "Show canonicalized (relative paths converted to absolute) file paths"
)]
canonicalize: bool,
#[arg(
short = 'c',
long,
help = "Read checksums from the file(s) and verify them"
)]
check: Vec<PathBuf>,
#[arg(
short = 'q',
long,
help = "(only used with -c) Only print checksums that fail; do not print OK for files that are successful"
)]
failures_only: bool,
#[arg(
short = 'Q',
long,
help = "(only used with -c) Suppress all output to stdout, including failures"
)]
quiet: bool,
#[arg(trailing_var_arg = true)]
files: Vec<PathBuf>,
}
fn checksum(thread_id: usize, filenames: Arc<Mutex<VecDeque<PathBuf>>>) -> Result<(), String> {
let running = true;
while running {
let filename = match filenames.lock().unwrap().pop_front() {
Some(f) => f,
None => return Ok(())
};
let mut hasher = Sha256::new();
let mut file = File::open(&filename).unwrap();
struct ThreadInfo {
debug: Arc<bool>,
failures_only: Arc<bool>,
quiet: Arc<bool>,
thread_id: usize,
filenames: Arc<Mutex<VecDeque<PathBuf>>>,
algorithm: Arc<String>,
}
io::copy(&mut file, &mut hasher);
let hash = hasher.finalize();
// println!("thread {} result: {:x}\t{}", thread_id, hash, filename.as_path().display());
println!("{:x}\t{}", hash, filename.as_path().display());
fn hash(info: ThreadInfo) -> Result<(), String> {
loop {
let filename = match info.filenames.lock().unwrap().pop_front() {
Some(f) => f,
None => break,
};
if !*info.quiet && *info.debug {
eprintln!(
"thread {} is hashing file '{}'",
info.thread_id,
filename.as_path().display()
);
}
let file = File::open(&filename).unwrap();
let res = match &*info.algorithm.as_str() {
"sha256" => hashers::hash_sha256(file),
"sha384" => hashers::hash_sha384(file),
"sha512" => hashers::hash_sha512(file),
_ => panic!("Somehow did not pass a supported algorithm"),
};
if !*info.quiet {
println!("{} {}", res, filename.as_path().display());
}
}
println!("thread {} has ran out of work", thread_id);
if !*info.quiet && *info.debug {
eprintln!("thread {} has ran out of work", info.thread_id);
}
Ok(())
}
@ -49,33 +119,37 @@ fn main() {
let mut buffer = VecDeque::new();
let mut handles = vec![];
for file in args.files {
// match fs::canonicalize(file.as_path()) {
// Ok(p) => buffer.push_back(p),
// Err(e) => panic!("unable to canonicalize {}: {}", file.as_path().display(), e)
// };
buffer.push_back(file);
if args.canonicalize {
match fs::canonicalize(file.as_path()) {
Ok(p) => buffer.push_back(p),
Err(e) => panic!("unable to canonicalize {}: {}", file.as_path().display(), e),
};
} else {
buffer.push_back(file);
}
}
let arc_buf = Arc::new(Mutex::new(buffer));
// let mut handles = vec![];
// let chunks = Arc::new(Mutex::new(args.files.chunks(args.files.len()/ cpus).clone()));
/* let threads = chunks.into_iter().map(|chunk| {
thread::spawn(move || checksum(0, chunk.to_vec()))
}).collect::<Vec<_>>(); */
/* threads.into_iter().for_each(|i| { i.join().unwrap(); });
// for file in args.files {
// if let Err(e) = checksum(0, file) { */
// println!("{}", e);
// break;
// };
// }
let arc_buf = Arc::new(Mutex::new(buffer));
for i in 0..cpus {
let safe = Arc::clone(&arc_buf);
handles.push(thread::spawn(move || checksum(i, safe)))
let safe_buf = Arc::clone(&arc_buf);
let safe_alg = Arc::new(args.algorithm.clone());
handles.push(thread::spawn(move || {
hash(ThreadInfo {
debug: Arc::new(args.debug),
failures_only: Arc::new(args.failures_only),
quiet: Arc::new(args.quiet),
thread_id: i,
filenames: safe_buf,
algorithm: safe_alg,
})
}))
}
for handle in handles {
handle.join().unwrap();
match handle.join().unwrap() {
Err(e) => panic!("{}", e),
Ok(_) => (),
}
}
// println!("{}", available_parallelism().unwrap().get());