Files
flipper/packer/src/tarsum.rs
Stiopa Koltsov 7227f722ac Apply rustfmt
Reviewed By: zertosh

Differential Revision: D38485159

fbshipit-source-id: 0abdf44561da00697953fe2cb9211ceb5cf27ac0
2022-08-06 08:42:43 -07:00

98 lines
3.1 KiB
Rust

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//! Intrinsic hash for a tarball.
use std::collections;
use std::io;
use anyhow::Result;
use crate::types;
/// Computes the intrinsic SHA256 checksum of a tar archive.
pub fn tarsum<R: io::Read>(reader: R) -> Result<types::HashSum> {
use sha2::Digest;
let mut archive = tar::Archive::new(reader);
let mut map = collections::BTreeMap::new();
// Store all entries in a BTreeMap using their path as key which implements `Ord`.
// This way we ensure that the hash of hashes is consistent indepent of the
// file order inside the archive.
for entry in archive.entries()? {
let mut e = entry?;
let path = e.path()?.into_owned();
map.insert(path.clone(), digest_file(&mut e)?);
}
let mut digest = sha2::Sha256::new();
for (_, file_hash) in map {
digest.update(file_hash.0);
}
let hash = digest.finalize();
Ok(types::HashSum(data_encoding::HEXLOWER.encode(&hash)))
}
fn digest_file<R: io::Read>(reader: &mut R) -> io::Result<types::HashSum> {
use sha2::Digest;
let mut digest = sha2::Sha256::new();
io::copy(reader, &mut digest)?;
let hash = digest.finalize();
Ok(types::HashSum(data_encoding::HEXLOWER.encode(&hash)))
}
#[cfg(test)]
mod test {
use std::fs;
use std::path;
use super::*;
#[test]
fn test_nested_archive_tarsum() {
// This is an archive with a nested directory structure.
let archive_path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("__fixtures__")
.join("nested_archive.tar");
let reader = fs::File::open(archive_path).unwrap();
let res = tarsum(reader).unwrap();
assert_eq!(
res,
types::HashSum(
"6f92565bb50b9469494b3e1ad668f5d809caa3ffb534c3e56dec75f7ea7912df".to_string()
)
);
}
#[test]
fn test_differently_ordered_archives() {
// These archives have equivalent contents but were created in reverse ways:
// $ tar cf archive_a.tar archive/a.txt
// $ tar cf archive_b.tar archive/b.txt
// $ tar rf archive_a.tar archive/b.txt
// $ tar rf archive_b.tar archive/a.txt
// $ gsha256sum archive_*.tar
// 8de80c3904d85115d1595d48c215022e5db225c920811d4d2eee80586e6390c8 archive_a.tar
// 60097b704cb1684f52f7e98e98193595ea2876047e9ecc6931db97757bc8a5fd archive_b.tar
let fixture_path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("__fixtures__");
let archive_a = fixture_path.join("archive_a.tar");
let archive_b = fixture_path.join("archive_b.tar");
let reader_a = fs::File::open(archive_a).unwrap();
let reader_b = fs::File::open(archive_b).unwrap();
let res_a = tarsum(reader_a).unwrap();
let res_b = tarsum(reader_b).unwrap();
assert_eq!(res_a, res_b);
}
}