| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2020-01-28 06:12:34 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	"context" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2020-01-28 06:12:34 +08:00
										 |  |  | 	objectlock "github.com/minio/minio/pkg/bucket/object/lock" | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // enforceRetentionBypassForDelete enforces whether an existing object under governance can be deleted
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // with governance bypass headers set in the request.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // Objects under site wide WORM can never be overwritten.
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
 | 
					
						
							|  |  |  | // governance bypass headers are set and user has governance bypass permissions.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // Objects in "Compliance" mode can be overwritten only if retention date is past.
 | 
					
						
							|  |  |  | func enforceRetentionBypassForDelete(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, govBypassPerm APIErrorCode) (oi ObjectInfo, s3Err APIErrorCode) { | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							|  |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	var err error | 
					
						
							|  |  |  | 	var opts ObjectOptions | 
					
						
							|  |  |  | 	opts, err = getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return oi, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oi, err = getObjectInfoFn(ctx, bucket, object, opts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// ignore case where object no longer exists
 | 
					
						
							|  |  |  | 		if toAPIError(ctx, err).Code == "NoSuchKey" { | 
					
						
							| 
									
										
										
										
											2019-11-22 05:18:32 +08:00
										 |  |  | 			oi.UserDefined = map[string]string{} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 			return oi, ErrNone | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return oi, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined) | 
					
						
							|  |  |  | 	lhold := objectlock.GetObjectLegalHoldMeta(oi.UserDefined) | 
					
						
							|  |  |  | 	if lhold.Status == objectlock.ON { | 
					
						
							|  |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	// Here bucket does not support object lock
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if ret.Mode == objectlock.Invalid { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		return oi, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if ret.Mode != objectlock.Compliance && ret.Mode != objectlock.Governance { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		return oi, ErrUnknownWORMModeDirective | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if ret.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 		return oi, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if objectlock.IsObjectLockGovernanceBypassSet(r.Header) && ret.Mode == objectlock.Governance && govBypassPerm == ErrNone { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		return oi, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return oi, ErrObjectLocked | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // enforceRetentionBypassForPut enforces whether an existing object under governance can be overwritten
 | 
					
						
							|  |  |  | // with governance bypass headers set in the request.
 | 
					
						
							|  |  |  | // Objects under site wide WORM cannot be overwritten.
 | 
					
						
							|  |  |  | // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
 | 
					
						
							|  |  |  | // governance bypass headers are set and user has governance bypass permissions.
 | 
					
						
							|  |  |  | // Objects in compliance mode can be overwritten only if retention date is being extended. No mode change is permitted.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | func enforceRetentionBypassForPut(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, govBypassPerm APIErrorCode, objRetention *objectlock.ObjectRetention) (oi ObjectInfo, s3Err APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if globalWORMEnabled { | 
					
						
							|  |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	var opts ObjectOptions | 
					
						
							|  |  |  | 	opts, err = getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return oi, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oi, err = getObjectInfoFn(ctx, bucket, object, opts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// ignore case where object no longer exists
 | 
					
						
							|  |  |  | 		if toAPIError(ctx, err).Code == "NoSuchKey" { | 
					
						
							|  |  |  | 			oi.UserDefined = map[string]string{} | 
					
						
							|  |  |  | 			return oi, ErrNone | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return oi, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	// no retention metadata on object
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if ret.Mode == objectlock.Invalid { | 
					
						
							| 
									
										
										
										
											2020-01-14 09:29:31 +08:00
										 |  |  | 		if _, isWORMBucket := globalBucketObjectLockConfig.Get(bucket); !isWORMBucket { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 			return oi, ErrInvalidBucketObjectLockConfiguration | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return oi, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if ret.Mode == objectlock.Compliance { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		// Compliance retention mode cannot be changed and retention period cannot be shortened as per
 | 
					
						
							|  |  |  | 		// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		if objRetention.Mode != objectlock.Compliance || objRetention.RetainUntilDate.Before(ret.RetainUntilDate.Time) { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 			return oi, ErrObjectLocked | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if objRetention.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 			return oi, ErrInvalidRetentionDate | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return oi, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if ret.Mode == objectlock.Governance { | 
					
						
							|  |  |  | 		if !objectlock.IsObjectLockGovernanceBypassSet(r.Header) { | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 			if objRetention.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 				return oi, ErrInvalidRetentionDate | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 			if objRetention.RetainUntilDate.Before((ret.RetainUntilDate.Time)) { | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 				return oi, ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return oi, ErrNone | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		return oi, govBypassPerm | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return oi, ErrNone | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | // checkPutObjectLockAllowed enforces object retention policy and legal hold policy
 | 
					
						
							|  |  |  | // for requests with WORM headers
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // See https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-managing.html for the spec.
 | 
					
						
							|  |  |  | // For non-existing objects with object retention headers set, this method returns ErrNone if bucket has
 | 
					
						
							|  |  |  | // locking enabled and user has requisite permissions (s3:PutObjectRetention)
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // If object exists on object store and site wide WORM enabled - this method
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // returns an error. For objects in "Governance" mode, overwrite is allowed if the retention date has expired.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // For objects in "Compliance" mode, retention date cannot be shortened, and mode cannot be altered.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | // For objects with legal hold header set, the s3:PutObjectLegalHold permission is expected to be set
 | 
					
						
							|  |  |  | // Both legal hold and retention can be applied independently on an object
 | 
					
						
							|  |  |  | func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, retentionPermErr, legalHoldPermErr APIErrorCode) (objectlock.Mode, objectlock.RetentionDate, objectlock.ObjectLegalHold, APIErrorCode) { | 
					
						
							|  |  |  | 	var mode objectlock.Mode | 
					
						
							|  |  |  | 	var retainDate objectlock.RetentionDate | 
					
						
							|  |  |  | 	var legalHold objectlock.ObjectLegalHold | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 09:29:31 +08:00
										 |  |  | 	retention, isWORMBucket := globalBucketObjectLockConfig.Get(bucket) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	retentionRequested := objectlock.IsObjectLockRetentionRequested(r.Header) | 
					
						
							|  |  |  | 	legalHoldRequested := objectlock.IsObjectLockLegalHoldRequested(r.Header) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var objExists bool | 
					
						
							|  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if objInfo, err := getObjectInfoFn(ctx, bucket, object, opts); err == nil { | 
					
						
							|  |  |  | 		objExists = true | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		r := objectlock.GetObjectRetentionMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 		if globalWORMEnabled || r.Mode == objectlock.Compliance { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		mode = r.Mode | 
					
						
							|  |  |  | 		retainDate = r.RetainUntilDate | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		legalHold = objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 		// Disallow overwriting an object on legal hold
 | 
					
						
							|  |  |  | 		if legalHold.Status == "ON" { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if legalHoldRequested { | 
					
						
							|  |  |  | 		if !isWORMBucket { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var lerr error | 
					
						
							|  |  |  | 		if legalHold, lerr = objectlock.ParseObjectLockLegalHoldHeaders(r.Header); lerr != nil { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if retentionRequested { | 
					
						
							|  |  |  | 		if !isWORMBucket { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		legalHold, err := objectlock.ParseObjectLockLegalHoldHeaders(r.Header) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		rMode, rDate, err := objectlock.ParseObjectLockRetentionHeaders(r.Header) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		// AWS S3 just creates a new version of object when an object is being overwritten.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if objExists && retainDate.After(t) { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		if rMode == objectlock.Invalid { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, objectlock.ErrObjectLockInvalidHeaders) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if retentionPermErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, retentionPermErr | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		return rMode, rDate, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if !retentionRequested && isWORMBucket { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		if retention.IsEmpty() && (mode == objectlock.Compliance || mode == objectlock.Governance) { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		if retentionPermErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, retentionPermErr | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		// AWS S3 just creates a new version of object when an object is being overwritten.
 | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		if objExists && retainDate.After(t) { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		if !legalHoldRequested { | 
					
						
							|  |  |  | 			// inherit retention from bucket configuration
 | 
					
						
							|  |  |  | 			return retention.Mode, objectlock.RetentionDate{Time: t.Add(retention.Validity)}, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2020-01-07 02:15:45 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	return mode, retainDate, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | } |