mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-07 12:35:54 +00:00
fix(restore): reject SELECT restore and keep typed S3 errors (#7113)
* fix(restore): reject SELECT restore and keep typed S3 errors RestoreObject accepted `Type=SELECT` requests, but the restore path can only write the retrieved bytes back to the source key: `put_restore_opts` built SELECT output options and `restore_transitioned_object` then PUT them over the source bucket/object. On an unversioned bucket that dropped `x-amz-restore`, user metadata and tags from the live object; on a versioned bucket it published a bogus latest version. Nothing was ever written to `OutputLocation.S3`, yet the response still carried a fabricated `x-amz-restore-output-path`. Reject SELECT at the API boundary with a typed NotImplemented, before any guard or metadata write, and fail closed in `put_restore_opts` as the backstop for any other caller. Every other RestoreObject failure was collapsed into a `Custom` error code, which serializes as a generic retryable 500: a missing key or version, a malformed version-id, an object that was never transitioned, an illegal `Days`, and authorization or storage failures all looked the same to a client. Map them to their S3 identities instead — NoSuchKey, NoSuchVersion, InvalidArgument, InvalidObjectState, InvalidRequest, MalformedXML — by preserving `StorageError` through `post_restore_opts` and letting `ApiError` do the mapping. The intentional 409 RestoreAlreadyInProgress and 503 SlowDown behaviour is unchanged, and request validation now runs before any lock is taken. backlog#1341, backlog#2205 * test(restore): give the typed-error regression the ecstore test stack `execute_restore_object_maps_failures_to_typed_s3_errors` builds a real ECStore fixture, and under nextest each test runs in a spawned thread with libtest's 2 MiB stack. On Linux CI that overflowed: the test aborted with SIGABRT / "fatal runtime error: stack overflow" while every other test in the run passed. Add it to the `ecstore-base-stack` filter in both the default and ci profiles, alongside the other `package(rustfs)` tests that drive the same store fixture. 4 MiB matches what the deeper multipart and access roundtrips already use.
This commit is contained in:
@@ -69,7 +69,7 @@ filter = 'package(rustfs-ecstore) & test(/^(bucket::lifecycle::bucket_lifecycle_
|
||||
setup = 'ecstore-large-stack'
|
||||
|
||||
[[profile.default.scripts]]
|
||||
filter = 'package(rustfs-ecstore) | package(rustfs-s3select-api) | package(rustfs-scanner) | (package(rustfs) & test(/^(app::multipart_usecase::tests::concurrent_completions_share_durable_bucket_quota_reservations|app::object::delete::tests::compressed_delete_requests_update_observed_usage_without_releasing_quota_floor|app::object::internal_put::tests::internal_multipart_roundtrip_completes_and_abort_leaves_nothing|storage::access::tests::(delete_object_access_captures_authorized_bucket_incarnation|copy_operations_reject_recreated_source_bucket_after_authorization|request_slot_keeps_bucket_policy_bound_to_its_store))$/))'
|
||||
filter = 'package(rustfs-ecstore) | package(rustfs-s3select-api) | package(rustfs-scanner) | (package(rustfs) & test(/^(app::multipart_usecase::tests::concurrent_completions_share_durable_bucket_quota_reservations|app::object::delete::tests::compressed_delete_requests_update_observed_usage_without_releasing_quota_floor|app::object::internal_put::tests::internal_multipart_roundtrip_completes_and_abort_leaves_nothing|app::object::restore::tests::execute_restore_object_maps_failures_to_typed_s3_errors|storage::access::tests::(delete_object_access_captures_authorized_bucket_incarnation|copy_operations_reject_recreated_source_bucket_after_authorization|request_slot_keeps_bucket_policy_bound_to_its_store))$/))'
|
||||
setup = 'ecstore-base-stack'
|
||||
|
||||
[[profile.default.scripts]]
|
||||
@@ -210,7 +210,7 @@ filter = 'package(rustfs-ecstore) & test(/^(bucket::lifecycle::bucket_lifecycle_
|
||||
setup = 'ecstore-large-stack'
|
||||
|
||||
[[profile.ci.scripts]]
|
||||
filter = 'package(rustfs-ecstore) | package(rustfs-s3select-api) | package(rustfs-scanner) | (package(rustfs) & test(/^(app::multipart_usecase::tests::concurrent_completions_share_durable_bucket_quota_reservations|app::object::delete::tests::compressed_delete_requests_update_observed_usage_without_releasing_quota_floor|app::object::internal_put::tests::internal_multipart_roundtrip_completes_and_abort_leaves_nothing|storage::access::tests::(delete_object_access_captures_authorized_bucket_incarnation|copy_operations_reject_recreated_source_bucket_after_authorization|request_slot_keeps_bucket_policy_bound_to_its_store))$/))'
|
||||
filter = 'package(rustfs-ecstore) | package(rustfs-s3select-api) | package(rustfs-scanner) | (package(rustfs) & test(/^(app::multipart_usecase::tests::concurrent_completions_share_durable_bucket_quota_reservations|app::object::delete::tests::compressed_delete_requests_update_observed_usage_without_releasing_quota_floor|app::object::internal_put::tests::internal_multipart_roundtrip_completes_and_abort_leaves_nothing|app::object::restore::tests::execute_restore_object_maps_failures_to_typed_s3_errors|storage::access::tests::(delete_object_access_captures_authorized_bucket_incarnation|copy_operations_reject_recreated_source_bucket_after_authorization|request_slot_keeps_bucket_policy_bound_to_its_store))$/))'
|
||||
setup = 'ecstore-base-stack'
|
||||
|
||||
[[profile.ci.scripts]]
|
||||
|
||||
Reference in New Issue
Block a user