aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/s3api_object_handlers.go
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2025-07-16 23:00:25 -0700
committerGitHub <noreply@github.com>2025-07-16 23:00:25 -0700
commita524b4f485ce5aa2f234c742bd7d1e75386f569b (patch)
tree794b343485e4adace63dc091703d2368f9075616 /weed/s3api/s3api_object_handlers.go
parent89706d36dccc5d851ef6b818f0dd32249e6560a3 (diff)
downloadseaweedfs-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.go48
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)
+ }
+}