@ -1017,27 +1017,29 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
srcInfo . UserDefined [ xhttp . AmzObjectTagging ] = tags
srcInfo . UserDefined [ xhttp . AmzObjectTagging ] = tags
}
}
srcInfo . UserDefined = objectlock . FilterObjectLockMetadata ( srcInfo . UserDefined , true , true )
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , dstBucket , dstObject , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , dstBucket , dstObject , r , iampolicy . PutObjectLegalHoldAction )
getObjectInfo := objectAPI . GetObjectInfo
getObjectInfo := objectAPI . GetObjectInfo
if api . CacheAPI ( ) != nil {
if api . CacheAPI ( ) != nil {
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
}
srcInfo . UserDefined = objectlock . FilterObjectLockMetadata ( srcInfo . UserDefined , true , true )
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , dstBucket , dstObject , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , dstBucket , dstObject , r , iampolicy . PutObjectLegalHoldAction )
// apply default bucket configuration/governance headers for dest side.
// apply default bucket configuration/governance headers for dest side.
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , dstBucket , dstObject , getObjectInfo , retPerms , holdPerms )
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , dstBucket , dstObject , getObjectInfo , retPerms , holdPerms )
if s3Err == ErrNone && retentionMode != "" {
if s3Err == ErrNone && retentionMode . Valid ( ) {
srcInfo . UserDefined [ xhttp . AmzObjectLockMode ] = string ( retentionMode )
srcInfo . UserDefined [ xhttp . AmzObjectLockMode ] = string ( retentionMode )
srcInfo . UserDefined [ xhttp . AmzObjectLockRetainUntilDate ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
srcInfo . UserDefined [ xhttp . AmzObjectLockRetainUntilDate ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
}
}
if s3Err == ErrNone && legalHold . Status != "" {
if s3Err == ErrNone && legalHold . Status . Valid ( ) {
srcInfo . UserDefined [ xhttp . AmzObjectLockLegalHold ] = string ( legalHold . Status )
srcInfo . UserDefined [ xhttp . AmzObjectLockLegalHold ] = string ( legalHold . Status )
}
}
if s3Err != ErrNone {
if s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
// Store the preserved compression metadata.
// Store the preserved compression metadata.
for k , v := range compressMetadata {
for k , v := range compressMetadata {
srcInfo . UserDefined [ k ] = v
srcInfo . UserDefined [ k ] = v
@ -1327,20 +1329,25 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
writeErrorResponseHeadersOnly ( w , toAPIError ( ctx , err ) )
writeErrorResponseHeadersOnly ( w , toAPIError ( ctx , err ) )
return
return
}
}
getObjectInfo := objectAPI . GetObjectInfo
if api . CacheAPI ( ) != nil {
if api . CacheAPI ( ) != nil {
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
putObject = api . CacheAPI ( ) . PutObject
putObject = api . CacheAPI ( ) . PutObject
}
}
retPerms := isPutActionAllowed ( rAuthType , bucket , object , r , iampolicy . PutObjectRetentionAction )
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
getObjectInfo := objectAPI . GetObjectInfo
if api . CacheAPI ( ) != nil {
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , getObjectInfo , retPerms , holdPerms )
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , getObjectInfo , retPerms , holdPerms )
if s3Err == ErrNone && retentionMode != "" {
if s3Err == ErrNone && retentionMode . Valid ( ) {
metadata [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( retentionMode )
metadata [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( retentionMode )
metadata [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
metadata [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
}
}
if s3Err == ErrNone && legalHold . Status != "" {
if s3Err == ErrNone && legalHold . Status . Valid ( ) {
metadata [ strings . ToLower ( xhttp . AmzObjectLockLegalHold ) ] = string ( legalHold . Status )
metadata [ strings . ToLower ( xhttp . AmzObjectLockLegalHold ) ] = string ( legalHold . Status )
}
}
if s3Err != ErrNone {
if s3Err != ErrNone {
@ -1471,6 +1478,14 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
return
return
}
}
// Deny if WORM is enabled
if globalWORMEnabled {
if _ , err := objectAPI . GetObjectInfo ( ctx , bucket , object , opts ) ; err == nil {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrMethodNotAllowed ) , r . URL , guessIsBrowserReq ( r ) )
return
}
}
// Validate storage class metadata if present
// Validate storage class metadata if present
if sc := r . Header . Get ( xhttp . AmzStorageClass ) ; sc != "" {
if sc := r . Header . Get ( xhttp . AmzStorageClass ) ; sc != "" {
if ! storageclass . IsValid ( sc ) {
if ! storageclass . IsValid ( sc ) {
@ -1499,21 +1514,28 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectRetentionAction )
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , objectAPI . GetObjectInfo , retPerms , holdPerms )
getObjectInfo := objectAPI . GetObjectInfo
if s3Err == ErrNone && retentionMode != "" {
if api . CacheAPI ( ) != nil {
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
retentionMode , retentionDate , legalHold , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , getObjectInfo , retPerms , holdPerms )
if s3Err == ErrNone && retentionMode . Valid ( ) {
metadata [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( retentionMode )
metadata [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( retentionMode )
metadata [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
metadata [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = retentionDate . UTC ( ) . Format ( time . RFC3339 )
}
}
if s3Err == ErrNone && legalHold . Status != "" {
if s3Err == ErrNone && legalHold . Status . Valid ( ) {
metadata [ strings . ToLower ( xhttp . AmzObjectLockLegalHold ) ] = string ( legalHold . Status )
metadata [ strings . ToLower ( xhttp . AmzObjectLockLegalHold ) ] = string ( legalHold . Status )
}
}
if s3Err != ErrNone {
if s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
// We need to preserve the encryption headers set in EncryptRequest,
// We need to preserve the encryption headers set in EncryptRequest,
// so we do not want to override them, copy them instead.
// so we do not want to override them, copy them instead.
for k , v := range encMetadata {
for k , v := range encMetadata {
@ -2339,22 +2361,6 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
return
return
}
}
// Reject retention or governance headers if set, CompleteMultipartUpload spec
// does not use these headers, and should not be passed down to checkPutObjectLockAllowed
if objectlock . IsObjectLockRequested ( r . Header ) || objectlock . IsObjectLockGovernanceBypassSet ( r . Header ) {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidRequest ) , r . URL , guessIsBrowserReq ( r ) )
return
}
// Enforce object lock governance in case a competing upload finalized first.
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
if _ , _ , _ , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , objectAPI . GetObjectInfo , retPerms , holdPerms ) ; s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
}
// Get upload id.
// Get upload id.
uploadID , _ , _ , _ , s3Error := getObjectResources ( r . URL . Query ( ) )
uploadID , _ , _ , _ , s3Error := getObjectResources ( r . URL . Query ( ) )
if s3Error != ErrNone {
if s3Error != ErrNone {
@ -2375,6 +2381,36 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidPartOrder ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidPartOrder ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
// Reject retention or governance headers if set, CompleteMultipartUpload spec
// does not use these headers, and should not be passed down to checkPutObjectLockAllowed
if objectlock . IsObjectLockRequested ( r . Header ) || objectlock . IsObjectLockGovernanceBypassSet ( r . Header ) {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidRequest ) , r . URL , guessIsBrowserReq ( r ) )
return
}
// Deny if global WORM is enabled
if globalWORMEnabled {
opts , err := getOpts ( ctx , r , bucket , object )
if err != nil {
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
}
if _ , err := objectAPI . GetObjectInfo ( ctx , bucket , object , opts ) ; err == nil {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrMethodNotAllowed ) , r . URL , guessIsBrowserReq ( r ) )
return
}
}
// Enforce object lock governance in case a competing upload finalized first.
retPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectRetentionAction )
holdPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , iampolicy . PutObjectLegalHoldAction )
if _ , _ , _ , s3Err := checkPutObjectLockAllowed ( ctx , r , bucket , object , objectAPI . GetObjectInfo , retPerms , holdPerms ) ; s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
}
var objectEncryptionKey [ ] byte
var objectEncryptionKey [ ] byte
var opts ObjectOptions
var opts ObjectOptions
var isEncrypted , ssec bool
var isEncrypted , ssec bool
@ -2546,12 +2582,6 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
}
govBypassPerms := checkRequestAuthType ( ctx , r , policy . BypassGovernanceRetentionAction , bucket , object )
if _ , err := enforceRetentionBypassForDelete ( ctx , r , bucket , object , getObjectInfo , govBypassPerms ) ; err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( err ) , r . URL , guessIsBrowserReq ( r ) )
return
}
if globalDNSConfig != nil {
if globalDNSConfig != nil {
_ , err := globalDNSConfig . Get ( bucket )
_ , err := globalDNSConfig . Get ( bucket )
if err != nil {
if err != nil {
@ -2560,16 +2590,41 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
}
}
}
}
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
// Deny if global WORM is enabled
if err := deleteObject ( ctx , objectAPI , api . CacheAPI ( ) , bucket , object , r ) ; err != nil {
if globalWORMEnabled {
switch err . ( type ) {
opts , err := getOpts ( ctx , r , bucket , object )
case BucketNotFound :
if err != nil {
// When bucket doesn't exist specially handle it.
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
// Ignore delete object errors while replying to client, since we are suppposed to reply only 204.
if _ , err := objectAPI . GetObjectInfo ( ctx , bucket , object , opts ) ; err == nil {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrMethodNotAllowed ) , r . URL , guessIsBrowserReq ( r ) )
return
}
}
}
apiErr := ErrNone
if _ , ok := globalBucketObjectLockConfig . Get ( bucket ) ; ok {
apiErr = enforceRetentionBypassForDelete ( ctx , r , bucket , object , getObjectInfo )
if apiErr != ErrNone && apiErr != ErrNoSuchKey {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( apiErr ) , r . URL , guessIsBrowserReq ( r ) )
return
}
}
if apiErr == ErrNone {
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
if err := deleteObject ( ctx , objectAPI , api . CacheAPI ( ) , bucket , object , r ) ; err != nil {
switch err . ( type ) {
case BucketNotFound :
// When bucket doesn't exist specially handle it.
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
}
// Ignore delete object errors while replying to client, since we are suppposed to reply only 204.
}
}
writeSuccessNoContent ( w )
writeSuccessNoContent ( w )
}
}
@ -2695,6 +2750,11 @@ func (api objectAPIHandlers) GetObjectLegalHoldHandler(w http.ResponseWriter, r
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
}
if _ , ok := globalBucketObjectLockConfig . Get ( bucket ) ; ! ok {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidBucketObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
return
}
opts , err := getOpts ( ctx , r , bucket , object )
opts , err := getOpts ( ctx , r , bucket , object )
if err != nil {
if err != nil {
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
@ -2707,15 +2767,12 @@ func (api objectAPIHandlers) GetObjectLegalHoldHandler(w http.ResponseWriter, r
return
return
}
}
if _ , ok := globalBucketObjectLockConfig . Get ( bucket ) ; ! ok {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidBucketObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
return
}
legalHold := objectlock . GetObjectLegalHoldMeta ( objInfo . UserDefined )
legalHold := objectlock . GetObjectLegalHoldMeta ( objInfo . UserDefined )
if legalHold . IsEmpty ( ) {
if legalHold . IsEmpty ( ) {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrNoSuchObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrNoSuchObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
writeSuccessResponseXML ( w , encodeResponse ( legalHold ) )
writeSuccessResponseXML ( w , encodeResponse ( legalHold ) )
// Notify object legal hold accessed via a GET request.
// Notify object legal hold accessed via a GET request.
sendEvent ( eventArgs {
sendEvent ( eventArgs {
@ -2753,8 +2810,9 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrServerNotInitialized ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrServerNotInitialized ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
// Check permissions to perform this governance operation
if s3Err := checkRequestAuthType ( ctx , r , policy . PutObjectRetentionAction , bucket , object ) ; s3Err != ErrNone {
cred , owner , claims , s3Err := validateSignature ( getRequestAuthType ( r ) , r )
if s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
@ -2763,11 +2821,17 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
if ! hasContentMD5 ( r . Header ) {
if ! hasContentMD5 ( r . Header ) {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrMissingContentMD5 ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrMissingContentMD5 ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
if globalWORMEnabled {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrObjectLocked ) , r . URL , guessIsBrowserReq ( r ) )
return
}
if _ , ok := globalBucketObjectLockConfig . Get ( bucket ) ; ! ok {
if _ , ok := globalBucketObjectLockConfig . Get ( bucket ) ; ! ok {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidBucketObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( ErrInvalidBucketObjectLockConfiguration ) , r . URL , guessIsBrowserReq ( r ) )
return
return
@ -2780,13 +2844,13 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
writeErrorResponse ( ctx , w , apiErr , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , apiErr , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
getObjectInfo := objectAPI . GetObjectInfo
getObjectInfo := objectAPI . GetObjectInfo
if api . CacheAPI ( ) != nil {
if api . CacheAPI ( ) != nil {
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
getObjectInfo = api . CacheAPI ( ) . GetObjectInfo
}
}
govBypassPerms := isPutActionAllowed ( getRequestAuthType ( r ) , bucket , object , r , policy . BypassGovernanceRetentionAction )
objInfo , s3Err := enforceRetentionBypassForPut ( ctx , r , bucket , object , getObjectInfo , objRetention , cred , owner , claims )
objInfo , s3Err := enforceRetentionBypassForPut ( ctx , r , bucket , object , getObjectInfo , govBypassPerms , objRetention )
if s3Err != ErrNone {
if s3Err != ErrNone {
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , errorCodes . ToAPIErr ( s3Err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
@ -2794,7 +2858,7 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
objInfo . UserDefined [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( objRetention . Mode )
objInfo . UserDefined [ strings . ToLower ( xhttp . AmzObjectLockMode ) ] = string ( objRetention . Mode )
objInfo . UserDefined [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = objRetention . RetainUntilDate . UTC ( ) . Format ( time . RFC3339 )
objInfo . UserDefined [ strings . ToLower ( xhttp . AmzObjectLockRetainUntilDate ) ] = objRetention . RetainUntilDate . UTC ( ) . Format ( time . RFC3339 )
objInfo . metadataOnly = true
objInfo . metadataOnly = true // Perform only metadata updates.
if _ , err = objectAPI . CopyObject ( ctx , bucket , object , bucket , object , objInfo , ObjectOptions { } , ObjectOptions { } ) ; err != nil {
if _ , err = objectAPI . CopyObject ( ctx , bucket , object , bucket , object , objInfo , ObjectOptions { } , ObjectOptions { } ) ; err != nil {
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
return