Unified · Split
| @@ -1,3 +1,4 @@ | ||
| 1 | +use crate::pack; | |
| 1 | 2 | use byteorder::{BigEndian, ByteOrder}; |
| 2 | 3 | use filebuffer::FileBuffer; |
| 3 | 4 | use git_object::{self as object, SHA1_SIZE}; |
| @@ -54,6 +55,14 @@ quick_error! { · 50 unmodified lines | ||
| 54 | 55 | Mismatch { expected: object::Id, actual: object::Id } { |
| 55 | 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 | 87 | pub fn checksum_of_index(&self) -> object::Id { |
| 79 | 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 | 94 | #[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 | + } | |
| 92 | 123 | } |
| 93 | 124 | } |
| 94 | 125 | |
| @@ -24,7 +24,7 @@ fn pack_lookup() { · 23 unmodified lines | ||
| 24 | 24 | assert_eq!(pack.kind(), pack::Kind::V2); |
| 25 | 25 | assert_eq!(pack.num_objects(), idx.num_objects()); |
| 26 | 26 | assert_eq!( |
| 27 | − idx.verify_checksum_of_index().unwrap(), | |
| 27 | + idx.verify_checksum_of_index(Some(&pack)).unwrap(), | |
| 28 | 28 | idx.checksum_of_index() |
| 29 | 29 | ); |
| 30 | 30 | for idx_entry in idx.iter() { |
| @@ -66,6 +66,10 @@ fn iter() { · 35 unmodified lines | ||
| 66 | 66 | assert_eq!(idx.kind(), *kind); |
| 67 | 67 | assert_eq!(idx.version(), *version); |
| 68 | 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 | 73 | assert_eq!(idx.checksum_of_index(), hex_to_id(index_checksum)); |
| 70 | 74 | assert_eq!(idx.checksum_of_pack(), hex_to_id(pack_checksum)); |
| 71 | 75 | assert_eq!(idx.iter().count(), *num_objects as usize); |
| @@ -5,7 +5,11 @@ pub fn init() -> Result<()> { · 4 unmodified lines | ||
| 5 | 5 | git_repository::init::repository().with_context(|| "Repository initialization failed") |
| 6 | 6 | } |
| 7 | 7 | |
| 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<()> { | |
| 9 | 13 | let path = path.as_ref(); |
| 10 | 14 | let ext = path.extension() |
| 11 | 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 | 22 | "idx" => { |
| 19 | 23 | let idx = git_odb::pack::index::File::at(path) |
| 20 | 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 | 34 | ext => { |
| 24 | 35 | return Err(anyhow!( |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | use anyhow::Result; |
| 4 | 4 | use gitoxide_core as core; |
| 5 | −use std::io::stdout; | |
| 5 | +use std::io::{stderr, stdout}; | |
| 6 | 6 | use structopt::StructOpt; |
| 7 | 7 | |
| 8 | 8 | mod options { |
| @@ -53,7 +53,7 @@ fn main() -> Result<()> { · 44 unmodified lines | ||
| 53 | 53 | options::Subcommands::Init => core::init(), |
| 54 | 54 | options::Subcommands::Plumbing(cmd) => match cmd { |
| 55 | 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 | }?; |