use anyhow::{Context, Result};
use blazehash::output::make_writer;
use blazehash::walk::walk_paths;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use super::report_walk_errors;
const BLAKE3_BYTES_PER_SEC: u64 = 3 * 1024 * 1024 * 1024;
pub fn run(
paths: &[PathBuf],
recursive: bool,
output: Option<&PathBuf>,
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))] mft: bool,
) -> Result<()> {
let mut writer = make_writer(output.map(|p| p.as_path()), false)?;
let mut total_bytes = 0u64;
let mut total_files = 0usize;
for path in paths {
#[cfg(target_os = "windows")]
if mft && path.is_dir() {
let entries = run_mft_for_path(path, recursive)?;
for e in &entries {
writeln!(writer, "{}\t{}", e.size, e.path.display())?;
total_bytes += e.size;
total_files += 1;
}
continue;
}
if path.is_file() {
let meta = fs::metadata(path)
.with_context(|| format!("failed to read metadata for {}", path.display()))?;
let size = meta.len();
writeln!(writer, "{}\t{}", size, path.display())?;
total_bytes += size;
total_files += 1;
} else if path.is_dir() {
let (file_paths, errors) = walk_paths(path, recursive);
report_walk_errors(&errors);
for file_path in &file_paths {
let meta = fs::metadata(file_path).with_context(|| {
format!("failed to read metadata for {}", file_path.display())
})?;
let size = meta.len();
writeln!(writer, "{}\t{}", size, file_path.display())?;
total_bytes += size;
total_files += 1;
}
}
}
writer.flush()?;
#[cfg(target_os = "windows")]
fn run_mft_for_path(
root: &std::path::Path,
recursive: bool,
) -> Result<Vec<blazehash::walk_windows_mft::MftEntry>> {
use blazehash::walk_windows_mft::{
enumerate_mft_sizes, is_elevated, read_mft_results, spawn_elevated_mft_worker,
};
if is_elevated() {
return enumerate_mft_sizes(root, recursive);
}
eprintln!("[*] --mft: not running as Administrator.");
eprintln!("[*] A UAC elevation prompt will appear. Approve it to use MFT direct access.");
let tmp = tempfile::NamedTempFile::new()
.context("failed to create temp file for MFT worker output")?;
let tmp_path = tmp.path().to_path_buf();
let _tmp = tmp;
spawn_elevated_mft_worker(root, recursive, &tmp_path)?;
read_mft_results(&tmp_path)
}
let file_word = if total_files == 1 { "file" } else { "files" };
let mib = total_bytes as f64 / (1024.0 * 1024.0);
let est_secs = total_bytes as f64 / BLAKE3_BYTES_PER_SEC as f64;
let (size_str, unit) = if mib >= 1024.0 {
(mib / 1024.0, "GiB")
} else {
(mib, "MiB")
};
eprintln!(
"[+] {total_files} {file_word} — {size_str:.1} {unit} (est. {est_secs:.1}s to hash @ BLAKE3 ~3 GiB/s)"
);
Ok(())
}