mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-26 15:00:48 +01:00
s3: answer GET and HEAD of a delete marker as S3 does (#12051)
* s3: answer GET and HEAD of a delete marker as S3 does A delete marker as the current version now returns 404 NoSuchKey with x-amz-delete-marker: true and the marker's x-amz-version-id. Asking for the marker by versionId returns 405 MethodNotAllowed with the same headers and Last-Modified, instead of 404 NoSuchKey. * s3: check the tag-conditioned read grant before answering a delete marker The marker branch returned before the entry-based policy recheck the rest of the read path performs, so an s3:ExistingObjectTag-conditioned Deny did not apply to the delete-marker response and its version headers. * s3: send Allow: DELETE with the delete-marker 405 RFC 9110 15.5.6 requires a 405 answer to list the methods the resource supports; S3 sends allow: DELETE for a delete marker named by versionId. * s3: keep delete-marker headers on conditional-read misses If-Match or If-Unmodified-Since against a current delete marker short- circuited to a bare NoSuchKey. Carry the resolved marker through the conditional result so the 404 keeps x-amz-delete-marker and x-amz-version-id, matching the unconditional read. * s3: exercise delete-marker responses through GET and HEAD handlers * s3: read marker version id from the entry before the caller's guess A delete marker written outside the normal PUT path can miss the version-id attribute while the v_<id> file name still names it; the new x-amz-version-id header would then report "null". Reuse versionIdFromEntry so both the handler and conditional-miss paths emit the real id. * s3: answer 405 for versionId=null on a suspended-versioning marker The null marker lives in .versions while getSpecificObjectVersion only checks the plain path, so GET/HEAD ?versionId=null answered NoSuchVersion. On that miss, look up the null marker and answer it like any other marker named by versionId. * s3: propagate null-marker lookup failures instead of answering 404 A transient filer error reading .versions/v_null was swallowed as "no marker", so GET/HEAD answered NoSuchVersion for a live read failure. NotFound and absent entries still mean no marker; anything else is InternalError now. --------- Co-authored-by: Chris Lu <chris.lu@gmail.com> Co-authored-by: chrislusf <chrislusf@users.noreply.github.com>
This commit is contained in:
5 files changed
+333
-11
No files matched your search
@@ -314,6 +314,26 @@ func (s3a *S3ApiServer) createNullDeleteMarker(bucket, object string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nullDeleteMarkerEntry returns the suspended-versioning "null" delete marker
|
||||
// stored under .versions, or nil when the object has none.
|
||||
// getSpecificObjectVersion only checks the plain path for "null", so callers
|
||||
// that must answer a marker named by versionId consult this on a miss. Lookup
|
||||
// failures other than not found propagate: a masked read error would answer
|
||||
// a live filer hiccup as a deleted version.
|
||||
func (s3a *S3ApiServer) nullDeleteMarkerEntry(bucket, object string) (*filer_pb.Entry, error) {
|
||||
entry, err := s3a.getEntry(s3a.getVersionedObjectDir(bucket, s3_constants.NormalizeObjectKey(object)), s3a.getVersionFileName("null"))
|
||||
if err != nil {
|
||||
if errors.Is(err, filer_pb.ErrNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil || string(entry.Extended[s3_constants.ExtDeleteMarkerKey]) != "true" {
|
||||
return nil, nil
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// versionListItem represents an item in the unified version/prefix list
|
||||
type versionListItem struct {
|
||||
key string
|
||||
|
||||
Reference in new issue
Block a user