mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-03 11:37:42 +00:00
api: switch to crc_fast for checksumming and implement full-object mpu checksums
This commit is contained in:
@@ -21,9 +21,7 @@ garage_util.workspace = true
|
||||
base64.workspace = true
|
||||
bytes.workspace = true
|
||||
chrono.workspace = true
|
||||
crc32fast.workspace = true
|
||||
crc32c.workspace = true
|
||||
crc64fast-nvme.workspace = true
|
||||
crc-fast.workspace = true
|
||||
crypto-common.workspace = true
|
||||
err-derive.workspace = true
|
||||
hex.workspace = true
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::hash::Hasher;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use base64::prelude::*;
|
||||
use crc32c::Crc32cHasher as Crc32c;
|
||||
use crc32fast::Hasher as Crc32;
|
||||
use crc64fast_nvme::Digest as Crc64Nvme;
|
||||
use crc_fast::{CrcAlgorithm, Digest as CrcDigest};
|
||||
use md5::{Digest, Md5};
|
||||
use sha1::Sha1;
|
||||
use sha2::Sha256;
|
||||
@@ -41,6 +38,21 @@ pub type Md5Checksum = [u8; 16];
|
||||
pub type Sha1Checksum = [u8; 20];
|
||||
pub type Sha256Checksum = [u8; 32];
|
||||
|
||||
// -- MAP OF CRC ALGORITHMS :
|
||||
// CRC32 -> CrcAlgorithm::Crc32IsoHdlc
|
||||
// CRC32C -> CrcAlgorithm::Crc32Iscsi
|
||||
// CRC64NVME -> CrcAlgorithm::Crc64Nvme
|
||||
|
||||
pub fn new_crc32() -> CrcDigest {
|
||||
CrcDigest::new(CrcAlgorithm::Crc32IsoHdlc)
|
||||
}
|
||||
pub fn new_crc32c() -> CrcDigest {
|
||||
CrcDigest::new(CrcAlgorithm::Crc32Iscsi)
|
||||
}
|
||||
pub fn new_crc64nvme() -> CrcDigest {
|
||||
CrcDigest::new(CrcAlgorithm::Crc64Nvme)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ExpectedChecksums {
|
||||
// base64-encoded md5 (content-md5 header)
|
||||
@@ -52,9 +64,9 @@ pub struct ExpectedChecksums {
|
||||
}
|
||||
|
||||
pub struct Checksummer {
|
||||
pub crc32: Option<Crc32>,
|
||||
pub crc32c: Option<Crc32c>,
|
||||
pub crc64nvme: Option<Crc64Nvme>,
|
||||
pub crc32: Option<CrcDigest>,
|
||||
pub crc32c: Option<CrcDigest>,
|
||||
pub crc64nvme: Option<CrcDigest>,
|
||||
pub md5: Option<Md5>,
|
||||
pub sha1: Option<Sha1>,
|
||||
pub sha256: Option<Sha256>,
|
||||
@@ -103,13 +115,13 @@ impl Checksummer {
|
||||
self.sha256 = Some(Sha256::new());
|
||||
}
|
||||
if matches!(&expected.extra, Some(ChecksumValue::Crc32(_))) {
|
||||
self.crc32 = Some(Crc32::new());
|
||||
self.crc32 = Some(new_crc32());
|
||||
}
|
||||
if matches!(&expected.extra, Some(ChecksumValue::Crc32c(_))) {
|
||||
self.crc32c = Some(Crc32c::default());
|
||||
self.crc32c = Some(new_crc32c());
|
||||
}
|
||||
if matches!(&expected.extra, Some(ChecksumValue::Crc64Nvme(_))) {
|
||||
self.crc64nvme = Some(Crc64Nvme::default());
|
||||
self.crc64nvme = Some(new_crc64nvme());
|
||||
}
|
||||
if matches!(&expected.extra, Some(ChecksumValue::Sha1(_))) {
|
||||
self.sha1 = Some(Sha1::new());
|
||||
@@ -119,13 +131,13 @@ impl Checksummer {
|
||||
pub fn add(mut self, algo: Option<ChecksumAlgorithm>) -> Self {
|
||||
match algo {
|
||||
Some(ChecksumAlgorithm::Crc32) => {
|
||||
self.crc32 = Some(Crc32::new());
|
||||
self.crc32 = Some(new_crc32());
|
||||
}
|
||||
Some(ChecksumAlgorithm::Crc32c) => {
|
||||
self.crc32c = Some(Crc32c::default());
|
||||
self.crc32c = Some(new_crc32c());
|
||||
}
|
||||
Some(ChecksumAlgorithm::Crc64Nvme) => {
|
||||
self.crc64nvme = Some(Crc64Nvme::default());
|
||||
self.crc64nvme = Some(new_crc64nvme());
|
||||
}
|
||||
Some(ChecksumAlgorithm::Sha1) => {
|
||||
self.sha1 = Some(Sha1::new());
|
||||
@@ -143,10 +155,10 @@ impl Checksummer {
|
||||
crc32.update(bytes);
|
||||
}
|
||||
if let Some(crc32c) = &mut self.crc32c {
|
||||
crc32c.write(bytes);
|
||||
crc32c.update(bytes);
|
||||
}
|
||||
if let Some(crc64nvme) = &mut self.crc64nvme {
|
||||
crc64nvme.write(bytes);
|
||||
crc64nvme.update(bytes);
|
||||
}
|
||||
if let Some(md5) = &mut self.md5 {
|
||||
md5.update(bytes);
|
||||
@@ -161,11 +173,9 @@ impl Checksummer {
|
||||
|
||||
pub fn finalize(self) -> Checksums {
|
||||
Checksums {
|
||||
crc32: self.crc32.map(|x| u32::to_be_bytes(x.finalize())),
|
||||
crc32c: self
|
||||
.crc32c
|
||||
.map(|x| u32::to_be_bytes(u32::try_from(x.finish()).unwrap())),
|
||||
crc64nvme: self.crc64nvme.map(|x| u64::to_be_bytes(x.sum64())),
|
||||
crc32: self.crc32.map(|x| u32::to_be_bytes(x.finalize() as u32)),
|
||||
crc32c: self.crc32c.map(|x| u32::to_be_bytes(x.finalize() as u32)),
|
||||
crc64nvme: self.crc64nvme.map(|x| u64::to_be_bytes(x.finalize())),
|
||||
md5: self.md5.map(|x| x.finalize()[..].try_into().unwrap()),
|
||||
sha1: self.sha1.map(|x| x.finalize()[..].try_into().unwrap()),
|
||||
sha256: self.sha256.map(|x| x.finalize()[..].try_into().unwrap()),
|
||||
@@ -197,10 +207,11 @@ impl Checksums {
|
||||
}
|
||||
if let Some(extra) = expected.extra {
|
||||
let algo = extra.algorithm();
|
||||
if self.extract(Some(algo)) != Some(extra) {
|
||||
let calculated = self.extract(Some(algo));
|
||||
if calculated != Some(extra) {
|
||||
return Err(Error::InvalidDigest(format!(
|
||||
"Failed to validate checksum for algorithm {:?}",
|
||||
algo
|
||||
"Failed to validate checksum for algorithm {:?}: calculated {:?}, expected {:?}",
|
||||
algo, calculated, extra
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ async-compression.workspace = true
|
||||
base64.workspace = true
|
||||
bytes.workspace = true
|
||||
chrono.workspace = true
|
||||
crc32fast.workspace = true
|
||||
crc32c.workspace = true
|
||||
crc64fast-nvme.workspace = true
|
||||
crc-fast.workspace = true
|
||||
err-derive.workspace = true
|
||||
hex.workspace = true
|
||||
hmac.workspace = true
|
||||
|
||||
@@ -1002,9 +1002,11 @@ mod tests {
|
||||
inner: ObjectVersionMetaInner {
|
||||
headers: vec![],
|
||||
checksum: None,
|
||||
checksum_type: None,
|
||||
},
|
||||
},
|
||||
checksum_algorithm: None,
|
||||
checksum_type: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+126
-85
@@ -1,12 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::hash::Hasher;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base64::prelude::*;
|
||||
use crc32c::Crc32cHasher as Crc32c;
|
||||
use crc32fast::Hasher as Crc32;
|
||||
use crc64fast_nvme::Digest as Crc64Nvme;
|
||||
use crc_fast::{CrcAlgorithm, Digest as CrcDigest};
|
||||
use futures::prelude::*;
|
||||
use hyper::{header::HeaderValue, HeaderMap, Request, Response};
|
||||
use md5::{Digest, Md5};
|
||||
@@ -410,7 +407,11 @@ pub async fn handle_complete_multipart_upload(
|
||||
// https://teppen.io/2018/06/23/aws_s3_etags/
|
||||
let mut checksummer = MultipartChecksummer::init(checksum_algorithm, checksum_type);
|
||||
for part in parts.iter() {
|
||||
checksummer.update(part.etag.as_ref().unwrap(), part.checksum)?;
|
||||
checksummer.update(
|
||||
part.etag.as_ref().unwrap(),
|
||||
part.checksum,
|
||||
part.size.unwrap(),
|
||||
)?;
|
||||
}
|
||||
let (checksum_md5, checksum_extra) = checksummer.finalize();
|
||||
|
||||
@@ -693,36 +694,14 @@ pub(crate) struct MultipartChecksummer {
|
||||
pub extra: Option<MultipartExtraChecksummer>,
|
||||
}
|
||||
|
||||
pub(crate) enum MultipartExtraChecksummer {
|
||||
Crc32(Crc32),
|
||||
Crc32c(Crc32c),
|
||||
Crc64Nvme(Crc64Nvme),
|
||||
Sha1(Sha1),
|
||||
Sha256(Sha256),
|
||||
}
|
||||
|
||||
impl MultipartChecksummer {
|
||||
pub(crate) fn init(algo: Option<ChecksumAlgorithm>, cktype: Option<ChecksumType>) -> Self {
|
||||
if cktype == Some(ChecksumType::FullObject) {
|
||||
todo!()
|
||||
}
|
||||
Self {
|
||||
md5: Md5::new(),
|
||||
extra: match algo {
|
||||
None => None,
|
||||
Some(ChecksumAlgorithm::Crc32) => {
|
||||
Some(MultipartExtraChecksummer::Crc32(Crc32::new()))
|
||||
}
|
||||
Some(ChecksumAlgorithm::Crc32c) => {
|
||||
Some(MultipartExtraChecksummer::Crc32c(Crc32c::default()))
|
||||
}
|
||||
Some(ChecksumAlgorithm::Crc64Nvme) => {
|
||||
Some(MultipartExtraChecksummer::Crc64Nvme(Crc64Nvme::default()))
|
||||
}
|
||||
Some(ChecksumAlgorithm::Sha1) => Some(MultipartExtraChecksummer::Sha1(Sha1::new())),
|
||||
Some(ChecksumAlgorithm::Sha256) => {
|
||||
Some(MultipartExtraChecksummer::Sha256(Sha256::new()))
|
||||
}
|
||||
extra: match (algo, cktype) {
|
||||
(None, None) => None,
|
||||
(Some(algo), Some(cktype)) => Some(MultipartExtraChecksummer::init(algo, cktype)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -731,68 +710,130 @@ impl MultipartChecksummer {
|
||||
&mut self,
|
||||
etag: &str,
|
||||
checksum: Option<ChecksumValue>,
|
||||
part_len: u64,
|
||||
) -> Result<(), Error> {
|
||||
self.md5
|
||||
.update(&hex::decode(&etag).ok_or_message("invalid etag hex")?);
|
||||
match (&mut self.extra, checksum) {
|
||||
(None, _) => (),
|
||||
(
|
||||
Some(MultipartExtraChecksummer::Crc32(ref mut crc32)),
|
||||
Some(ChecksumValue::Crc32(x)),
|
||||
) => {
|
||||
crc32.update(&x);
|
||||
}
|
||||
(
|
||||
Some(MultipartExtraChecksummer::Crc32c(ref mut crc32c)),
|
||||
Some(ChecksumValue::Crc32c(x)),
|
||||
) => {
|
||||
crc32c.write(&x);
|
||||
}
|
||||
(
|
||||
Some(MultipartExtraChecksummer::Crc64Nvme(ref mut crc64nvme)),
|
||||
Some(ChecksumValue::Crc64Nvme(x)),
|
||||
) => {
|
||||
crc64nvme.write(&x);
|
||||
}
|
||||
(Some(MultipartExtraChecksummer::Sha1(ref mut sha1)), Some(ChecksumValue::Sha1(x))) => {
|
||||
sha1.update(&x);
|
||||
}
|
||||
(
|
||||
Some(MultipartExtraChecksummer::Sha256(ref mut sha256)),
|
||||
Some(ChecksumValue::Sha256(x)),
|
||||
) => {
|
||||
sha256.update(&x);
|
||||
}
|
||||
(Some(_), b) => {
|
||||
return Err(Error::internal_error(format!(
|
||||
"part checksum was not computed correctly, got: {:?}",
|
||||
b
|
||||
)))
|
||||
}
|
||||
if let Some(extra) = &mut self.extra {
|
||||
extra.update(checksum, part_len)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn finalize(self) -> (Md5Checksum, Option<ChecksumValue>) {
|
||||
let md5 = self.md5.finalize()[..].try_into().unwrap();
|
||||
let extra = match self.extra {
|
||||
None => None,
|
||||
Some(MultipartExtraChecksummer::Crc32(crc32)) => {
|
||||
Some(ChecksumValue::Crc32(u32::to_be_bytes(crc32.finalize())))
|
||||
}
|
||||
Some(MultipartExtraChecksummer::Crc32c(crc32c)) => Some(ChecksumValue::Crc32c(
|
||||
u32::to_be_bytes(u32::try_from(crc32c.finish()).unwrap()),
|
||||
)),
|
||||
Some(MultipartExtraChecksummer::Crc64Nvme(crc64nvme)) => Some(
|
||||
ChecksumValue::Crc64Nvme(u64::to_be_bytes(crc64nvme.sum64())),
|
||||
),
|
||||
Some(MultipartExtraChecksummer::Sha1(sha1)) => {
|
||||
Some(ChecksumValue::Sha1(sha1.finalize()[..].try_into().unwrap()))
|
||||
}
|
||||
Some(MultipartExtraChecksummer::Sha256(sha256)) => Some(ChecksumValue::Sha256(
|
||||
sha256.finalize()[..].try_into().unwrap(),
|
||||
)),
|
||||
};
|
||||
let extra = self.extra.map(|c| c.finalize());
|
||||
(md5, extra)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MultipartExtraChecksummer {
|
||||
FullObjectCrc(CrcAlgorithm, Option<u64>),
|
||||
CompositeCrc(ChecksumAlgorithm, CrcDigest),
|
||||
CompositeSha1(Sha1),
|
||||
CompositeSha256(Sha256),
|
||||
}
|
||||
|
||||
impl MultipartExtraChecksummer {
|
||||
fn init(algo: ChecksumAlgorithm, cktype: ChecksumType) -> Self {
|
||||
match (algo, cktype) {
|
||||
(algo, ChecksumType::FullObject) => {
|
||||
let crc_type = match algo {
|
||||
ChecksumAlgorithm::Crc32 => CrcAlgorithm::Crc32IsoHdlc,
|
||||
ChecksumAlgorithm::Crc32c => CrcAlgorithm::Crc32Iscsi,
|
||||
ChecksumAlgorithm::Crc64Nvme => CrcAlgorithm::Crc64Nvme,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Self::FullObjectCrc(crc_type, None)
|
||||
}
|
||||
(ChecksumAlgorithm::Crc32, ChecksumType::Composite) => {
|
||||
Self::CompositeCrc(ChecksumAlgorithm::Crc32, new_crc32())
|
||||
}
|
||||
(ChecksumAlgorithm::Crc32c, ChecksumType::Composite) => {
|
||||
Self::CompositeCrc(ChecksumAlgorithm::Crc32c, new_crc32c())
|
||||
}
|
||||
(ChecksumAlgorithm::Sha1, ChecksumType::Composite) => Self::CompositeSha1(Sha1::new()),
|
||||
(ChecksumAlgorithm::Sha256, ChecksumType::Composite) => {
|
||||
Self::CompositeSha256(Sha256::new())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, checksum: Option<ChecksumValue>, part_len: u64) -> Result<(), Error> {
|
||||
match (self, checksum) {
|
||||
(Self::FullObjectCrc(crc_algo, crc_value), Some(ck)) => {
|
||||
let ck_u64 = match ck {
|
||||
ChecksumValue::Crc32(x) => u32::from_be_bytes(x) as u64,
|
||||
ChecksumValue::Crc32c(x) => u32::from_be_bytes(x) as u64,
|
||||
ChecksumValue::Crc64Nvme(x) => u64::from_be_bytes(x),
|
||||
_ => {
|
||||
return Err(Error::internal_error(format!(
|
||||
"part checksum was not computed correctly, got: {:?}",
|
||||
ck
|
||||
)))
|
||||
}
|
||||
};
|
||||
*crc_value = match *crc_value {
|
||||
None => Some(ck_u64),
|
||||
Some(prev) => Some(crc_fast::checksum_combine(
|
||||
*crc_algo, prev, ck_u64, part_len,
|
||||
)),
|
||||
};
|
||||
}
|
||||
(Self::CompositeCrc(_, digest), Some(ck)) => match ck {
|
||||
ChecksumValue::Crc32(x) => digest.update(&x),
|
||||
ChecksumValue::Crc32c(x) => digest.update(&x),
|
||||
ChecksumValue::Crc64Nvme(x) => digest.update(&x),
|
||||
_ => {
|
||||
return Err(Error::internal_error(format!(
|
||||
"part checksum was not computed correctly, got: {:?}",
|
||||
ck
|
||||
)))
|
||||
}
|
||||
},
|
||||
(Self::CompositeSha1(sha1), Some(ChecksumValue::Sha1(x))) => {
|
||||
sha1.update(&x);
|
||||
}
|
||||
(Self::CompositeSha256(sha256), Some(ChecksumValue::Sha256(x))) => {
|
||||
sha256.update(&x);
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::internal_error(format!(
|
||||
"part checksum was not computed correctly, got: {:?}",
|
||||
checksum
|
||||
)))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn finalize(self) -> ChecksumValue {
|
||||
match self {
|
||||
Self::FullObjectCrc(algo, value) => match (algo, value) {
|
||||
(CrcAlgorithm::Crc32IsoHdlc, Some(v)) => {
|
||||
ChecksumValue::Crc32(u32::to_be_bytes(v as u32))
|
||||
}
|
||||
(CrcAlgorithm::Crc32Iscsi, Some(v)) => {
|
||||
ChecksumValue::Crc32c(u32::to_be_bytes(v as u32))
|
||||
}
|
||||
(CrcAlgorithm::Crc64Nvme, Some(v)) => ChecksumValue::Crc64Nvme(u64::to_be_bytes(v)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Self::CompositeCrc(algo, crc) => match algo {
|
||||
ChecksumAlgorithm::Crc32 => {
|
||||
ChecksumValue::Crc32(u32::to_be_bytes(crc.finalize() as u32))
|
||||
}
|
||||
ChecksumAlgorithm::Crc32c => {
|
||||
ChecksumValue::Crc32c(u32::to_be_bytes(crc.finalize() as u32))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Self::CompositeSha1(sha1) => {
|
||||
ChecksumValue::Sha1(sha1.finalize()[..].try_into().unwrap())
|
||||
}
|
||||
Self::CompositeSha256(sha256) => {
|
||||
ChecksumValue::Sha256(sha256.finalize()[..].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user