diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-07-16 23:00:25 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-16 23:00:25 -0700 |
| commit | a524b4f485ce5aa2f234c742bd7d1e75386f569b (patch) | |
| tree | 794b343485e4adace63dc091703d2368f9075616 /weed/s3api/s3api_object_handlers.go | |
| parent | 89706d36dccc5d851ef6b818f0dd32249e6560a3 (diff) | |
| download | seaweedfs-a524b4f485ce5aa2f234c742bd7d1e75386f569b.tar.xz seaweedfs-a524b4f485ce5aa2f234c742bd7d1e75386f569b.zip | |
Object locking need to persist the tags and set the headers (#6994)
* fix object locking read and write
No logic to include object lock metadata in HEAD/GET response headers
No logic to extract object lock metadata from PUT request headers
* add tests for object locking
* Update weed/s3api/s3api_object_handlers_put.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* refactor
* add unit tests
* sync versions
* Update s3_worm_integration_test.go
* fix legal hold values
* lint
* fix tests
* racing condition when enable versioning
* fix tests
* validate put object lock header
* allow check lock permissions for PUT
* default to OFF legal hold
* only set object lock headers for objects that are actually from object lock-enabled buckets
fix --- FAIL: TestAddObjectLockHeadersToResponse/Handle_entry_with_no_object_lock_metadata (0.00s)
* address comments
* fix tests
* purge
* fix
* refactoring
* address comment
* address comment
* Update weed/s3api/s3api_object_handlers_put.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers_put.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* avoid nil
* ensure locked objects cannot be overwritten
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Diffstat (limited to 'weed/s3api/s3api_object_handlers.go')
| -rw-r--r-- | weed/s3api/s3api_object_handlers.go | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 6b811a024..0aa96b21a 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "net/url" + "strconv" "strings" "time" @@ -195,6 +196,9 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) // Set version ID in response header w.Header().Set("x-amz-version-id", targetVersionId) + + // Add object lock metadata to response headers if present + s3a.addObjectLockHeadersToResponse(w, entry) } else { // Handle regular GET (non-versioned) destUrl = s3a.toFilerUrl(bucket, object) @@ -271,6 +275,9 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request // Set version ID in response header w.Header().Set("x-amz-version-id", targetVersionId) + + // Add object lock metadata to response headers if present + s3a.addObjectLockHeadersToResponse(w, entry) } else { // Handle regular HEAD (non-versioned) destUrl = s3a.toFilerUrl(bucket, object) @@ -435,3 +442,44 @@ func passThroughResponse(proxyResponse *http.Response, w http.ResponseWriter) (s } return statusCode, bytesTransferred } + +// addObjectLockHeadersToResponse extracts object lock metadata from entry Extended attributes +// and adds the appropriate S3 headers to the response +func (s3a *S3ApiServer) addObjectLockHeadersToResponse(w http.ResponseWriter, entry *filer_pb.Entry) { + if entry == nil || entry.Extended == nil { + return + } + + // Check if this entry has any object lock metadata (indicating it's from an object lock enabled bucket) + hasObjectLockMode := false + hasRetentionDate := false + + // Add object lock mode header if present + if modeBytes, exists := entry.Extended[s3_constants.ExtObjectLockModeKey]; exists && len(modeBytes) > 0 { + w.Header().Set(s3_constants.AmzObjectLockMode, string(modeBytes)) + hasObjectLockMode = true + } + + // Add retention until date header if present + if dateBytes, exists := entry.Extended[s3_constants.ExtRetentionUntilDateKey]; exists && len(dateBytes) > 0 { + dateStr := string(dateBytes) + // Convert Unix timestamp to ISO8601 format for S3 compatibility + if timestamp, err := strconv.ParseInt(dateStr, 10, 64); err == nil { + retainUntilDate := time.Unix(timestamp, 0).UTC() + w.Header().Set(s3_constants.AmzObjectLockRetainUntilDate, retainUntilDate.Format(time.RFC3339)) + hasRetentionDate = true + } else { + glog.Errorf("addObjectLockHeadersToResponse: failed to parse retention until date from stored metadata (dateStr: %s): %v", dateStr, err) + } + } + + // Add legal hold header - AWS S3 behavior: always include legal hold for object lock enabled buckets + if legalHoldBytes, exists := entry.Extended[s3_constants.ExtLegalHoldKey]; exists && len(legalHoldBytes) > 0 { + // Return stored S3 standard "ON"/"OFF" values directly + w.Header().Set(s3_constants.AmzObjectLockLegalHold, string(legalHoldBytes)) + } else if hasObjectLockMode || hasRetentionDate { + // If this entry has object lock metadata (indicating object lock enabled bucket) + // but no legal hold specifically set, default to "OFF" as per AWS S3 behavior + w.Header().Set(s3_constants.AmzObjectLockLegalHold, s3_constants.LegalHoldOff) + } +} |
