mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-02 11:29:17 +00:00
b1ddda3bb2
A same-name CopyObject marks the operation `metadata_only`, which lets the store layer rewrite `xl.meta` in place and leave the data blocks untouched. The handler independently strips the source encryption metadata and calls `sse_encryption`, which mints a *fresh* DEK. On an unversioned bucket both happen at once, so the object ends up with a new DEK sitting beside ciphertext sealed under the old one, and can never be decrypted again. The mirror case is silent: an encrypted source copied without any destination SSE keeps its ciphertext while losing the key metadata, so GET returns raw ciphertext as if it were plaintext, with HTTP 200 and no error anywhere. Keep `metadata_only` off whenever either side of the copy is encrypted, so the store layer performs a full read/write rewrite through `put_object`. This is the same resolution the versioned historical-restore path already uses for this risk (issue #4238), and it matches MinIO's `isSourceEncrypted || isTargetEncrypted -> metadataOnly = false` guard in CopyObjectHandler. The target half of the predicate deliberately tests `effective_sse` rather than the request headers MinIO inspects: `effective_sse` also resolves the bucket default-encryption rule, and `sse_encryption` mints a DEK from that resolved value. A header-only check would miss a same-key copy performed under a bucket default rule. The source half reuses `ObjectInfo::is_encrypted` so a future encryption flavour is covered here as soon as it is recognised there. Versioned buckets were already safe: that path falls through to `put_object` regardless of `metadata_only`. RestoreObject also sets `metadata_only` but only appends restore keys and never re-derives a DEK, so it is unaffected.