mirror of
https://github.com/pgsty/minio.git
synced 2026-09-25 04:15:59 +03:00
fix: recognize timestamp-only retention-removal tombstone in resend compare
retentionRemovedAtSource only recognized representation (1) of a removed retention: the object lock key present with an empty value. But a removal that arrived by replication persists representation (2): restoreRetention (and the receiver's replica update path) writes only the retention ordering timestamp when the mode is empty, leaving the mode and retain-until-date keys absent. For that shape the helper returned false, so replicationActionForTarget skipped the GetObjectRetention confirmation and let getReplicationAction's replicateNone stand, silently dropping a needed removal when the destination HEAD hides retention behind a permission-filtered credential. Recognize representation (2) as well: a present retention ordering timestamp with the mode value absent or empty is a removal. A present timestamp paired with a non-empty mode is a retention that was set, not removed, and still returns false. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7qJqWwy8oFA6aCXWRzXQe Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -1067,15 +1067,29 @@ type objectRetentionGetter interface {
|
||||
GetObjectRetention(ctx context.Context, bucketName, objectName, versionID string) (*minio.RetentionMode, *time.Time, error)
|
||||
}
|
||||
|
||||
// retentionRemovedAtSource reports whether oi carries the shape a removed retention leaves behind,
|
||||
// an object lock key present with an empty value (cmd/object-handlers.go:3211-3215).
|
||||
// retentionRemovedAtSource reports whether oi carries the shape a removed retention leaves behind.
|
||||
// Two representations persist. A retention removed directly on this cluster keeps the object lock
|
||||
// keys present with empty values (PutObjectRetentionHandler, cmd/object-handlers.go:3309-3316). A
|
||||
// removal that arrived by replication keeps only the retention ordering timestamp, with the mode
|
||||
// and retain-until-date keys absent, because restoreRetention and the replica update path write
|
||||
// the timestamp alone when the mode is empty (cmd/bucket-object-lock.go:388-399,
|
||||
// cmd/object-handlers.go:1782-1797). A present ordering timestamp paired with a non-empty mode is
|
||||
// a retention that was set, not removed, and must not be mistaken for one.
|
||||
func retentionRemovedAtSource(oi ObjectInfo) bool {
|
||||
lkMap := caseInsensitiveMap(oi.UserDefined)
|
||||
// Representation (1): an object lock key is present with an empty value.
|
||||
for _, k := range []string{xhttp.AmzObjectLockMode, xhttp.AmzObjectLockRetainUntilDate} {
|
||||
if v, ok := lkMap.Lookup(k); ok && v == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Representation (2): a recorded retention ordering timestamp with the mode value absent or
|
||||
// empty is a removal restoreRetention persisted without the empty public keys.
|
||||
if _, ok := oi.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp]; ok {
|
||||
if v, ok := lkMap.Lookup(xhttp.AmzObjectLockMode); !ok || v == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user