Skip to content

Commit b09b4e1

Browse files
committed
Support for verifying pack files and index files
1 parent 4a153da commit b09b4e1

File tree

4 files changed

+61
-15
lines changed

4 files changed

+61
-15
lines changed

Diff for: git-odb/src/pack/index/file.rs

+41-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::pack;
12
use byteorder::{BigEndian, ByteOrder};
23
use filebuffer::FileBuffer;
34
use git_object::{self as object, SHA1_SIZE};
@@ -54,6 +55,14 @@ quick_error! {
5455
Mismatch { expected: object::Id, actual: object::Id } {
5556
display("index checksum mismatch: expected {}, got {}", expected, actual)
5657
}
58+
PackChecksum (err: pack::ChecksumError) {
59+
display("The pack of this index file failed to verify its checksums")
60+
from()
61+
cause(err)
62+
}
63+
PackMismatch { expected: object::Id, actual: object::Id } {
64+
display("The packfiles checksum didn't match the index file checksum: expected {}, got {}", expected, actual)
65+
}
5766
}
5867
}
5968

@@ -78,17 +87,39 @@ impl File {
7887
pub fn checksum_of_index(&self) -> object::Id {
7988
object::Id::from_20_bytes(&self.data[self.data.len() - SHA1_SIZE..])
8089
}
90+
91+
/// If `pack` is provided, it is expected (and validated to be) the pack belonging to this index.
92+
/// It will be used to validate internal integrity of the pack before checking each objects integrity
93+
/// is indeed as advertised via its SHA1 as stored in this index, as well as the CRC hash.
8194
#[cfg(any(feature = "fast-sha1", feature = "minimal-sha1"))]
82-
pub fn verify_checksum_of_index(&self) -> Result<object::Id, ChecksumError> {
83-
let mut hasher = crate::sha1::Sha1::default();
84-
hasher.update(&self.data[..self.data.len() - SHA1_SIZE]);
85-
let actual = hasher.digest();
86-
87-
let expected = self.checksum_of_index();
88-
if actual == expected {
89-
Ok(actual)
90-
} else {
91-
Err(ChecksumError::Mismatch { actual, expected })
95+
pub fn verify_checksum_of_index(
96+
&self,
97+
pack: Option<&pack::File>,
98+
) -> Result<object::Id, ChecksumError> {
99+
let verify_self = || {
100+
let mut hasher = crate::sha1::Sha1::default();
101+
hasher.update(&self.data[..self.data.len() - SHA1_SIZE]);
102+
let actual = hasher.digest();
103+
104+
let expected = self.checksum_of_index();
105+
if actual == expected {
106+
Ok(actual)
107+
} else {
108+
Err(ChecksumError::Mismatch { actual, expected })
109+
}
110+
};
111+
match pack {
112+
None => verify_self(),
113+
Some(pack) => {
114+
if self.checksum_of_pack() != pack.checksum() {
115+
return Err(ChecksumError::PackMismatch {
116+
actual: pack.checksum(),
117+
expected: self.checksum_of_pack(),
118+
});
119+
}
120+
pack.verify_checksum()?;
121+
verify_self()
122+
}
92123
}
93124
}
94125

Diff for: git-odb/tests/pack/index.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn pack_lookup() {
2424
assert_eq!(pack.kind(), pack::Kind::V2);
2525
assert_eq!(pack.num_objects(), idx.num_objects());
2626
assert_eq!(
27-
idx.verify_checksum_of_index().unwrap(),
27+
idx.verify_checksum_of_index(Some(&pack)).unwrap(),
2828
idx.checksum_of_index()
2929
);
3030
for idx_entry in idx.iter() {
@@ -66,6 +66,10 @@ fn iter() {
6666
assert_eq!(idx.kind(), *kind);
6767
assert_eq!(idx.version(), *version);
6868
assert_eq!(idx.num_objects(), *num_objects);
69+
assert_eq!(
70+
idx.verify_checksum_of_index(None).unwrap(),
71+
idx.checksum_of_index()
72+
);
6973
assert_eq!(idx.checksum_of_index(), hex_to_id(index_checksum));
7074
assert_eq!(idx.checksum_of_pack(), hex_to_id(pack_checksum));
7175
assert_eq!(idx.iter().count(), *num_objects as usize);

Diff for: gitoxide-core/src/lib.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ pub fn init() -> Result<()> {
55
git_repository::init::repository().with_context(|| "Repository initialization failed")
66
}
77

8-
pub fn verify_pack_or_pack_index(path: impl AsRef<Path>, mut out: impl io::Write) -> Result<()> {
8+
pub fn verify_pack_or_pack_index(
9+
path: impl AsRef<Path>,
10+
mut out: impl io::Write,
11+
mut err: impl io::Write,
12+
) -> Result<()> {
913
let path = path.as_ref();
1014
let ext = path.extension()
1115
.and_then(|ext| ext.to_str())
@@ -18,7 +22,14 @@ pub fn verify_pack_or_pack_index(path: impl AsRef<Path>, mut out: impl io::Write
1822
"idx" => {
1923
let idx = git_odb::pack::index::File::at(path)
2024
.with_context(|| "Could not open pack index file")?;
21-
idx.verify_checksum_of_index()?;
25+
let packfile_path = path.with_extension("pack");
26+
let pack = git_odb::pack::File::at(&packfile_path)
27+
.or_else(|e| {
28+
writeln!(err, "Could not find matching pack file at '{}' - only index file will be verified, error was: {}", packfile_path.display(), e).ok();
29+
Err(e)
30+
})
31+
.ok();
32+
idx.verify_checksum_of_index(pack.as_ref())?;
2233
}
2334
ext => {
2435
return Err(anyhow!(

Diff for: src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use anyhow::Result;
44
use gitoxide_core as core;
5-
use std::io::stdout;
5+
use std::io::{stderr, stdout};
66
use structopt::StructOpt;
77

88
mod options {
@@ -53,7 +53,7 @@ fn main() -> Result<()> {
5353
options::Subcommands::Init => core::init(),
5454
options::Subcommands::Plumbing(cmd) => match cmd {
5555
options::Plumbing::VerifyPack { path } => {
56-
core::verify_pack_or_pack_index(path, stdout())
56+
core::verify_pack_or_pack_index(path, stdout(), stderr())
5757
}
5858
},
5959
}?;

0 commit comments

Comments
 (0)