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