Major fixes. Make git history even messier.
This commit is contained in:
@@ -32,7 +32,7 @@ where
|
||||
}
|
||||
|
||||
Ok(PathBuf::from(format!(
|
||||
"{:?}.{}",
|
||||
"{}.{}",
|
||||
source_name
|
||||
.as_ref()
|
||||
.to_str()
|
||||
|
||||
@@ -41,12 +41,13 @@ fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
let map: MapFile = {
|
||||
let path = crate::io::get_path(&config.output, &config.input, "map")
|
||||
let path = crate::io::get_path(&config.map, &config.input, "map")
|
||||
.context("Failed to generate map path.")?;
|
||||
|
||||
MapFile::try_from(
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)
|
||||
.with_context(|| {
|
||||
@@ -58,7 +59,7 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
let mut recover_tool = Recover::new(&config, &mut input, output, map)?;
|
||||
|
||||
recover_tool.run();
|
||||
recover_tool.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ impl MapFile {
|
||||
for cluster in self.map.iter() {
|
||||
match cluster.stage {
|
||||
Stage::Untested => return Stage::Untested,
|
||||
Stage::ForIsolation(_) => {
|
||||
Stage::ForIsolation { .. } => {
|
||||
if recover_stage == Stage::Damaged || cluster.stage < recover_stage {
|
||||
// Note that recover_stage after first condition is
|
||||
// only ever Stage::ForIsolation(_), thus PartialEq,
|
||||
@@ -186,12 +186,15 @@ impl MapFile {
|
||||
});
|
||||
Some(delta)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_map_to_file(file: &mut File, map: &MapFile) -> anyhow::Result<usize> {
|
||||
/// Writes the map to the provided item implementing `Write` trait.
|
||||
/// Usually a file.
|
||||
pub fn write_to<W: Write>(&mut self, file: &mut W) -> anyhow::Result<usize> {
|
||||
self.defrag();
|
||||
|
||||
let written_bytes = file.write(
|
||||
ron::ser::to_string_pretty(
|
||||
map,
|
||||
self,
|
||||
ron::ser::PrettyConfig::new()
|
||||
.new_line("\n".to_string())
|
||||
.struct_names(true),
|
||||
@@ -200,6 +203,7 @@ pub fn write_map_to_file(file: &mut File, map: &MapFile) -> anyhow::Result<usize
|
||||
)?;
|
||||
|
||||
Ok(written_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// This is split out for a shred of readability.
|
||||
@@ -730,8 +734,8 @@ mod tests {
|
||||
|
||||
let stages = vec![
|
||||
Stage::Damaged,
|
||||
Stage::ForIsolation(1),
|
||||
Stage::ForIsolation(0),
|
||||
Stage::ForIsolation { level: 1 },
|
||||
Stage::ForIsolation { level: 0 },
|
||||
Stage::Untested,
|
||||
];
|
||||
|
||||
@@ -758,19 +762,19 @@ mod tests {
|
||||
|
||||
mf.map = vec![
|
||||
*Cluster::default().set_stage(Stage::Damaged),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation(0)),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation(1)),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation { level: 0 }),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation { level: 1 }),
|
||||
Cluster::default(),
|
||||
Cluster::default(),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation(1)),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation(0)),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation { level: 1 }),
|
||||
*Cluster::default().set_stage(Stage::ForIsolation { level: 0 }),
|
||||
*Cluster::default().set_stage(Stage::Damaged),
|
||||
];
|
||||
|
||||
let stages = vec![
|
||||
Stage::Damaged,
|
||||
Stage::ForIsolation(1),
|
||||
Stage::ForIsolation(0),
|
||||
Stage::ForIsolation { level: 1 },
|
||||
Stage::ForIsolation { level: 0 },
|
||||
Stage::Untested,
|
||||
];
|
||||
|
||||
@@ -811,19 +815,19 @@ mod tests {
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 3, end: 4 },
|
||||
stage: Stage::ForIsolation(0),
|
||||
stage: Stage::ForIsolation { level: 0 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 4, end: 5 },
|
||||
stage: Stage::ForIsolation(0),
|
||||
stage: Stage::ForIsolation { level: 0 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 5, end: 6 },
|
||||
stage: Stage::ForIsolation(1),
|
||||
stage: Stage::ForIsolation { level: 1 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 6, end: 7 },
|
||||
stage: Stage::ForIsolation(0),
|
||||
stage: Stage::ForIsolation { level: 0 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 7, end: 8 },
|
||||
@@ -839,15 +843,15 @@ mod tests {
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 3, end: 5 },
|
||||
stage: Stage::ForIsolation(0),
|
||||
stage: Stage::ForIsolation { level: 0 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 5, end: 6 },
|
||||
stage: Stage::ForIsolation(1),
|
||||
stage: Stage::ForIsolation { level: 1 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 6, end: 7 },
|
||||
stage: Stage::ForIsolation(0),
|
||||
stage: Stage::ForIsolation { level: 0 },
|
||||
},
|
||||
Cluster {
|
||||
domain: Domain { start: 7, end: 8 },
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
pub use super::cluster::Cluster;
|
||||
pub use super::domain::{Domain, DomainOverlap};
|
||||
pub use super::map::{write_map_to_file, MapFile};
|
||||
pub use super::map::MapFile;
|
||||
pub use super::stage::Stage;
|
||||
|
||||
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Stage {
|
||||
Untested,
|
||||
ForIsolation(u8),
|
||||
ForIsolation { level: u8 },
|
||||
Damaged,
|
||||
Intact,
|
||||
}
|
||||
|
||||
103
src/recovery.rs
103
src/recovery.rs
@@ -1,5 +1,6 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{BufWriter, Read, Seek, Write};
|
||||
use std::usize;
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
@@ -12,8 +13,6 @@ pub struct Recover<'a> {
|
||||
config: &'a Args,
|
||||
stream_len: u64,
|
||||
|
||||
buf: Vec<u8>,
|
||||
|
||||
input: &'a mut File,
|
||||
output: BufWriter<File>,
|
||||
map: MapFile,
|
||||
@@ -32,7 +31,6 @@ impl<'a> Recover<'a> {
|
||||
let mut r = Recover {
|
||||
config,
|
||||
stream_len,
|
||||
buf: vec![],
|
||||
input,
|
||||
output: BufWriter::with_capacity(stream_len as usize, output),
|
||||
map,
|
||||
@@ -44,35 +42,41 @@ impl<'a> Recover<'a> {
|
||||
}
|
||||
|
||||
/// Recover media.
|
||||
pub fn run(&mut self) -> () {
|
||||
pub fn run(&mut self) -> anyhow::Result<usize> {
|
||||
// From start, read to end or error.
|
||||
//
|
||||
// If all data recovered, return early.
|
||||
// Else, read from end to error.
|
||||
|
||||
self.copy_untested();
|
||||
|
||||
/*
|
||||
let mut is_finished = false;
|
||||
|
||||
while !is_finished {
|
||||
self.map.defrag();
|
||||
|
||||
match self.map.get_stage() {
|
||||
Stage::Untested => {
|
||||
self.copy_untested();
|
||||
}
|
||||
Stage::ForIsolation(level) => {
|
||||
self.copy_isolate(level);
|
||||
}
|
||||
Stage::Damaged => {
|
||||
Stage::Untested => self.copy_untested()?,
|
||||
Stage::ForIsolation { .. } => todo!(),
|
||||
Stage::Damaged | Stage::Intact => {
|
||||
println!("Cannot recover further.");
|
||||
|
||||
is_finished = true
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
// return recovered_bytes
|
||||
// Need to reset seek position between algorithms.
|
||||
self.input
|
||||
.rewind()
|
||||
.context("Failed to reset input seek position.")?;
|
||||
self.output
|
||||
.rewind()
|
||||
.context("Failed to reset output seek position")?;
|
||||
|
||||
dbg!();
|
||||
}
|
||||
|
||||
// Temporary.
|
||||
let recovered_bytes = usize::MIN;
|
||||
Ok(recovered_bytes)
|
||||
}
|
||||
|
||||
/// Restore current progress based on MapFile.
|
||||
@@ -83,53 +87,68 @@ impl<'a> Recover<'a> {
|
||||
|
||||
/// Attempt to copy all untested blocks.
|
||||
fn copy_untested(&mut self) -> anyhow::Result<()> {
|
||||
for untested in self.map.get_clusters(Stage::Untested) {
|
||||
// Caching.
|
||||
let buf_capacity = self.get_buf_capacity();
|
||||
let mut read_position: usize;
|
||||
let mut cluster: Cluster;
|
||||
let mut buf_capacity = self.get_buf_capacity() as usize;
|
||||
let mut buf = vec![crate::FB_PAD_VALUE; buf_capacity];
|
||||
|
||||
self.buf = vec![crate::FB_PAD_VALUE; buf_capacity as usize];
|
||||
read_position = untested.domain.start;
|
||||
|
||||
// Yet more caching.
|
||||
let mut read_position = 0_u64;
|
||||
let final_read_position = self.stream_len - buf_capacity;
|
||||
while read_position < untested.domain.end {
|
||||
cluster = Cluster {
|
||||
domain: Domain {
|
||||
start: read_position,
|
||||
end: read_position + buf_capacity,
|
||||
},
|
||||
stage: Stage::Intact,
|
||||
};
|
||||
|
||||
buf_capacity = buf_capacity.min(untested.domain.end - read_position);
|
||||
|
||||
if let Err(err) = self.input.read_exact(&mut buf) {
|
||||
// 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
|
||||
// values are potentially invalid.
|
||||
//
|
||||
// That padding should have a cli arg to control it.
|
||||
|
||||
while read_position < final_read_position {
|
||||
if let Err(err) = self.input.read_exact(&mut self.buf) {
|
||||
println!("Hit error: {:?}", err);
|
||||
self.input
|
||||
.seek_relative(buf_capacity as i64)
|
||||
.context("Failed to seek input by buf_capacity to skip previous error")?;
|
||||
} else {
|
||||
self.output
|
||||
.write_all(self.buf.as_slice())
|
||||
.context("Failed to write data to output file")?;
|
||||
|
||||
self.map.update(Cluster {
|
||||
domain: Domain {
|
||||
start: read_position as usize,
|
||||
end: (read_position + buf_capacity) as usize,
|
||||
},
|
||||
stage: Stage::Intact,
|
||||
});
|
||||
// I don't remember what level was for.
|
||||
cluster.stage = Stage::ForIsolation { level: 1 };
|
||||
}
|
||||
|
||||
if cluster.stage == Stage::Intact {
|
||||
self.output
|
||||
.write_all(&buf[0..buf_capacity])
|
||||
.context("Failed to write data to output file")?;
|
||||
}
|
||||
|
||||
self.map.update(cluster);
|
||||
read_position += buf_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
write_map_to_file(
|
||||
{
|
||||
self.map
|
||||
.write_to({
|
||||
let map_path = crate::io::get_path(&self.config.map, &self.config.input, "map")
|
||||
.context("Failed to generate map path.")?;
|
||||
|
||||
&mut OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true) // Wipe old map. Should really make a backup first.
|
||||
.open(&map_path)
|
||||
.with_context(|| {
|
||||
format!("Failed to open map file at: {}", map_path.display())
|
||||
})?
|
||||
},
|
||||
&self.map,
|
||||
)
|
||||
})
|
||||
.context("Failed to write map file")?;
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user