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 @@
1use crate::pack;
1use byteorder::{BigEndian, ByteOrder};2use byteorder::{BigEndian, ByteOrder};
2use filebuffer::FileBuffer;3use filebuffer::FileBuffer;
3use git_object::{self as object, SHA1_SIZE};4use git_object::{self as object, SHA1_SIZE};
@@ -54,6 +55,14 @@ quick_error! { · 50 unmodified lines
54 Mismatch { expected: object::Id, actual: object::Id } {55 Mismatch { expected: object::Id, actual: object::Id } {
55 display("index checksum mismatch: expected {}, got {}", expected, actual)56 display("index checksum mismatch: expected {}, got {}", expected, actual)
56 }57 }
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 }
57 }66 }
58}67}
59 68
@@ -78,17 +87,39 @@ impl File { · 18 unmodified lines
78 pub fn checksum_of_index(&self) -> object::Id {87 pub fn checksum_of_index(&self) -> object::Id {
79 object::Id::from_20_bytes(&self.data[self.data.len() - SHA1_SIZE..])88 object::Id::from_20_bytes(&self.data[self.data.len() - SHA1_SIZE..])
80 }89 }
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.
81 #[cfg(any(feature = "fast-sha1", feature = "minimal-sha1"))]94 #[cfg(any(feature = "fast-sha1", feature = "minimal-sha1"))]
82 pub fn verify_checksum_of_index(&self) -> Result<object::Id, ChecksumError> {95 pub fn verify_checksum_of_index(
83 let mut hasher = crate::sha1::Sha1::default();96 &self,
84 hasher.update(&self.data[..self.data.len() - SHA1_SIZE]);97 pack: Option<&pack::File>,
85 let actual = hasher.digest();98 ) -> Result<object::Id, ChecksumError> {
86 99 let verify_self = || {
87 let expected = self.checksum_of_index();100 let mut hasher = crate::sha1::Sha1::default();
88 if actual == expected {101 hasher.update(&self.data[..self.data.len() - SHA1_SIZE]);
89 Ok(actual)102 let actual = hasher.digest();
90 } else {103
91 Err(ChecksumError::Mismatch { actual, expected })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 }
92 }123 }
93 }124 }
94 125
git-odb/tests/pack/index.rs+5−1
@@ -24,7 +24,7 @@ fn pack_lookup() { · 23 unmodified lines
24 assert_eq!(pack.kind(), pack::Kind::V2);24 assert_eq!(pack.kind(), pack::Kind::V2);
25 assert_eq!(pack.num_objects(), idx.num_objects());25 assert_eq!(pack.num_objects(), idx.num_objects());
26 assert_eq!(26 assert_eq!(
27 idx.verify_checksum_of_index().unwrap(),27 idx.verify_checksum_of_index(Some(&pack)).unwrap(),
28 idx.checksum_of_index()28 idx.checksum_of_index()
29 );29 );
30 for idx_entry in idx.iter() {30 for idx_entry in idx.iter() {
@@ -66,6 +66,10 @@ fn iter() { · 35 unmodified lines
66 assert_eq!(idx.kind(), *kind);66 assert_eq!(idx.kind(), *kind);
67 assert_eq!(idx.version(), *version);67 assert_eq!(idx.version(), *version);
68 assert_eq!(idx.num_objects(), *num_objects);68 assert_eq!(idx.num_objects(), *num_objects);
69 assert_eq!(
70 idx.verify_checksum_of_index(None).unwrap(),
71 idx.checksum_of_index()
72 );
69 assert_eq!(idx.checksum_of_index(), hex_to_id(index_checksum));73 assert_eq!(idx.checksum_of_index(), hex_to_id(index_checksum));
70 assert_eq!(idx.checksum_of_pack(), hex_to_id(pack_checksum));74 assert_eq!(idx.checksum_of_pack(), hex_to_id(pack_checksum));
71 assert_eq!(idx.iter().count(), *num_objects as usize);75 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
5 git_repository::init::repository().with_context(|| "Repository initialization failed")5 git_repository::init::repository().with_context(|| "Repository initialization failed")
6}6}
7 7
8pub fn verify_pack_or_pack_index(path: impl AsRef<Path>, mut out: impl io::Write) -> Result<()> {8pub 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<()> {
9 let path = path.as_ref();13 let path = path.as_ref();
10 let ext = path.extension()14 let ext = path.extension()
11 .and_then(|ext| ext.to_str())15 .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
18 "idx" => {22 "idx" => {
19 let idx = git_odb::pack::index::File::at(path)23 let idx = git_odb::pack::index::File::at(path)
20 .with_context(|| "Could not open pack index file")?;24 .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())?;
22 }33 }
23 ext => {34 ext => {
24 return Err(anyhow!(35 return Err(anyhow!(
src/main.rs+2−2
@@ -2,7 +2,7 @@ · 1 unmodified line
2 2
3use anyhow::Result;3use anyhow::Result;
4use gitoxide_core as core;4use gitoxide_core as core;
5use std::io::stdout;5use std::io::{stderr, stdout};
6use structopt::StructOpt;6use structopt::StructOpt;
7 7
8mod options {8mod options {
@@ -53,7 +53,7 @@ fn main() -> Result<()> { · 44 unmodified lines
53 options::Subcommands::Init => core::init(),53 options::Subcommands::Init => core::init(),
54 options::Subcommands::Plumbing(cmd) => match cmd {54 options::Subcommands::Plumbing(cmd) => match cmd {
55 options::Plumbing::VerifyPack { path } => {55 options::Plumbing::VerifyPack { path } => {
56 core::verify_pack_or_pack_index(path, stdout())56 core::verify_pack_or_pack_index(path, stdout(), stderr())
57 }57 }
58 },58 },
59 }?;59 }?;