mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
fix(s3): return NoSuchUpload for abort_multipart_upload when upload_id not found (#1569)
Signed-off-by: 安正超 <anzhengchao@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
+7
-1
@@ -219,6 +219,7 @@ impl From<StorageError> for ApiError {
|
|||||||
StorageError::SlowDown => S3ErrorCode::SlowDown,
|
StorageError::SlowDown => S3ErrorCode::SlowDown,
|
||||||
StorageError::PrefixAccessDenied(_, _) => S3ErrorCode::AccessDenied,
|
StorageError::PrefixAccessDenied(_, _) => S3ErrorCode::AccessDenied,
|
||||||
StorageError::InvalidUploadIDKeyCombination(_, _) => S3ErrorCode::InvalidArgument,
|
StorageError::InvalidUploadIDKeyCombination(_, _) => S3ErrorCode::InvalidArgument,
|
||||||
|
StorageError::MalformedUploadID(_) => S3ErrorCode::InvalidArgument,
|
||||||
StorageError::ObjectNameTooLong(_, _) => S3ErrorCode::InvalidArgument,
|
StorageError::ObjectNameTooLong(_, _) => S3ErrorCode::InvalidArgument,
|
||||||
StorageError::ObjectNamePrefixAsSlash(_, _) => S3ErrorCode::InvalidArgument,
|
StorageError::ObjectNamePrefixAsSlash(_, _) => S3ErrorCode::InvalidArgument,
|
||||||
StorageError::ObjectNotFound(_, _) => S3ErrorCode::NoSuchKey,
|
StorageError::ObjectNotFound(_, _) => S3ErrorCode::NoSuchKey,
|
||||||
@@ -227,7 +228,7 @@ impl From<StorageError> for ApiError {
|
|||||||
StorageError::FileNotFound => S3ErrorCode::NoSuchKey,
|
StorageError::FileNotFound => S3ErrorCode::NoSuchKey,
|
||||||
StorageError::FileVersionNotFound => S3ErrorCode::NoSuchVersion,
|
StorageError::FileVersionNotFound => S3ErrorCode::NoSuchVersion,
|
||||||
StorageError::VersionNotFound(_, _, _) => S3ErrorCode::NoSuchVersion,
|
StorageError::VersionNotFound(_, _, _) => S3ErrorCode::NoSuchVersion,
|
||||||
StorageError::InvalidUploadID(_, _, _) => S3ErrorCode::InvalidPart,
|
StorageError::InvalidUploadID(_, _, _) => S3ErrorCode::NoSuchUpload,
|
||||||
StorageError::InvalidVersionID(_, _, _) => S3ErrorCode::InvalidArgument,
|
StorageError::InvalidVersionID(_, _, _) => S3ErrorCode::InvalidArgument,
|
||||||
StorageError::DataMovementOverwriteErr(_, _, _) => S3ErrorCode::InvalidArgument,
|
StorageError::DataMovementOverwriteErr(_, _, _) => S3ErrorCode::InvalidArgument,
|
||||||
StorageError::ObjectExistsAsDirectory(_, _) => S3ErrorCode::InvalidArgument,
|
StorageError::ObjectExistsAsDirectory(_, _) => S3ErrorCode::InvalidArgument,
|
||||||
@@ -415,6 +416,11 @@ mod tests {
|
|||||||
(StorageError::VolumeNotFound, S3ErrorCode::NoSuchBucket),
|
(StorageError::VolumeNotFound, S3ErrorCode::NoSuchBucket),
|
||||||
(StorageError::FileNotFound, S3ErrorCode::NoSuchKey),
|
(StorageError::FileNotFound, S3ErrorCode::NoSuchKey),
|
||||||
(StorageError::FileVersionNotFound, S3ErrorCode::NoSuchVersion),
|
(StorageError::FileVersionNotFound, S3ErrorCode::NoSuchVersion),
|
||||||
|
(StorageError::MalformedUploadID("test".into()), S3ErrorCode::InvalidArgument),
|
||||||
|
(
|
||||||
|
StorageError::InvalidUploadID("bucket".into(), "object".into(), "uploadid".into()),
|
||||||
|
S3ErrorCode::NoSuchUpload,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (storage_error, expected_code) in test_cases {
|
for (storage_error, expected_code) in test_cases {
|
||||||
|
|||||||
@@ -4902,11 +4902,25 @@ impl S3 for FS {
|
|||||||
|
|
||||||
let opts = &ObjectOptions::default();
|
let opts = &ObjectOptions::default();
|
||||||
|
|
||||||
store
|
// Special handling for abort_multipart_upload: Per AWS S3 API specification, this operation
|
||||||
|
// should return NoSuchUpload (404) when the upload_id doesn't exist, even if the format
|
||||||
|
// appears invalid. This differs from other multipart operations (upload_part, list_parts,
|
||||||
|
// complete_multipart_upload) which return InvalidArgument for malformed upload_ids.
|
||||||
|
// The lenient validation matches AWS S3 behavior where format validation is relaxed for
|
||||||
|
// abort operations to avoid leaking information about upload_id format requirements.
|
||||||
|
match store
|
||||||
.abort_multipart_upload(bucket.as_str(), key.as_str(), upload_id.as_str(), opts)
|
.abort_multipart_upload(bucket.as_str(), key.as_str(), upload_id.as_str(), opts)
|
||||||
.await
|
.await
|
||||||
.map_err(ApiError::from)?;
|
{
|
||||||
Ok(S3Response::new(AbortMultipartUploadOutput { ..Default::default() }))
|
Ok(_) => Ok(S3Response::new(AbortMultipartUploadOutput { ..Default::default() })),
|
||||||
|
Err(err) => {
|
||||||
|
// Convert MalformedUploadID to NoSuchUpload for S3 API compatibility
|
||||||
|
if matches!(err, StorageError::MalformedUploadID(_)) {
|
||||||
|
return Err(S3Error::new(S3ErrorCode::NoSuchUpload));
|
||||||
|
}
|
||||||
|
Err(ApiError::from(err).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
|||||||
@@ -138,3 +138,4 @@ test_ranged_request_empty_object
|
|||||||
test_ranged_request_invalid_range
|
test_ranged_request_invalid_range
|
||||||
test_set_multipart_tagging
|
test_set_multipart_tagging
|
||||||
test_upload_part_copy_percent_encoded_key
|
test_upload_part_copy_percent_encoded_key
|
||||||
|
test_api_error_from_storage_error_mappings
|
||||||
|
|||||||
Reference in New Issue
Block a user