use std::fs::File;
use std::io;
use std::path::Path;
use onelf_format::EntryKind;
use crate::extract::decompress_entry;
use crate::info::read_footer_and_manifest;
pub fn verify(binary: &Path) -> io::Result<()> {
let (footer, manifest) = read_footer_and_manifest(binary)?;
let mut file = File::open(binary)?;
let dict = crate::info::read_dict(&mut file, &footer)?;
let file_entries: Vec<(usize, String)> = manifest
.entries
.iter()
.enumerate()
.filter(|(_, e)| e.kind == EntryKind::File)
.map(|(i, _)| (i, manifest.entry_path(i)))
.collect();
let total = file_entries.len();
let mut failed: Vec<(String, String)> = Vec::new();
for (idx, rel_path) in &file_entries {
let entry = &manifest.entries[*idx];
let data = decompress_entry(&mut file, &footer, entry, dict.as_deref())?;
let actual: [u8; 32] = blake3::hash(&data).into();
if actual != entry.content_hash {
failed.push((
rel_path.clone(),
format!(
"expected {}, got {}",
hex32(&entry.content_hash),
hex32(&actual)
),
));
}
}
println!("Checked {total} file(s)");
if failed.is_empty() {
println!("OK");
Ok(())
} else {
eprintln!("{} file(s) failed verification:", failed.len());
for (path, msg) in &failed {
eprintln!(" {path}: {msg}");
}
Err(io::Error::other("verification failed"))
}
}
fn hex32(bytes: &[u8; 32]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}