diff --git a/crates/rustic_core/src/commands/restore.rs b/crates/rustic_core/src/commands/restore.rs
index a8fa1c7..6d6dd29 100644
--- a/crates/rustic_core/src/commands/restore.rs
+++ b/crates/rustic_core/src/commands/restore.rs
@@ -5,7 +5,6 @@ use log::{debug, error, info, trace, warn};
use std::{
cmp::Ordering,
collections::BTreeMap,
- io::Read,
num::NonZeroU32,
path::{Path, PathBuf},
sync::Mutex,
@@ -18,10 +17,9 @@ use rayon::ThreadPoolBuilder;
use crate::{
error::CommandErrorKind,
- hash,
repository::{IndexedFull, IndexedTree},
- DecryptReadBackend, FileType, Id, IndexedBackend, LocalDestination, Node, NodeType, Open,
- Progress, ProgressBars, ReadBackend, Repository, RusticResult,
+ BlobType, DecryptReadBackend, FileType, Id, LocalDestination, Node, NodeType, Open, Progress,
+ ProgressBars, ReadBackend, Repository, RusticResult,
};
pub(crate) mod constants {
@@ -167,7 +165,7 @@ impl RestoreOpts {
match (
exists,
restore_infos
- .add_file(dest, node, path.clone(), repo.index(), self.verify_existing)
+ .add_file(dest, node, path.clone(), repo, self.verify_existing)
.map_err(|err| {
CommandErrorKind::ErrorCollecting(path.to_path_buf(), Box::new(err))
})?,
@@ -502,12 +500,12 @@ enum AddFileResult {
impl RestoreInfos {
/// Add the file to [`FileInfos`] using `index` to get blob information.
- fn add_file(
+ fn add_file
(
&mut self,
dest: &LocalDestination,
file: &Node,
name: PathBuf,
- index: &impl IndexedBackend,
+ repo: &Repository
,
ignore_mtime: bool,
) -> RusticResult {
let mut open_file = dest.get_matching_file(&name, file.meta.size);
@@ -543,9 +541,7 @@ impl RestoreInfos {
let mut file_pos = 0;
let mut has_unmatched = false;
for id in file.content.iter().flatten() {
- let ie = index
- .get_data(id)
- .ok_or_else(|| CommandErrorKind::IdNotFound(*id))?;
+ let ie = repo.get_index_entry(BlobType::Data, id)?;
let bl = BlobLocation {
offset: ie.offset,
length: ie.length,
@@ -553,11 +549,9 @@ impl RestoreInfos {
};
let length = bl.data_length();
- let matches = open_file.as_mut().map_or(false, |file| {
- // Existing file content; check if SHA256 matches
- let mut vec = vec![0; length as usize];
- file.read_exact(&mut vec).is_ok() && id == &hash(&vec)
- });
+ let matches = open_file
+ .as_mut()
+ .map_or(false, |file| id.blob_matches_reader(length as usize, file));
let blob_location = self.r.entry((ie.pack, bl)).or_insert_with(Vec::new);
blob_location.push(FileLocation {
diff --git a/crates/rustic_core/src/error.rs b/crates/rustic_core/src/error.rs
index 0053bc1..3e6926d 100644
--- a/crates/rustic_core/src/error.rs
+++ b/crates/rustic_core/src/error.rs
@@ -185,8 +185,6 @@ pub enum CommandErrorKind {
ErrorCollecting(PathBuf, Box),
/// error setting length for {0:?}: {1:?}
ErrorSettingLength(PathBuf, Box),
- /// did not find id {0} in index
- IdNotFound(Id),
/// {0:?}
FromRayonError(#[from] rayon::ThreadPoolBuildError),
/// conversion to `u64` failed: `{0:?}`
@@ -274,6 +272,8 @@ pub enum RepositoryErrorKind {
ReadingPasswordFromPromptFailed(std::io::Error),
/// Config file already exists. Aborting.
ConfigFileExists,
+ /// did not find id {0} in index
+ IdNotFound(Id),
}
/// [`IndexErrorKind`] describes the errors that can be returned by processing Indizes
diff --git a/crates/rustic_core/src/id.rs b/crates/rustic_core/src/id.rs
index 4aa6039..ac1f05c 100644
--- a/crates/rustic_core/src/id.rs
+++ b/crates/rustic_core/src/id.rs
@@ -1,11 +1,11 @@
-use std::{fmt, ops::Deref, path::Path};
+use std::{fmt, io::Read, ops::Deref, path::Path};
use binrw::{BinRead, BinWrite};
use derive_more::{Constructor, Display};
use rand::{thread_rng, RngCore};
use serde::{Deserialize, Serialize};
-use crate::{error::IdErrorKind, RusticResult};
+use crate::{error::IdErrorKind, hash, RusticResult};
pub(super) mod constants {
pub(super) const LEN: usize = 32;
@@ -63,6 +63,12 @@ impl Id {
pub fn is_null(&self) -> bool {
self == &Self::default()
}
+
+ pub fn blob_matches_reader(&self, length: usize, r: &mut impl Read) -> bool {
+ // check if SHA256 matches
+ let mut vec = vec![0; length];
+ r.read_exact(&mut vec).is_ok() && self == &hash(&vec)
+ }
}
impl fmt::Debug for Id {
diff --git a/crates/rustic_core/src/lib.rs b/crates/rustic_core/src/lib.rs
index fc1828f..761dc90 100644
--- a/crates/rustic_core/src/lib.rs
+++ b/crates/rustic_core/src/lib.rs
@@ -142,5 +142,5 @@ pub use crate::{
SnapshotOptions, StringList,
},
},
- repository::{Open, OpenStatus, Repository, RepositoryOptions},
+ repository::{IndexedFull, Open, OpenStatus, Repository, RepositoryOptions},
};
diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs
index 770b138..5545e82 100644
--- a/crates/rustic_core/src/repository.rs
+++ b/crates/rustic_core/src/repository.rs
@@ -46,13 +46,14 @@ use crate::{
},
crypto::aespoly1305::Key,
error::{KeyFileErrorKind, RepositoryErrorKind, RusticErrorKind},
+ index::IndexEntry,
repofile::{
configfile::ConfigFile, keyfile::find_key_in_backend, snapshotfile::SnapshotSummary,
RepoFile,
},
BlobType, Id, IndexBackend, IndexedBackend, LocalDestination, NoProgressBars, Node,
- NodeStreamer, PathList, ProgressBars, PruneOpts, PrunePlan, RusticResult, SnapshotFile,
- SnapshotGroup, SnapshotGroupCriterion, Tree, TreeStreamerOptions,
+ NodeStreamer, PathList, ProgressBars, PruneOpts, PrunePlan, ReadIndex, RusticResult,
+ SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, Tree, TreeStreamerOptions,
};
mod warm_up;
@@ -502,6 +503,16 @@ impl Repository {
commands::snapshots::get_snapshot_group(self, ids, group_by, filter)
}
+ pub fn get_snapshot_from_str(
+ &self,
+ id: &str,
+ filter: impl FnMut(&SnapshotFile) -> bool + Send + Sync,
+ ) -> RusticResult {
+ let p = self.pb.progress_counter("getting snapshot...");
+ let snap = SnapshotFile::from_str(self.dbe(), id, filter, &p)?;
+ Ok(snap)
+ }
+
pub fn get_snapshots(&self, ids: &[String]) -> RusticResult> {
let p = self.pb.progress_counter("getting snapshots...");
SnapshotFile::from_ids(self.dbe(), ids, &p)
@@ -670,6 +681,16 @@ impl Open for IndexedStatus {
}
}
+impl Repository
{
+ pub fn get_index_entry(&self, tpe: BlobType, id: &Id) -> RusticResult {
+ let ie = self
+ .index()
+ .get_id(tpe, id)
+ .ok_or_else(|| RepositoryErrorKind::IdNotFound(*id))?;
+ Ok(ie)
+ }
+}
+
impl Repository {
pub fn node_from_snapshot_path(
&self,
@@ -684,6 +705,14 @@ impl Repository {
Tree::node_from_path(self.index(), snap.tree, Path::new(path))
}
+ pub fn node_from_snapshot_and_path(
+ &self,
+ snap: &SnapshotFile,
+ path: &str,
+ ) -> RusticResult {
+ Tree::node_from_path(self.index(), snap.tree, Path::new(path))
+ }
+
pub fn cat_tree(
&self,
snap: &str,
diff --git a/src/commands/diff.rs b/src/commands/diff.rs
index 3f3ea69..8f1b73e 100644
--- a/src/commands/diff.rs
+++ b/src/commands/diff.rs
@@ -6,15 +6,14 @@ use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
use abscissa_core::{Command, Runnable, Shutdown};
-use std::io::Read;
use std::path::{Path, PathBuf};
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{bail, Context, Result};
use rustic_core::{
- hash, IndexBackend, LocalDestination, LocalSource, LocalSourceFilterOptions,
- LocalSourceSaveOptions, Node, NodeStreamer, NodeType, Open, Progress, ProgressBars, ReadIndex,
- ReadSourceEntry, RusticResult, SnapshotFile, Tree,
+ BlobType, IndexedFull, LocalDestination, LocalSource, LocalSourceFilterOptions,
+ LocalSourceSaveOptions, Node, NodeType, ReadSourceEntry, Repository, RusticResult,
+ TreeStreamerOptions,
};
/// `diff` subcommand
@@ -53,31 +52,25 @@ impl DiffCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
- let repo = open_repository(&config)?;
+ let repo = open_repository(&config)?.to_indexed()?;
- let be = repo.dbe();
let (id1, path1) = arg_to_snap_path(&self.snap1, "");
let (id2, path2) = arg_to_snap_path(&self.snap2, path1);
- let progress_options = &config.global.progress_options;
-
_ = match (id1, id2) {
(Some(id1), Some(id2)) => {
// diff between two snapshots
- let p = progress_options.progress_spinner("getting snapshots...");
- let snaps = SnapshotFile::from_ids(be, &[id1.to_string(), id2.to_string()], &p)?;
- p.finish();
+ let snaps = repo.get_snapshots(&[id1.to_string(), id2.to_string()])?;
let snap1 = &snaps[0];
let snap2 = &snaps[1];
- let index = IndexBackend::new(be, &progress_options.progress_counter(""))?;
- let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?;
- let node2 = Tree::node_from_path(&index, snap2.tree, Path::new(path2))?;
+ let node1 = repo.node_from_snapshot_and_path(snap1, path1)?;
+ let node2 = repo.node_from_snapshot_and_path(snap2, path2)?;
diff(
- NodeStreamer::new(index.clone(), &node1)?,
- NodeStreamer::new(index, &node2)?,
+ repo.ls(&node1, &TreeStreamerOptions::default(), true)?,
+ repo.ls(&node2, &TreeStreamerOptions::default(), true)?,
self.no_content,
|_path, node1, node2| Ok(node1.content == node2.content),
self.metadata,
@@ -85,13 +78,10 @@ impl DiffCmd {
}
(Some(id1), None) => {
// diff between snapshot and local path
- let p = progress_options.progress_spinner("getting snapshot...");
let snap1 =
- SnapshotFile::from_str(be, id1, |sn| config.snapshot_filter.matches(sn), &p)?;
- p.finish();
+ repo.get_snapshot_from_str(id1, |sn| config.snapshot_filter.matches(sn))?;
- let index = IndexBackend::new(be, &progress_options.progress_counter(""))?;
- let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?;
+ let node1 = repo.node_from_snapshot_and_path(&snap1, path1)?;
let local = LocalDestination::new(path2, false, !node1.is_dir())?;
let path2 = PathBuf::from(path2);
let is_dir = path2
@@ -103,24 +93,11 @@ impl DiffCmd {
&self.ignore_opts,
&[&path2],
)?
- .map(|item| {
- let ReadSourceEntry { path, node, .. } = match item {
- Ok(it) => it,
- Err(err) => {
- status_err!("{}", err);
- RUSTIC_APP.shutdown(Shutdown::Crash);
- }
- };
+ .map(|item| -> RusticResult<_> {
+ let ReadSourceEntry { path, node, .. } = item?;
let path = if is_dir {
// remove given path prefix for dirs as local path
- match path.strip_prefix(&path2) {
- Ok(it) => it,
- Err(err) => {
- status_err!("{}", err);
- RUSTIC_APP.shutdown(Shutdown::Crash);
- }
- }
- .to_path_buf()
+ path.strip_prefix(&path2).unwrap().to_path_buf()
} else {
// ensure that we really get the filename if local path is a file
path2.file_name().unwrap().into()
@@ -129,10 +106,10 @@ impl DiffCmd {
});
diff(
- NodeStreamer::new(index.clone(), &node1)?,
+ repo.ls(&node1, &TreeStreamerOptions::default(), true)?,
src,
self.no_content,
- |path, node1, _node2| identical_content_local(&local, &index, path, node1),
+ |path, node1, _node2| identical_content_local(&local, &repo, path, node1),
self.metadata,
)
}
@@ -158,26 +135,20 @@ fn arg_to_snap_path<'a>(arg: &'a str, default_path: &'a str) -> (Option<&'a str>
}
}
-fn identical_content_local(
+fn identical_content_local(
local: &LocalDestination,
- index: &impl ReadIndex,
+ repo: &Repository
,
path: &Path,
node: &Node,
) -> Result {
let Some(mut open_file) = local.get_matching_file(path, node.meta.size) else { return Ok(false) };
for id in node.content.iter().flatten() {
- let ie = index
- .get_data(id)
- .ok_or_else(|| anyhow!("did not find id {} in index", id))?;
+ let ie = repo.get_index_entry(BlobType::Data, id)?;
let length = ie.data_length();
-
- // check if SHA256 matches
- let mut vec = vec![0; length as usize];
- if open_file.read_exact(&mut vec).is_ok() && id == &hash(&vec) {
- continue;
+ if !id.blob_matches_reader(length as usize, &mut open_file) {
+ return Ok(false);
}
- return Ok(false);
}
Ok(true)
}