Compare commits
2 Commits
read-poiso
...
53d773e2ea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53d773e2ea | ||
|
|
1607f7ebfa |
38
src/io.rs
38
src/io.rs
@@ -1,7 +1,6 @@
|
||||
use std::ffi::CString;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{self, Seek, SeekFrom};
|
||||
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd};
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
use crate::cli::CONFIG;
|
||||
|
||||
@@ -21,22 +20,12 @@ pub fn get_stream_length<S: Seek>(stream: &mut S) -> io::Result<u64> {
|
||||
len
|
||||
}
|
||||
|
||||
/*
|
||||
* IO Error Poisoning:
|
||||
*
|
||||
*
|
||||
* Attempt 1:
|
||||
*
|
||||
* Wrap C calls w/ `ffi`. Most importantly, execute the `close()` through C.
|
||||
*
|
||||
* I cannot seem to execute `close()`... one hopes that reassigning will drop
|
||||
* calling `close()` on the old fd, but I don't know.
|
||||
*/
|
||||
|
||||
pub fn load_input() -> anyhow::Result<File> {
|
||||
let path = CString::new(CONFIG.input.to_str().unwrap().to_owned())?;
|
||||
let flags = libc::O_RDONLY | libc::O_DIRECT;
|
||||
Ok(unsafe { File::from_raw_fd(libc::open(path.as_ptr(), flags)) })
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_DIRECT)
|
||||
.open(&CONFIG.input)
|
||||
.with_context(|| format!("Failed to open input file: {}", &CONFIG.input.display()))
|
||||
}
|
||||
|
||||
pub fn load_output() -> anyhow::Result<File> {
|
||||
@@ -72,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