mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-06 03:59:14 +00:00
8bf569899a
* fix(ecstore): persist merged checksum type for full-object multipart complete_multipart_upload built the object-level checksum record from a ChecksumType copied before the MULTIPART / INCLUDES_MULTIPART flags were merged in. ChecksumType::merge takes &mut self, so the merge updated the local variable while the copy already inside the Checksum struct stayed behind. The composite branch rebuilt the Checksum from the merged type and was unaffected; the full-object branch never rebuilt it, so those flags never reached disk. rustfs_rio::read_checksums only sets its multipart flag and only emits the "x-amz-checksum-type" = "FULL_OBJECT" entry inside its MULTIPART branch, so a full-object multipart object read back as non-multipart with no type entry, and GetObject and HeadObject answered with no x-amz-checksum-type header at all where AWS returns FULL_OBJECT. Hand the full-object branch the merged type instead of rebuilding the Checksum: the value must stay the running merge produced by add_part, because hashing the concatenated part digests would yield the COMPOSITE value, a different number than the one the client sent. The serialization now lives in multipart_object_checksum_record so both shapes are covered by unit tests. Records written by earlier builds carry the bare algorithm type with no MULTIPART flags and no trailing part block; they keep reading back to the same checksum value, and the FULL_OBJECT reader arm predates this change so older peers parse the new record shape correctly too. Found while root-causing rustfs#6825. * fix(s3): reject contradicting multipart checksum type as client error A CompleteMultipartUpload declaring an x-amz-checksum-type that contradicts the type recorded at CreateMultipartUpload answered 500 InternalError, telling the caller to retry a request that can only ever fail. The storage layer does refuse the combination, but through a generic error that maps to InternalError. Validate the header against the recorded type in the usecase, where the upload metadata returned by get_multipart_info is already in hand, and answer InvalidRequest naming both types, matching AWS. The storage-layer check stays as a backstop for non-HTTP callers. Uploads created without a checksum algorithm record no type, so there is nothing to contradict and the header is left alone rather than newly rejected. Replication is unaffected: replication_put_object_options already excludes x-amz-checksum-type from the metadata it forwards. * test(e2e): cover full-object multipart checksum type round-trip Adds an end-to-end test that a CRC32 FULL_OBJECT multipart upload reports x-amz-checksum-type: FULL_OBJECT and the unsuffixed full-object value on both GetObject and HeadObject, and one that a CompleteMultipartUpload contradicting the recorded type is rejected as InvalidRequest while leaving the upload intact. Extends the existing CRC64NVME multipart test with the same checksum-type assertion. * fix(s3): keep checksum-type validation off the s3s error macro The s3s footprint ratchet (scripts/check_s3s_footprint.sh) counts s3_error! invocation lines and is lower-only: new code must route through the gateway abstractions rather than widen the direct s3s surface the s3gate migration is shrinking. Raise the contradiction through ApiError::invalid_request instead. The response is byte-for-byte identical -- From<ApiError> for S3Error carries the InvalidRequest code and the message through unchanged -- and the usecase already returns ApiError elsewhere, so this is the idiomatic path rather than a way around the counter. The explanatory comment deliberately says "the s3s error macro" instead of naming the macro: the ratchet counts raw matches, so spelling it out in a comment tripped the same check.