diff --git a/src/io.rs b/src/io.rs index 1521815..793de77 100644 --- a/src/io.rs +++ b/src/io.rs @@ -20,39 +20,12 @@ pub fn get_stream_length(stream: &mut S) -> io::Result { len } -/* - * IO Error Poisoning: - * - * - * Attempt 1: - * - * Wrap C calls w/ `ffi`. Most importantly, execute the `close()` through C. - * - * - * Attempt 2: - * - * Return to using safe-on-the-surface Rust, but attempt to execute the reads - * via a separate thread, hoping that the observed poisoning goes away with the - * thread being closed and doesn't affect the main thread. - * - * May need `mpsc` to transfer data without spawning a thread on every read. - */ - pub fn load_input() -> anyhow::Result { OpenOptions::new() .read(true) .custom_flags(libc::O_DIRECT) .open(&CONFIG.input) .with_context(|| format!("Failed to open input file: {}", &CONFIG.input.display())) - - /* - use std::ffi::CString; - use std::os::fd::FromRawFd; - - let path = CString::new(CONFIG.input.to_str().unwrap().to_owned()).unwrap(); - let flags = libc::O_DIRECT | libc::O_RDONLY; - let f = unsafe { File::from_raw_fd(libc::open(path.as_ptr(), flags)) }; - */ } pub fn load_output() -> anyhow::Result { @@ -88,3 +61,18 @@ pub fn load_map_write() -> anyhow::Result { ) }) } + +#[repr(C, align(512))] +pub struct DirectIOBuffer(pub [u8; crate::MAX_BUFFER_SIZE]); + +impl Default for DirectIOBuffer { + fn default() -> Self { + Self([crate::FB_NULL_VALUE; _]) + } +} + +impl From<[u8; crate::MAX_BUFFER_SIZE]> for DirectIOBuffer { + fn from(value: [u8; crate::MAX_BUFFER_SIZE]) -> Self { + Self(value) + } +} diff --git a/src/main.rs b/src/main.rs index d444ec6..0718e77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,8 @@ use anyhow; const FB_SECTOR_SIZE: usize = 2048; const FB_NULL_VALUE: u8 = 0; +const MAX_BUFFER_SIZE: usize = FB_SECTOR_SIZE * 16; + fn main() -> anyhow::Result<()> { let mut recover_tool = Recover::new()?; recover_tool.run()?; diff --git a/src/mapping/cluster.rs b/src/mapping/cluster.rs index 1e66537..47b38c7 100644 --- a/src/mapping/cluster.rs +++ b/src/mapping/cluster.rs @@ -21,6 +21,7 @@ impl Default for Cluster { impl Cluster { /// Breaks apart into a vec of clusters, /// each of cluster_size, excepting last. + #[allow(dead_code)] pub fn subdivide(&mut self, cluster_len: usize) -> Vec { let domain_len = self.domain.len(); let mut start = self.domain.start; @@ -49,6 +50,8 @@ impl Cluster { clusters } + // This is used in unit tests at present. Ideally it probably shouldn't exist. + #[allow(dead_code)] pub fn set_stage(&mut self, stage: Stage) -> &mut Self { self.stage = stage; self diff --git a/src/mapping/domain.rs b/src/mapping/domain.rs index b6b1b71..e5d296c 100644 --- a/src/mapping/domain.rs +++ b/src/mapping/domain.rs @@ -16,6 +16,7 @@ impl Default for Domain { impl Domain { /// Return length of domain in sectors. + #[allow(dead_code)] pub fn len(self) -> usize { self.end - self.start } diff --git a/src/recovery.rs b/src/recovery.rs index 65f2a6b..e1e07cd 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -2,13 +2,13 @@ use std::fs::File; use std::io::{BufWriter, Read, Seek, SeekFrom, Write}; use std::usize; -use anyhow::{Context, anyhow}; +use anyhow::Context; use crate::cli::CONFIG; +use crate::io::DirectIOBuffer; use crate::mapping::prelude::*; #[derive(Debug)] -#[allow(dead_code)] pub struct Recover { input: File, output: BufWriter, @@ -90,11 +90,12 @@ impl Recover { /// Attempt to copy all untested blocks. fn copy_untested(&mut self) -> anyhow::Result<()> { + let mut buf = DirectIOBuffer::default(); + for untested in self.map.get_clusters(Stage::Untested) { // Caching. let mut read_position: usize; let mut cluster: Cluster; - let mut buf: Vec; let mut buf_capacity = self.get_buf_capacity() as usize; dbg!(untested.domain); @@ -104,7 +105,6 @@ impl Recover { dbg!(read_position); buf_capacity = buf_capacity.min(untested.domain.end - read_position); - buf = vec![crate::FB_NULL_VALUE; buf_capacity]; cluster = Cluster { domain: Domain { @@ -114,7 +114,7 @@ impl Recover { stage: Stage::Intact, }; - if let Err(err) = self.input.read_exact(&mut buf) { + if let Err(err) = self.input.read_exact(&mut buf.0) { // If buf were zeroed out before every read, one could theoretically recover // part of that read given the assumption that all null values from the end to // the first non-null value are unread, and some further padding from the last @@ -141,7 +141,7 @@ impl Recover { if cluster.stage == Stage::Intact { self.output - .write_all(&buf[0..buf_capacity]) + .write_all(&buf.0[0..buf_capacity]) .context("Failed to write data to output file")?; } @@ -151,6 +151,8 @@ impl Recover { } } + drop(buf); + self.map.write_to(&mut crate::io::load_map_write()?)?; Ok(()) @@ -159,7 +161,7 @@ impl Recover { /// Set buffer capacity as cluster length in bytes. /// Varies depending on the recovery stage. fn get_buf_capacity(&mut self) -> u64 { - CONFIG.sector_size as u64 * CONFIG.cluster_length as u64 + crate::MAX_BUFFER_SIZE.min(CONFIG.sector_size * CONFIG.cluster_length) as u64 } /// Reloads the input and restores the seek position.