Major fixes. Make git history even messier.

This commit is contained in:
Cutieguwu
2025-12-31 15:27:41 -05:00
parent 43454d1c8a
commit ae3b5d8855
6 changed files with 106 additions and 82 deletions

View File

@@ -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()

View File

@@ -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(())
} }

View File

@@ -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,12 +186,15 @@ 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.
/// Usually a file.
pub fn write_to<W: Write>(&mut self, file: &mut W) -> anyhow::Result<usize> {
self.defrag();
let written_bytes = file.write( let written_bytes = file.write(
ron::ser::to_string_pretty( ron::ser::to_string_pretty(
map, self,
ron::ser::PrettyConfig::new() ron::ser::PrettyConfig::new()
.new_line("\n".to_string()) .new_line("\n".to_string())
.struct_names(true), .struct_names(true),
@@ -201,6 +204,7 @@ pub fn write_map_to_file(file: &mut File, map: &MapFile) -> anyhow::Result<usize
Ok(written_bytes) Ok(written_bytes)
} }
}
// This is split out for a shred of readability. // This is split out for a shred of readability.
fn other_engulfs_self_update(new: Cluster, old: &mut Cluster, map: &mut Vec<Cluster>) { fn other_engulfs_self_update(new: Cluster, old: &mut Cluster, map: &mut Vec<Cluster>) {
@@ -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 },

View File

@@ -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;

View File

@@ -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,
} }

View File

@@ -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,53 +87,68 @@ 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<()> {
for untested in self.map.get_clusters(Stage::Untested) {
// Caching. // 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. while read_position < untested.domain.end {
let mut read_position = 0_u64; cluster = Cluster {
let final_read_position = self.stream_len - buf_capacity; 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); println!("Hit error: {:?}", err);
self.input self.input
.seek_relative(buf_capacity as i64) .seek_relative(buf_capacity as i64)
.context("Failed to seek input by buf_capacity to skip previous error")?; .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 { // I don't remember what level was for.
domain: Domain { cluster.stage = Stage::ForIsolation { level: 1 };
start: read_position as usize,
end: (read_position + buf_capacity) as usize,
},
stage: Stage::Intact,
});
} }
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; 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(())