diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-11-25 13:25:47 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-25 13:25:47 -0800 |
| commit | 2e6c746a30600f54a7a2a4da0b068dbcc5945e5f (patch) | |
| tree | 57d0eecdab6c619e369d8a903c62502b4661600d /weed/s3api/s3api_object_handlers_copy.go | |
| parent | 3f1a34d8d7ce9229ce7951baac24f19e59e7d4b0 (diff) | |
| download | seaweedfs-2e6c746a30600f54a7a2a4da0b068dbcc5945e5f.tar.xz seaweedfs-2e6c746a30600f54a7a2a4da0b068dbcc5945e5f.zip | |
fix copying for paused versioning buckets (#7548)
* fix copying for paused versioning buckets
* copy for non versioned files
* add tests
* better tests
* Update weed/s3api/s3api_object_handlers_copy.go
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* remove etag
* update
* Update s3api_object_handlers_copy_test.go
* Update weed/s3api/s3api_object_handlers_copy_test.go
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers_copy_test.go
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* revert
---------
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Diffstat (limited to 'weed/s3api/s3api_object_handlers_copy.go')
| -rw-r--r-- | weed/s3api/s3api_object_handlers_copy.go | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/weed/s3api/s3api_object_handlers_copy.go b/weed/s3api/s3api_object_handlers_copy.go index 91da98a0e..4dd31c8ce 100644 --- a/weed/s3api/s3api_object_handlers_copy.go +++ b/weed/s3api/s3api_object_handlers_copy.go @@ -230,10 +230,11 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request } } - // Check if destination bucket has versioning configured - dstVersioningConfigured, err := s3a.isVersioningConfigured(dstBucket) + // Check if destination bucket has versioning enabled + // Only create versions if versioning is explicitly "Enabled", not "Suspended" or unconfigured + dstVersioningState, err := s3a.getVersioningState(dstBucket) if err != nil { - glog.Errorf("Error checking versioning status for destination bucket %s: %v", dstBucket, err) + glog.Errorf("Error checking versioning state for destination bucket %s: %v", dstBucket, err) s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) return } @@ -241,7 +242,7 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request var dstVersionId string var etag string - if dstVersioningConfigured { + if shouldCreateVersionForCopy(dstVersioningState) { // For versioned destination, create a new version dstVersionId = generateVersionId() glog.V(2).Infof("CopyObjectHandler: creating version %s for destination %s/%s", dstVersionId, dstBucket, dstObject) @@ -294,6 +295,9 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request w.Header().Set("x-amz-version-id", dstVersionId) } else { // For non-versioned destination, use regular copy + // Remove any versioning-related metadata from source that shouldn't carry over + cleanupVersioningMetadata(dstEntry.Extended) + dstPath := util.FullPath(fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, dstBucket, dstObject)) dstDir, dstName := dstPath.DirAndName() @@ -2327,6 +2331,25 @@ func (ctx *EncryptionHeaderContext) shouldSkipEncryptedToUnencryptedHeader() boo return hasSourceEncryption && !hasDestinationEncryption && isAnyEncryptionHeader } +// cleanupVersioningMetadata removes versioning-related metadata from Extended attributes +// when copying to non-versioned or suspended-versioning buckets. +// This prevents objects in non-versioned buckets from carrying invalid versioning metadata. +// It also removes the source ETag to prevent metadata inconsistency, as a new ETag will be +// calculated for the destination object. +func cleanupVersioningMetadata(metadata map[string][]byte) { + delete(metadata, s3_constants.ExtVersionIdKey) + delete(metadata, s3_constants.ExtDeleteMarkerKey) + delete(metadata, s3_constants.ExtIsLatestKey) + delete(metadata, s3_constants.ExtETagKey) +} + +// shouldCreateVersionForCopy determines whether a version should be created during a copy operation +// based on the destination bucket's versioning state. +// Returns true only if versioning is explicitly "Enabled", not "Suspended" or unconfigured. +func shouldCreateVersionForCopy(versioningState string) bool { + return versioningState == s3_constants.VersioningEnabled +} + // shouldSkipEncryptionHeader determines if a header should be skipped when copying extended attributes // based on the source and destination encryption types. This consolidates the repetitive logic for // filtering encryption-related headers during copy operations. |
