//! Imagem forense de um disco: leitura tolerante a setores ruins, com mapa de //! erros e hash. Serve para trabalhar sempre sobre a cópia. use crate::error::{Error, Result}; use crate::hashing::MultiHasher; use crate::source::Source; use serde::{Deserialize, Serialize}; use std::io::Write; use std::path::Path; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct ImageReport { pub bytes: u64, pub sha256: String, #[serde(default)] pub md5: String, #[serde(default)] pub sha1: String, /// (offset, tamanho) dos trechos ilegíveis, preenchidos com zeros. pub bad_ranges: Vec<(u64, u64)>, pub cancelled: bool, } const BLOCK: usize = 1 << 20; const SECTOR: usize = 512; pub fn create_image( src: &dyn Source, dest: &Path, cancelled: &(dyn Fn() -> bool + Sync), progress: &mut dyn FnMut(u64, u64), ) -> Result { let mut out = std::fs::OpenOptions::new() .write(true) .create_new(true) .open(dest) .map_err(|e| { Error::msg(format!( "não foi possível criar {}: {e} (o arquivo não pode existir)", dest.display() )) })?; crate::owner::give_to_user(dest); let size = src.size(); let mut rep = ImageReport::default(); let mut hasher = MultiHasher::new(); let mut buf = vec![0u8; BLOCK]; let mut pos = 0u64; while pos < size { if cancelled() { rep.cancelled = true; break; } let want = (size - pos).min(BLOCK as u64) as usize; match read_full(src, pos, &mut buf[..want]) { Ok(()) => {} Err(_) => { // Refaz setor a setor para isolar os trechos ruins. let mut o = 0; while o < want { let n = SECTOR.min(want - o); if read_full(src, pos + o as u64, &mut buf[o..o + n]).is_err() { buf[o..o + n].fill(0); match rep.bad_ranges.last_mut() { Some(l) if l.0 + l.1 == pos + o as u64 => l.1 += n as u64, _ => rep.bad_ranges.push((pos + o as u64, n as u64)), } } o += n; } } } out.write_all(&buf[..want])?; hasher.update(&buf[..want]); pos += want as u64; progress(pos, size); } out.flush()?; rep.bytes = pos; let h = hasher.finalize(); rep.sha256 = h.sha256; rep.md5 = h.md5; rep.sha1 = h.sha1; let map = dest.with_extension("map.json"); crate::owner::write_owned(&map, serde_json::to_vec_pretty(&rep)?)?; Ok(rep) } fn read_full(src: &dyn Source, off: u64, buf: &mut [u8]) -> std::io::Result<()> { crate::source::read_exact_at(src, off, buf) }