Skip to content

Commit 6829e5e

Browse files
committed
change!: multi-index integrity check; use integrity::Outcome for various integrity checks (#279)
1 parent d851bed commit 6829e5e

File tree

6 files changed

+205
-32
lines changed

6 files changed

+205
-32
lines changed

git-pack/src/bundle/mod.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,25 @@ mod find;
55
///
66
pub mod write;
77

8-
mod verify {
8+
///
9+
pub mod verify {
910
use std::sync::atomic::AtomicBool;
1011

1112
use git_features::progress::Progress;
1213

14+
///
15+
pub mod integrity {
16+
/// Returned by [`Bundle::verify_integrity()`][crate::Bundle::verify_integrity()].
17+
pub struct Outcome<P> {
18+
/// The computed checksum of the index which matched the stored one.
19+
pub actual_index_checksum: git_hash::ObjectId,
20+
/// The packs traversal outcome
21+
pub pack_traverse_outcome: crate::index::traverse::Outcome,
22+
/// The provided progress instance.
23+
pub progress: Option<P>,
24+
}
25+
}
26+
1327
use crate::Bundle;
1428

1529
impl Bundle {
@@ -23,25 +37,28 @@ mod verify {
2337
thread_limit: Option<usize>,
2438
progress: Option<P>,
2539
should_interrupt: &AtomicBool,
26-
) -> Result<
27-
(git_hash::ObjectId, Option<crate::index::traverse::Outcome>, Option<P>),
28-
crate::index::traverse::Error<crate::index::verify::integrity::Error>,
29-
>
40+
) -> Result<integrity::Outcome<P>, crate::index::traverse::Error<crate::index::verify::integrity::Error>>
3041
where
3142
P: Progress,
3243
C: crate::cache::DecodeEntry,
3344
{
34-
self.index.verify_integrity(
35-
Some(crate::index::verify::PackContext {
36-
data: &self.pack,
37-
verify_mode,
38-
traversal_algorithm: traversal,
39-
make_cache_fn: make_pack_lookup_cache,
40-
}),
41-
thread_limit,
42-
progress,
43-
should_interrupt,
44-
)
45+
self.index
46+
.verify_integrity(
47+
Some(crate::index::verify::PackContext {
48+
data: &self.pack,
49+
verify_mode,
50+
traversal_algorithm: traversal,
51+
make_cache_fn: make_pack_lookup_cache,
52+
}),
53+
thread_limit,
54+
progress,
55+
should_interrupt,
56+
)
57+
.map(|o| integrity::Outcome {
58+
actual_index_checksum: o.actual_index_checksum,
59+
pack_traverse_outcome: o.pack_traverse_outcome.expect("pack is set"),
60+
progress: o.progress,
61+
})
4562
}
4663
}
4764
}

git-pack/src/index/verify.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ pub mod integrity {
2727
actual: BString,
2828
},
2929
}
30+
31+
/// Returned by [`index::File::verify_integrity()`][crate::index::File::verify_integrity()].
32+
pub struct Outcome<P> {
33+
/// The computed checksum of the index which matched the stored one.
34+
pub actual_index_checksum: git_hash::ObjectId,
35+
/// The packs traversal outcome, if one was provided
36+
pub pack_traverse_outcome: Option<crate::index::traverse::Outcome>,
37+
/// The provided progress instance.
38+
pub progress: Option<P>,
39+
}
3040
}
3141

3242
///
@@ -121,10 +131,7 @@ impl index::File {
121131
thread_limit: Option<usize>,
122132
progress: Option<P>,
123133
should_interrupt: &AtomicBool,
124-
) -> Result<
125-
(git_hash::ObjectId, Option<index::traverse::Outcome>, Option<P>),
126-
index::traverse::Error<crate::index::verify::integrity::Error>,
127-
>
134+
) -> Result<integrity::Outcome<P>, index::traverse::Error<crate::index::verify::integrity::Error>>
128135
where
129136
P: Progress,
130137
C: crate::cache::DecodeEntry,
@@ -155,11 +162,19 @@ impl index::File {
155162
should_interrupt,
156163
},
157164
)
158-
.map(|(id, outcome, root)| (id, Some(outcome), root)),
165+
.map(|(id, outcome, root)| integrity::Outcome {
166+
actual_index_checksum: id,
167+
pack_traverse_outcome: Some(outcome),
168+
progress: root,
169+
}),
159170
None => self
160171
.verify_checksum(root.add_child("Sha1 of index"), should_interrupt)
161172
.map_err(Into::into)
162-
.map(|id| (id, None, root.into_inner())),
173+
.map(|id| integrity::Outcome {
174+
actual_index_checksum: id,
175+
pack_traverse_outcome: None,
176+
progress: root.into_inner(),
177+
}),
163178
}
164179
}
165180

