History

Support for verifying pack files and index files

Sebastian Thiel committed 6 years ago

Commit b09b4e11 parent 4a153da4 files · +61 −15Browse files

Unified · Split

4 files
  1. git-odb/src/pack/index/file.rs +41 −10
  2. git-odb/tests/pack/index.rs +5 −1
  3. gitoxide-core/src/lib.rs +13 −2
  4. src/main.rs +2 −2
git-odb/src/pack/index/file.rs+41−10
@@ -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! { · 50 unmodified lines
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 { · 18 unmodified lines
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
git-odb/tests/pack/index.rs+5−1
@@ -24,7 +24,7 @@ fn pack_lookup() { · 23 unmodified lines
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() { · 35 unmodified lines
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);
gitoxide-core/src/lib.rs+13−2
@@ -5,7 +5,11 @@ pub fn init() -> Result<()> { · 4 unmodified lines
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 · 6 unmodified lines
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!(
src/main.rs+2−2
@@ -2,7 +2,7 @@ · 1 unmodified line
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<()> { · 44 unmodified lines
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 }?;