Compare commits
2 Commits
read-poiso
...
53d773e2ea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53d773e2ea | ||
|
|
1607f7ebfa |
36
src/io.rs
36
src/io.rs
@@ -20,33 +20,12 @@ pub fn get_stream_length<S: Seek>(stream: &mut S) -> io::Result<u64> {
|
||||
len
|
||||
}
|
||||
|
||||
/*
|
||||
* IO Error Poisoning:
|
||||
*
|
||||
* 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<File> {
|
||||
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<File> {
|
||||
@@ -82,3 +61,18 @@ pub fn load_map_write() -> anyhow::Result<File> {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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<Cluster> {
|
||||
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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<File>,
|
||||
@@ -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<u8>;
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user