git-pack/src/multi_index/verify.rs

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,34 @@ use crate::multi_index::File;
22
use git_features::progress::Progress;
33
use std::sync::atomic::AtomicBool;
44

5+
///
6+
pub mod integrity {
7+
/// Returned by [`multi_index::File::verify_integrity()`][crate::multi_index::File::verify_integrity()].
8+
#[derive(thiserror::Error, Debug)]
9+
#[allow(missing_docs)]
10+
pub enum Error {
11+
#[error(transparent)]
12+
MultiIndexChecksum(#[from] crate::multi_index::verify::checksum::Error),
13+
#[error(transparent)]
14+
IndexIntegrity(#[from] crate::index::verify::integrity::Error),
15+
#[error(transparent)]
16+
BundleInit(#[from] crate::bundle::init::Error),
17+
}
18+
19+
/// Returned by [`multi_index::File::verify_integrity()`][crate::multi_index::File::verify_integrity()].
20+
pub struct Outcome<P> {
21+
/// The computed checksum of the multi-index which matched the stored one.
22+
pub actual_index_checksum: git_hash::ObjectId,
23+
/// The for each entry in [`index_names()`][super::File::index_names()] provide the corresponding pack traversal outcome.
24+
pub pack_traverse_outcomes: Vec<crate::index::traverse::Outcome>,
25+
/// The provided progress instance.
26+
pub progress: Option<P>,
27+
}
28+
}
29+
530
///
631
pub mod checksum {
7-
/// Returned by [`index::File::verify_checksum()`][crate::index::File::verify_checksum()].
32+
/// Returned by [`multi_index::File::verify_checksum()`][crate::multi_index::File::verify_checksum()].
833
pub type Error = crate::verify::checksum::Error;
934
}
1035

@@ -36,16 +61,88 @@ impl File {
3661
traversal: crate::index::traverse::Algorithm,
3762
make_pack_lookup_cache: impl Fn() -> C + Send + Clone,
3863
thread_limit: Option<usize>,
39-
progress: Option<P>,
64+
mut progress: Option<P>,
4065
should_interrupt: &AtomicBool,
41-
) -> Result<
42-
(git_hash::ObjectId, Option<crate::index::traverse::Outcome>, Option<P>),
43-
crate::index::traverse::Error<crate::index::verify::integrity::Error>,
44-
>
66+
) -> Result<integrity::Outcome<P>, crate::index::traverse::Error<integrity::Error>>
4567
where
4668
P: Progress,
4769
C: crate::cache::DecodeEntry,
4870
{
49-
todo!()
71+
let parent = self.path.parent().expect("must be in a directory");
72+
73+
let mut progress = git_features::progress::DoOrDiscard::from(progress);
74+
let actual_index_checksum = self
75+
.verify_checksum(
76+
progress.add_child(format!("checksum of '{}'", self.path.display())),
77+
should_interrupt,
78+
)
79+
.map_err(integrity::Error::from)
80+
.map_err(crate::index::traverse::Error::Processor)?;
81+
let mut progress = progress.into_inner();
82+
83+
let mut pack_traverse_outcomes = Vec::new();
84+
for index_file_name in &self.index_names {
85+
let bundle = crate::Bundle::at(parent.join(index_file_name), self.object_hash)
86+
.map_err(integrity::Error::from)
87+
.map_err(crate::index::traverse::Error::Processor)?;
88+
if let Some(progress) = progress.as_mut() {
89+
progress.set_name(index_file_name.display().to_string());
90+
}
91+
let crate::bundle::verify::integrity::Outcome {
92+
actual_index_checksum: _,
93+
pack_traverse_outcome,
94+
progress: used_progress,
95+
} = bundle
96+
.verify_integrity(
97+
verify_mode,
98+
traversal,
99+
make_pack_lookup_cache.clone(),
100+
thread_limit,
101+
progress,
102+
should_interrupt,
103+
)
104+
.map_err(|err| {
105+
use crate::index::traverse::Error::*;
106+
match err {
107+
Processor(err) => Processor(integrity::Error::IndexIntegrity(err)),
108+
VerifyChecksum(err) => VerifyChecksum(err),
109+
Tree(err) => Tree(err),
110+
TreeTraversal(err) => TreeTraversal(err),
111+
PackDecode { id, offset, source } => PackDecode { id, offset, source },
112+
PackMismatch { expected, actual } => PackMismatch { expected, actual },
113+
PackObjectMismatch {
114+
expected,
115+
actual,
116+
offset,
117+
kind,
118+
} => PackObjectMismatch {
119+
expected,
120+
actual,
121+
offset,
122+
kind,
123+
},
124+
Crc32Mismatch {
125+
expected,
126+
actual,
127+
offset,
128+
kind,
129+
} => Crc32Mismatch {
130+
expected,
131+
actual,
132+
offset,
133+
kind,
134+
},
135+
Interrupted => Interrupted,
136+
}
137+
})?;
138+
pack_traverse_outcomes.push(pack_traverse_outcome);
139+
progress = used_progress;
140+
}
141+
142+
Ok(integrity::Outcome {
143+
actual_index_checksum,
144+
pack_traverse_outcomes,
145+
progress,
146+
})
50147
}
51148
}

git-pack/tests/pack/index.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ mod file {
311311
progress::Discard.into(),
312312
&AtomicBool::new(false)
313313
)
314-
.map(|(a, b, _)| (a, b))?,
314+
.map(|o| (o.actual_index_checksum, o.pack_traverse_outcome))?,
315315
(idx.index_checksum(), Some(stats.to_owned())),
316316
"{:?} -> {:?}",
317317
algo,
@@ -411,7 +411,7 @@ mod file {
411411
progress::Discard.into(),
412412
&AtomicBool::new(false)
413413
)
414-
.map(|(a, b, _)| (a, b))?,
414+
.map(|o| (o.actual_index_checksum, o.pack_traverse_outcome))?,
415415
(idx.index_checksum(), None)
416416
);
417417
assert_eq!(idx.index_checksum(), hex_to_id(index_checksum));

