88 "net/http"
99 "net/url"
1010 "path"
11+ "strconv"
1112 "strings"
1213 "time"
1314
@@ -230,14 +231,22 @@ func (b *SynapseBackend) GetObjectAttributes(ctx context.Context, input *s3.GetO
230231 }
231232
232233 checksum := types.Checksum {ChecksumSHA256 : & meta .Checksum }
233- return s3response.GetObjectAttributesResponse {
234+ resp := s3response.GetObjectAttributesResponse {
234235 ETag : & meta .QuotedETag ,
235236 ObjectSize : & meta .Size ,
236237 StorageClass : types .StorageClassStandard ,
237238 Checksum : & checksum ,
238239 VersionId : & meta .VersionID ,
239240 LastModified : & meta .LastModified ,
240- }, nil
241+ }
242+ if objectPartsRequested (input .ObjectAttributes ) {
243+ objectParts , err := b .getObjectAttributeParts (ctx , meta .MultipartUploadID , input )
244+ if err != nil {
245+ return s3response.GetObjectAttributesResponse {}, err
246+ }
247+ resp .ObjectParts = objectParts
248+ }
249+ return resp , nil
241250}
242251
243252func (b * SynapseBackend ) ListObjects (ctx context.Context , input * s3.ListObjectsInput ) (s3response.ListObjectsResult , error ) {
@@ -801,12 +810,13 @@ func derefStr(s *string) string {
801810}
802811
803812type objectMetadataResult struct {
804- Size int64
805- QuotedETag string
806- Checksum string
807- ContentType string
808- VersionID string
809- LastModified time.Time
813+ Size int64
814+ QuotedETag string
815+ Checksum string
816+ ContentType string
817+ VersionID string
818+ MultipartUploadID * string
819+ LastModified time.Time
810820}
811821
812822func (b * SynapseBackend ) objectMetadata (ctx context.Context , bucketID int64 , key , versionID string ) (objectMetadataResult , error ) {
@@ -816,15 +826,89 @@ func (b *SynapseBackend) objectMetadata(ctx context.Context, bucketID int64, key
816826 }
817827 etag := fmt .Sprintf (`"%s"` , version .ETag )
818828 return objectMetadataResult {
819- Size : version .Size ,
820- QuotedETag : etag ,
821- Checksum : version .Checksum ,
822- ContentType : version .ContentType ,
823- VersionID : version .VersionID ,
824- LastModified : version .CreatedAt ,
829+ Size : version .Size ,
830+ QuotedETag : etag ,
831+ Checksum : version .Checksum ,
832+ ContentType : version .ContentType ,
833+ VersionID : version .VersionID ,
834+ MultipartUploadID : version .MultipartUploadID ,
835+ LastModified : version .CreatedAt ,
825836 }, nil
826837}
827838
839+ func objectPartsRequested (attrs []types.ObjectAttributes ) bool {
840+ if len (attrs ) == 0 {
841+ return true
842+ }
843+ for _ , attr := range attrs {
844+ if attr == types .ObjectAttributesObjectParts {
845+ return true
846+ }
847+ }
848+ return false
849+ }
850+
851+ func (b * SynapseBackend ) getObjectAttributeParts (ctx context.Context , uploadID * string , input * s3.GetObjectAttributesInput ) (* s3response.ObjectParts , error ) {
852+ maxParts := 1000
853+ if input .MaxParts != nil {
854+ maxParts = int (* input .MaxParts )
855+ }
856+
857+ partMarker := 0
858+ if input .PartNumberMarker != nil {
859+ if v := * input .PartNumberMarker ; v != "" {
860+ parsed , err := strconv .Atoi (v )
861+ if err != nil {
862+ return nil , s3err .GetInvalidMaxLimiterErr ("part-number-marker" )
863+ }
864+ if parsed < 0 {
865+ return nil , s3err .GetNegativeMaxLimiterErr ("part-number-marker" )
866+ }
867+ partMarker = parsed
868+ }
869+ }
870+
871+ result := & s3response.ObjectParts {
872+ Parts : []types.ObjectPart {},
873+ MaxParts : maxParts ,
874+ PartNumberMarker : partMarker ,
875+ }
876+ if uploadID == nil || * uploadID == "" {
877+ return result , nil
878+ }
879+
880+ parts , err := b .repos .Multiparts .GetParts (ctx , * uploadID , partMarker , maxParts + 1 )
881+ if err != nil {
882+ return nil , fmt .Errorf ("listing object attribute parts: %w" , err )
883+ }
884+ if len (parts ) > maxParts {
885+ if maxParts > 0 {
886+ parts = parts [:maxParts ]
887+ result .NextPartNumberMarker = parts [len (parts )- 1 ].PartNumber
888+ } else {
889+ parts = nil
890+ }
891+ result .IsTruncated = true
892+ }
893+
894+ result .Parts = make ([]types.ObjectPart , 0 , len (parts ))
895+ for _ , p := range parts {
896+ partNumber := int32 (p .PartNumber )
897+ size := p .Size
898+ part := types.ObjectPart {
899+ PartNumber : & partNumber ,
900+ Size : & size ,
901+ }
902+ if p .Checksum != nil && * p .Checksum != "" {
903+ checksum := * p .Checksum
904+ part .ChecksumSHA256 = & checksum
905+ }
906+ result .Parts = append (result .Parts , part )
907+ }
908+
909+ return result , nil
910+ }
911+
828912func (b * SynapseBackend ) versionForRead (ctx context.Context , bucketID int64 , key , versionID string ) (* model.ObjectVersion , error ) {
829913 if versionID != "" {
830914 version , err := b .repos .Objects .GetVersionByBucketKeyAndID (ctx , bucketID , key , versionID )
0 commit comments