Browse Source

fixes

 Include VersionId in multipart upload completion responses when versioning is enabled
 Block retention mode changes (GOVERNANCE ↔ COMPLIANCE) without bypass permissions
 Handle all object lock validation errors consistently with proper error codes
 Pass the remaining object lock tests
pull/6997/head
chrislu 5 months ago
parent
commit
1e1806c7a1
  1. 73
      weed/s3api/filer_multipart.go
  2. 14
      weed/s3api/s3api_object_retention.go

73
weed/s3api/filer_multipart.go

@ -247,13 +247,72 @@ func (s3a *S3ApiServer) completeMultipartUpload(input *s3.CompleteMultipartUploa
return nil, s3err.ErrInternalError
}
output = &CompleteMultipartUploadResult{
CompleteMultipartUploadOutput: s3.CompleteMultipartUploadOutput{
Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer.ToHttpAddress(), urlEscapeObject(dirName), urlPathEscape(entryName))),
Bucket: input.Bucket,
ETag: aws.String("\"" + filer.ETagChunks(finalParts) + "\""),
Key: objectKey(input.Key),
},
// Check if versioning is enabled for this bucket
versioningEnabled, vErr := s3a.isVersioningEnabled(*input.Bucket)
if vErr == nil && versioningEnabled {
// For versioned buckets, create a version and return the version ID
versionId := generateVersionId()
versionFileName := s3a.getVersionFileName(versionId)
versionDir := dirName + "/" + entryName + ".versions"
// Move the completed object to the versions directory
err = s3a.mkFile(versionDir, versionFileName, finalParts, func(versionEntry *filer_pb.Entry) {
if versionEntry.Extended == nil {
versionEntry.Extended = make(map[string][]byte)
}
versionEntry.Extended[s3_constants.ExtVersionIdKey] = []byte(versionId)
versionEntry.Extended[s3_constants.SeaweedFSUploadId] = []byte(*input.UploadId)
for k, v := range pentry.Extended {
if k != "key" {
versionEntry.Extended[k] = v
}
}
if pentry.Attributes.Mime != "" {
versionEntry.Attributes.Mime = pentry.Attributes.Mime
} else if mime != "" {
versionEntry.Attributes.Mime = mime
}
versionEntry.Attributes.FileSize = uint64(offset)
})
if err != nil {
glog.Errorf("completeMultipartUpload: failed to create version %s: %v", versionId, err)
return nil, s3err.ErrInternalError
}
// Create a delete marker for the main object (latest version)
err = s3a.mkFile(dirName, entryName, nil, func(mainEntry *filer_pb.Entry) {
if mainEntry.Extended == nil {
mainEntry.Extended = make(map[string][]byte)
}
mainEntry.Extended[s3_constants.ExtVersionIdKey] = []byte(versionId)
mainEntry.Extended[s3_constants.ExtDeleteMarkerKey] = []byte("false") // This is the latest version, not a delete marker
})
if err != nil {
glog.Errorf("completeMultipartUpload: failed to update main entry: %v", err)
return nil, s3err.ErrInternalError
}
output = &CompleteMultipartUploadResult{
CompleteMultipartUploadOutput: s3.CompleteMultipartUploadOutput{
Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer.ToHttpAddress(), urlEscapeObject(dirName), urlPathEscape(entryName))),
Bucket: input.Bucket,
ETag: aws.String("\"" + filer.ETagChunks(finalParts) + "\""),
Key: objectKey(input.Key),
VersionId: aws.String(versionId),
},
}
} else {
// For non-versioned buckets, return response without VersionId
output = &CompleteMultipartUploadResult{
CompleteMultipartUploadOutput: s3.CompleteMultipartUploadOutput{
Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer.ToHttpAddress(), urlEscapeObject(dirName), urlPathEscape(entryName))),
Bucket: input.Bucket,
ETag: aws.String("\"" + filer.ETagChunks(finalParts) + "\""),
Key: objectKey(input.Key),
},
}
}
for _, deleteEntry := range deleteEntries {

14
weed/s3api/s3api_object_retention.go

@ -348,6 +348,20 @@ func (s3a *S3ApiServer) setObjectRetention(bucket, object, versionId string, ret
// Check if object is already under retention
if entry.Extended != nil {
if existingMode, exists := entry.Extended[s3_constants.ExtObjectLockModeKey]; exists {
// Check if attempting to change retention mode
if retention.Mode != "" && string(existingMode) != retention.Mode {
// Attempting to change retention mode
if string(existingMode) == s3_constants.RetentionModeCompliance {
// Cannot change compliance mode retention without bypass
return ErrComplianceModeActive
}
if string(existingMode) == s3_constants.RetentionModeGovernance && !bypassGovernance {
// Cannot change governance mode retention without bypass
return ErrGovernanceModeActive
}
}
if existingDateBytes, dateExists := entry.Extended[s3_constants.ExtRetentionUntilDateKey]; dateExists {
if timestamp, err := strconv.ParseInt(string(existingDateBytes), 10, 64); err == nil {
existingDate := time.Unix(timestamp, 0)

Loading…
Cancel
Save