git-pack/tests/pack/multi_index.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn access() {
5353

5454
mod verify {
5555
use crate::pack::multi_index::multi_index;
56+
use common_macros::b_tree_map;
5657
use git_features::progress;
5758
use std::sync::atomic::AtomicBool;
5859

@@ -65,4 +66,47 @@ mod verify {
6566
file.checksum()
6667
);
6768
}
69+
70+
#[test]
71+
fn integrity() {
72+
let (file, _) = multi_index();
73+
let outcome = file
74+
.verify_integrity(
75+
git_pack::index::verify::Mode::HashCrc32DecodeEncode,
76+
git_pack::index::traverse::Algorithm::DeltaTreeLookup,
77+
|| git_pack::cache::Never,
78+
None,
79+
Some(progress::Discard),
80+
&AtomicBool::new(false),
81+
)
82+
.unwrap();
83+
assert_eq!(outcome.actual_index_checksum, file.checksum());
84+
assert_eq!(
85+
outcome.pack_traverse_outcomes,
86+
vec![git_pack::index::traverse::Outcome {
87+
average: git_pack::data::decode_entry::Outcome {
88+
kind: git_object::Kind::Tree,
89+
num_deltas: 1,
90+
decompressed_size: 47,
91+
compressed_size: 46,
92+
object_size: 152
93+
},
94+
objects_per_chain_length: b_tree_map! {
95+
0 => 326,
96+
1 => 106,
97+
2 => 326,
98+
3 => 108,
99+
4 => 2,
100+
},
101+
total_compressed_entries_size: 40628,
102+
total_decompressed_entries_size: 40919,
103+
total_object_size: 131993,
104+
pack_size: 42856,
105+
num_commits: 16,
106+
num_trees: 40,
107+
num_tags: 1,
108+
num_blobs: 811
109+
}]
110+
);
111+
}
68112
}

gitoxide-core/src/pack/verify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ where
176176
progress,
177177
&should_interrupt,
178178
)
179-
.map(|(a, b, _)| (a, b))
179+
.map(|o| (o.actual_index_checksum, o.pack_traverse_outcome))
180180
.with_context(|| "Verification failure")?
181181
}
182182
ext => return Err(anyhow!("Unknown extension {:?}, expecting 'idx' or 'pack'", ext)),

0 commit comments

Comments
 (0)