| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | // Copyright (c) 2015-2023 MinIO, Inc.
 | 
					
						
							| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  |  | //
 | 
					
						
							|  |  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 	"archive/tar" | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2022-08-24 08:04:11 +08:00
										 |  |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2021-06-16 09:43:14 +08:00
										 |  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	"net/http/httptest" | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 	"net/textproto" | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2023-06-13 04:22:07 +08:00
										 |  |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2015-05-04 14:16:10 +08:00
										 |  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2019-02-06 12:58:09 +08:00
										 |  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	"unicode" | 
					
						
							| 
									
										
										
										
											2019-02-06 12:58:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2022-02-15 01:19:01 +08:00
										 |  |  |  | 	"github.com/klauspost/compress/gzhttp" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  |  | 	miniogo "github.com/minio/minio-go/v7" | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 	"github.com/minio/minio-go/v7/pkg/credentials" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  |  | 	"github.com/minio/minio-go/v7/pkg/encrypt" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio-go/v7/pkg/tags" | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/amztime" | 
					
						
							| 
									
										
										
										
											2023-02-07 01:27:29 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/auth" | 
					
						
							| 
									
										
										
										
											2021-09-22 00:02:15 +08:00
										 |  |  |  | 	sse "github.com/minio/minio/internal/bucket/encryption" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/bucket/lifecycle" | 
					
						
							|  |  |  |  | 	objectlock "github.com/minio/minio/internal/bucket/object/lock" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/bucket/replication" | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/config/cache" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/config/dns" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/config/storageclass" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/crypto" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/etag" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/event" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/handlers" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/hash" | 
					
						
							|  |  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  |  | 	xioutil "github.com/minio/minio/internal/ioutil" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							|  |  |  |  | 	"github.com/minio/minio/internal/s3select" | 
					
						
							| 
									
										
										
										
											2023-01-23 19:12:47 +08:00
										 |  |  |  | 	"github.com/minio/mux" | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	"github.com/minio/pkg/v2/policy" | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	"github.com/valyala/bytebufferpool" | 
					
						
							| 
									
										
										
										
											2015-05-10 10:39:00 +08:00
										 |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 02:04:04 +08:00
										 |  |  |  | // supportedHeadGetReqParams - supported request parameters for GET and HEAD presigned request.
 | 
					
						
							|  |  |  |  | var supportedHeadGetReqParams = map[string]string{ | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  |  | 	"response-expires":             xhttp.Expires, | 
					
						
							|  |  |  |  | 	"response-content-type":        xhttp.ContentType, | 
					
						
							|  |  |  |  | 	"response-cache-control":       xhttp.CacheControl, | 
					
						
							|  |  |  |  | 	"response-content-encoding":    xhttp.ContentEncoding, | 
					
						
							|  |  |  |  | 	"response-content-language":    xhttp.ContentLanguage, | 
					
						
							|  |  |  |  | 	"response-content-disposition": xhttp.ContentDisposition, | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | const ( | 
					
						
							|  |  |  |  | 	compressionAlgorithmV1 = "golang/snappy/LZ77" | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  |  | 	compressionAlgorithmV2 = "klauspost/compress/s2" | 
					
						
							| 
									
										
										
										
											2019-12-13 02:01:15 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// When an upload exceeds encryptBufferThreshold ...
 | 
					
						
							|  |  |  |  | 	encryptBufferThreshold = 1 << 20 | 
					
						
							|  |  |  |  | 	// add an input buffer of this size.
 | 
					
						
							|  |  |  |  | 	encryptBufferSize = 1 << 20 | 
					
						
							| 
									
										
										
										
											2022-07-12 22:42:04 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// minCompressibleSize is the minimum size at which we enable compression.
 | 
					
						
							|  |  |  |  | 	minCompressibleSize = 4096 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 02:04:04 +08:00
										 |  |  |  | // setHeadGetRespHeaders - set any requested parameters as response headers.
 | 
					
						
							|  |  |  |  | func setHeadGetRespHeaders(w http.ResponseWriter, reqParams url.Values) { | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  |  | 	for k, v := range reqParams { | 
					
						
							| 
									
										
										
										
											2020-04-21 13:01:59 +08:00
										 |  |  |  | 		if header, ok := supportedHeadGetReqParams[strings.ToLower(k)]; ok { | 
					
						
							| 
									
										
										
										
											2023-06-14 02:38:46 +08:00
										 |  |  |  | 			w.Header()[header] = []string{strings.Join(v, ",")} | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | // SelectObjectContentHandler - GET Object?select
 | 
					
						
							|  |  |  |  | // ----------
 | 
					
						
							|  |  |  |  | // This implementation of the GET operation retrieves object content based
 | 
					
						
							|  |  |  |  | // on an SQL expression. In the request, along with the sql expression, you must
 | 
					
						
							|  |  |  |  | // also specify a data serialization format (JSON, CSV) of the object.
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "SelectObject") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 	// Fetch object stat info.
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-16 04:57:15 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if crypto.S3.IsRequested(r.Header) || crypto.S3KMS.IsRequested(r.Header) { // If SSE-S3 or SSE-KMS present -> AWS fails with undefined error
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 06:04:53 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-16 04:57:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-09 08:53:04 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	// Check for auth type to return S3 compatible error.
 | 
					
						
							|  |  |  |  | 	// type to return the correct error (NoSuchKey vs AccessDenied)
 | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectAction, bucket, object); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		if getRequestAuthType(r) == authTypeAnonymous { | 
					
						
							|  |  |  |  | 			// As per "Permission" section in
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
 | 
					
						
							|  |  |  |  | 			// If the object you request does not exist,
 | 
					
						
							|  |  |  |  | 			// the error Amazon S3 returns depends on
 | 
					
						
							|  |  |  |  | 			// whether you also have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			// permission.
 | 
					
						
							|  |  |  |  | 			// * If you have the s3:ListBucket permission
 | 
					
						
							|  |  |  |  | 			//   on the bucket, Amazon S3 will return an
 | 
					
						
							|  |  |  |  | 			//   HTTP status code 404 ("no such key")
 | 
					
						
							|  |  |  |  | 			//   error.
 | 
					
						
							|  |  |  |  | 			// * if you don’t have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			//   permission, Amazon S3 will return an HTTP
 | 
					
						
							|  |  |  |  | 			//   status code 403 ("access denied") error.`
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 			if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{ | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 				Action:          policy.ListBucketAction, | 
					
						
							|  |  |  |  | 				BucketName:      bucket, | 
					
						
							| 
									
										
										
										
											2023-02-07 01:27:29 +08:00
										 |  |  |  | 				ConditionValues: getConditionValues(r, "", auth.AnonymousCredentials), | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 				IsOwner:         false, | 
					
						
							|  |  |  |  | 			}) { | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 				_, err = getObjectInfo(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  |  | 				if toAPIError(ctx, err).Code == "NoSuchKey" { | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 					s3Error = ErrNoSuchKey | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Get request range.
 | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  |  | 	rangeHeader := r.Header.Get(xhttp.Range) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	if rangeHeader != "" { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrUnsupportedRangeHeader), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 	if r.ContentLength <= 0 { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrEmptyRequestBody), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 	// Take read lock on object, here so subsequent lower-level
 | 
					
						
							|  |  |  |  | 	// calls do not need to.
 | 
					
						
							|  |  |  |  | 	lock := objectAPI.NewNSLock(bucket, object) | 
					
						
							|  |  |  |  | 	lkctx, err := lock.GetRLock(ctx, globalOperationTimeout) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	ctx = lkctx.Context() | 
					
						
							| 
									
										
										
										
											2022-12-24 11:49:07 +08:00
										 |  |  |  | 	defer lock.RUnlock(lkctx) | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	getObjectNInfo := objectAPI.GetObjectNInfo | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 	gopts := opts | 
					
						
							|  |  |  |  | 	gopts.NoLock = true // We already have a lock, we can live with it.
 | 
					
						
							|  |  |  |  | 	objInfo, err := getObjectInfo(ctx, bucket, object, gopts) | 
					
						
							| 
									
										
										
										
											2019-01-29 09:59:48 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 		// Versioning enabled quite possibly object is deleted might be delete-marker
 | 
					
						
							|  |  |  |  | 		// if present set the headers, no idea why AWS S3 sets these headers.
 | 
					
						
							|  |  |  |  | 		if objInfo.VersionID != "" && objInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID} | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzDeleteMarker] = []string{strconv.FormatBool(objInfo.DeleteMarker)} | 
					
						
							| 
									
										
										
										
											2020-07-03 07:17:27 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-29 09:59:48 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-25 04:51:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 17:01:08 +08:00
										 |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	getRetPerms := checkRequestAuthType(ctx, r, policy.GetObjectRetentionAction, bucket, object) | 
					
						
							|  |  |  |  | 	legalHoldPerms := checkRequestAuthType(ctx, r, policy.GetObjectLegalHoldAction, bucket, object) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	if _, err = DecryptObjectInfo(&objInfo, r); err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-05-19 17:01:08 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 	actualSize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectRSC := s3select.NewObjectReadSeekCloser( | 
					
						
							|  |  |  |  | 		func(offset int64) (io.ReadCloser, error) { | 
					
						
							|  |  |  |  | 			rs := &HTTPRangeSpec{ | 
					
						
							|  |  |  |  | 				IsSuffixLength: false, | 
					
						
							|  |  |  |  | 				Start:          offset, | 
					
						
							|  |  |  |  | 				End:            -1, | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-04-18 03:16:37 +08:00
										 |  |  |  | 			opts.NoLock = true | 
					
						
							|  |  |  |  | 			return getObjectNInfo(ctx, bucket, object, rs, r.Header, opts) | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 		}, | 
					
						
							|  |  |  |  | 		actualSize, | 
					
						
							|  |  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-02-17 17:44:40 +08:00
										 |  |  |  | 	defer objectRSC.Close() | 
					
						
							| 
									
										
										
										
											2020-04-25 04:51:48 +08:00
										 |  |  |  | 	s3Select, err := s3select.NewS3Select(r.Body) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		if serr, ok := err.(s3select.SelectError); ok { | 
					
						
							|  |  |  |  | 			encodedErrorResponse := encodeResponse(APIErrorResponse{ | 
					
						
							|  |  |  |  | 				Code:       serr.ErrorCode(), | 
					
						
							|  |  |  |  | 				Message:    serr.ErrorMessage(), | 
					
						
							|  |  |  |  | 				BucketName: bucket, | 
					
						
							|  |  |  |  | 				Key:        object, | 
					
						
							|  |  |  |  | 				Resource:   r.URL.Path, | 
					
						
							|  |  |  |  | 				RequestID:  w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2023-10-18 23:06:57 +08:00
										 |  |  |  | 				HostID:     globalDeploymentID(), | 
					
						
							| 
									
										
										
										
											2020-04-25 04:51:48 +08:00
										 |  |  |  | 			}) | 
					
						
							|  |  |  |  | 			writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML) | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-04-25 04:51:48 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-20 09:51:46 +08:00
										 |  |  |  | 	defer s3Select.Close() | 
					
						
							| 
									
										
										
										
											2020-04-25 04:51:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 	if err = s3Select.Open(objectRSC); err != nil { | 
					
						
							| 
									
										
										
										
											2019-01-09 08:53:04 +08:00
										 |  |  |  | 		if serr, ok := err.(s3select.SelectError); ok { | 
					
						
							| 
									
										
										
										
											2019-02-13 15:48:11 +08:00
										 |  |  |  | 			encodedErrorResponse := encodeResponse(APIErrorResponse{ | 
					
						
							|  |  |  |  | 				Code:       serr.ErrorCode(), | 
					
						
							|  |  |  |  | 				Message:    serr.ErrorMessage(), | 
					
						
							|  |  |  |  | 				BucketName: bucket, | 
					
						
							|  |  |  |  | 				Key:        object, | 
					
						
							|  |  |  |  | 				Resource:   r.URL.Path, | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  |  | 				RequestID:  w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2023-10-18 23:06:57 +08:00
										 |  |  |  | 				HostID:     globalDeploymentID(), | 
					
						
							| 
									
										
										
										
											2019-02-13 15:48:11 +08:00
										 |  |  |  | 			}) | 
					
						
							|  |  |  |  | 			writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML) | 
					
						
							| 
									
										
										
										
											2019-01-09 08:53:04 +08:00
										 |  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 17:01:08 +08:00
										 |  |  |  | 	// Set encryption response headers
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind { | 
					
						
							|  |  |  |  | 	case crypto.S3: | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) | 
					
						
							|  |  |  |  | 	case crypto.S3KMS: | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionKmsID, objInfo.KMSKeyID()) | 
					
						
							|  |  |  |  | 		if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok { | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	case crypto.SSEC: | 
					
						
							|  |  |  |  | 		// Validate the SSE-C Key set in the header.
 | 
					
						
							|  |  |  |  | 		if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2020-05-19 17:01:08 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) | 
					
						
							| 
									
										
										
										
											2020-05-19 17:01:08 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-09 08:53:04 +08:00
										 |  |  |  | 	s3Select.Evaluate(w) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  |  | 	// Notify object accessed via a GET request.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectAccessedGet, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							| 
									
										
										
										
											2019-03-26 02:45:42 +08:00
										 |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2018-08-15 18:30:19 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | func (api objectAPIHandlers) getObjectHandler(ctx context.Context, objectAPI ObjectLayer, bucket, object string, w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 	if crypto.S3.IsRequested(r.Header) || crypto.S3KMS.IsRequested(r.Header) { // If SSE-S3 or SSE-KMS present -> AWS fails with undefined error
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-01 04:10:12 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	// Check for auth type to return S3 compatible error.
 | 
					
						
							|  |  |  |  | 	// type to return the correct error (NoSuchKey vs AccessDenied)
 | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 	if s3Error := authenticateRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2018-05-02 14:43:27 +08:00
										 |  |  |  | 		if getRequestAuthType(r) == authTypeAnonymous { | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 			// As per "Permission" section in
 | 
					
						
							|  |  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
 | 
					
						
							|  |  |  |  | 			// If the object you request does not exist,
 | 
					
						
							|  |  |  |  | 			// the error Amazon S3 returns depends on
 | 
					
						
							|  |  |  |  | 			// whether you also have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			// permission.
 | 
					
						
							|  |  |  |  | 			// * If you have the s3:ListBucket permission
 | 
					
						
							|  |  |  |  | 			//   on the bucket, Amazon S3 will return an
 | 
					
						
							|  |  |  |  | 			//   HTTP status code 404 ("no such key")
 | 
					
						
							|  |  |  |  | 			//   error.
 | 
					
						
							|  |  |  |  | 			// * if you don’t have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			//   permission, Amazon S3 will return an HTTP
 | 
					
						
							|  |  |  |  | 			//   status code 403 ("access denied") error.`
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 			if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{ | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 				Action:          policy.ListBucketAction, | 
					
						
							|  |  |  |  | 				BucketName:      bucket, | 
					
						
							| 
									
										
										
										
											2023-02-07 01:27:29 +08:00
										 |  |  |  | 				ConditionValues: getConditionValues(r, "", auth.AnonymousCredentials), | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 				IsOwner:         false, | 
					
						
							|  |  |  |  | 			}) { | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 				getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 				_, err = getObjectInfo(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  |  | 				if toAPIError(ctx, err).Code == "NoSuchKey" { | 
					
						
							| 
									
										
										
										
											2018-05-02 14:43:27 +08:00
										 |  |  |  | 					s3Error = ErrNoSuchKey | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-08-04 07:17:21 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2018-05-02 14:43:27 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-29 04:08:30 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	getObjectNInfo := objectAPI.GetObjectNInfo | 
					
						
							| 
									
										
										
										
											2018-08-29 04:08:30 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Get request range.
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	var rs *HTTPRangeSpec | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 	var rangeErr error | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  |  | 	rangeHeader := r.Header.Get(xhttp.Range) | 
					
						
							| 
									
										
										
										
											2018-08-29 04:08:30 +08:00
										 |  |  |  | 	if rangeHeader != "" { | 
					
						
							| 
									
										
										
										
											2021-10-13 00:24:19 +08:00
										 |  |  |  | 		// Both 'Range' and 'partNumber' cannot be specified at the same time
 | 
					
						
							|  |  |  |  | 		if opts.PartNumber > 0 { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRangePartNumber), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 		rs, rangeErr = parseRequestRangeSpec(rangeHeader) | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 		// Handle only errInvalidRange. Ignore other
 | 
					
						
							|  |  |  |  | 		// parse error and treat it as regular Get
 | 
					
						
							|  |  |  |  | 		// request like Amazon S3.
 | 
					
						
							| 
									
										
										
										
											2022-12-16 00:25:05 +08:00
										 |  |  |  | 		if errors.Is(rangeErr, errInvalidRange) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRange), r.URL) | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	cachedResult := globalCacheConfig.Enabled() && opts.VersionID == "" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var update bool | 
					
						
							|  |  |  |  | 	if cachedResult { | 
					
						
							|  |  |  |  | 		rc := &cache.CondCheck{} | 
					
						
							|  |  |  |  | 		h := r.Header.Clone() | 
					
						
							|  |  |  |  | 		if opts.PartNumber > 0 { | 
					
						
							|  |  |  |  | 			h.Set(xhttp.PartNumber, strconv.Itoa(opts.PartNumber)) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		rc.Init(bucket, object, h) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		ci, err := globalCacheConfig.Get(rc) | 
					
						
							|  |  |  |  | 		if ci != nil { | 
					
						
							|  |  |  |  | 			tgs, ok := ci.Metadata[xhttp.AmzObjectTagging] | 
					
						
							|  |  |  |  | 			if ok { | 
					
						
							|  |  |  |  | 				// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 				r.Header.Set(xhttp.AmzObjectTagging, tgs) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			if s3Error := authorizeRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 				writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error)) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			okSt := (ci.StatusCode == http.StatusOK || ci.StatusCode == http.StatusPartialContent || | 
					
						
							|  |  |  |  | 				ci.StatusCode == http.StatusPreconditionFailed || ci.StatusCode == http.StatusNotModified) | 
					
						
							|  |  |  |  | 			if okSt { | 
					
						
							|  |  |  |  | 				ci.WriteHeaders(w, func() { | 
					
						
							|  |  |  |  | 					// set common headers
 | 
					
						
							|  |  |  |  | 					setCommonHeaders(w) | 
					
						
							|  |  |  |  | 				}, func() { | 
					
						
							|  |  |  |  | 					okSt := (ci.StatusCode == http.StatusOK || ci.StatusCode == http.StatusPartialContent) | 
					
						
							|  |  |  |  | 					if okSt && len(ci.Data) > 0 { | 
					
						
							|  |  |  |  | 						for k, v := range ci.Metadata { | 
					
						
							|  |  |  |  | 							w.Header().Set(k, v) | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						if opts.PartNumber > 0 && strings.Contains(ci.ETag, "-") { | 
					
						
							|  |  |  |  | 							w.Header()[xhttp.AmzMpPartsCount] = []string{ | 
					
						
							|  |  |  |  | 								strings.TrimLeftFunc(ci.ETag, func(r rune) bool { | 
					
						
							|  |  |  |  | 									return !unicode.IsNumber(r) | 
					
						
							|  |  |  |  | 								}), | 
					
						
							|  |  |  |  | 							} | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						// For providing ranged content
 | 
					
						
							|  |  |  |  | 						start, rangeLen, err := rs.GetOffsetLength(ci.Size) | 
					
						
							|  |  |  |  | 						if err != nil { | 
					
						
							|  |  |  |  | 							start, rangeLen = 0, ci.Size | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						// Set content length.
 | 
					
						
							|  |  |  |  | 						w.Header().Set(xhttp.ContentLength, strconv.FormatInt(rangeLen, 10)) | 
					
						
							|  |  |  |  | 						if rs != nil { | 
					
						
							|  |  |  |  | 							contentRange := fmt.Sprintf("bytes %d-%d/%d", start, start+rangeLen-1, ci.Size) | 
					
						
							|  |  |  |  | 							w.Header().Set(xhttp.ContentRange, contentRange) | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						io.Copy(w, bytes.NewReader(ci.Data)) | 
					
						
							|  |  |  |  | 						return | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 					if ci.StatusCode == http.StatusPreconditionFailed { | 
					
						
							|  |  |  |  | 						writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPreconditionFailed), r.URL) | 
					
						
							|  |  |  |  | 						return | 
					
						
							|  |  |  |  | 					} else if ci.StatusCode == http.StatusNotModified { | 
					
						
							|  |  |  |  | 						w.WriteHeader(ci.StatusCode) | 
					
						
							|  |  |  |  | 						return | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 					// We did not satisfy any requirement from the cache, update the cache.
 | 
					
						
							|  |  |  |  | 					// this basically means that we do not have the Data for the object
 | 
					
						
							|  |  |  |  | 					// cached yet
 | 
					
						
							|  |  |  |  | 					update = true | 
					
						
							|  |  |  |  | 				}) | 
					
						
							|  |  |  |  | 				if !update { | 
					
						
							|  |  |  |  | 					// No update is needed means we have written already to the client just return here.
 | 
					
						
							|  |  |  |  | 					return | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if errors.Is(err, cache.ErrKeyMissing) { | 
					
						
							|  |  |  |  | 			update = true | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 	// Validate pre-conditions if any.
 | 
					
						
							|  |  |  |  | 	opts.CheckPrecondFn = func(oi ObjectInfo) bool { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if _, err := DecryptObjectInfo(&oi, r); err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  |  | 		return checkPreconditions(ctx, w, r, oi, opts) | 
					
						
							| 
									
										
										
										
											2018-08-29 04:08:30 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	opts.FastGetObjInfo = true | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  |  | 	var proxy proxyResult | 
					
						
							| 
									
										
										
										
											2023-04-18 03:16:37 +08:00
										 |  |  |  | 	gr, err := getObjectNInfo(ctx, bucket, object, rs, r.Header, opts) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 		var ( | 
					
						
							|  |  |  |  | 			reader *GetObjectReader | 
					
						
							| 
									
										
										
										
											2022-03-30 01:53:09 +08:00
										 |  |  |  | 			perr   error | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2022-05-09 07:50:31 +08:00
										 |  |  |  | 		proxytgts := getProxyTargets(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		if !proxytgts.Empty() { | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 			// proxy to replication target if active-active replication is in place.
 | 
					
						
							| 
									
										
										
										
											2022-03-30 01:53:09 +08:00
										 |  |  |  | 			reader, proxy, perr = proxyGetToReplicationTarget(ctx, bucket, object, rs, r.Header, opts, proxytgts) | 
					
						
							| 
									
										
										
										
											2022-06-28 05:03:44 +08:00
										 |  |  |  | 			if perr != nil { | 
					
						
							|  |  |  |  | 				proxyGetErr := ErrorRespToObjectError(perr, bucket, object) | 
					
						
							| 
									
										
										
										
											2023-01-27 17:29:32 +08:00
										 |  |  |  | 				if !isErrBucketNotFound(proxyGetErr) && !isErrObjectNotFound(proxyGetErr) && !isErrVersionNotFound(proxyGetErr) && | 
					
						
							| 
									
										
										
										
											2022-06-28 05:03:44 +08:00
										 |  |  |  | 					!isErrPreconditionFailed(proxyGetErr) && !isErrInvalidRange(proxyGetErr) { | 
					
						
							| 
									
										
										
										
											2022-12-16 00:25:05 +08:00
										 |  |  |  | 					logger.LogIf(ctx, fmt.Errorf("Proxying request (replication) failed for %s/%s(%s) - %w", bucket, object, opts.VersionID, perr)) | 
					
						
							| 
									
										
										
										
											2022-06-28 05:03:44 +08:00
										 |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-03-09 05:58:55 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-03-30 01:53:09 +08:00
										 |  |  |  | 			if reader != nil && proxy.Proxy && perr == nil { | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 				gr = reader | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-03-09 05:58:55 +08:00
										 |  |  |  | 		if reader == nil || !proxy.Proxy { | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 			// validate if the request indeed was authorized, if it wasn't we need to return "ErrAccessDenied"
 | 
					
						
							|  |  |  |  | 			// instead of any namespace related error.
 | 
					
						
							|  |  |  |  | 			if s3Error := authorizeRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 				writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 			if isErrPreconditionFailed(err) { | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-03-09 05:58:55 +08:00
										 |  |  |  | 			if proxy.Err != nil { | 
					
						
							|  |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, proxy.Err), r.URL) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 			if gr != nil { | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 				if !gr.ObjInfo.VersionPurgeStatus.Empty() { | 
					
						
							|  |  |  |  | 					// Shows the replication status of a permanent delete of a version
 | 
					
						
							|  |  |  |  | 					w.Header()[xhttp.MinIODeleteReplicationStatus] = []string{string(gr.ObjInfo.VersionPurgeStatus)} | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				if !gr.ObjInfo.ReplicationStatus.Empty() && gr.ObjInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 					w.Header()[xhttp.MinIODeleteMarkerReplicationStatus] = []string{string(gr.ObjInfo.ReplicationStatus)} | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 				// Versioning enabled quite possibly object is deleted might be delete-marker
 | 
					
						
							|  |  |  |  | 				// if present set the headers, no idea why AWS S3 sets these headers.
 | 
					
						
							|  |  |  |  | 				if gr.ObjInfo.VersionID != "" && gr.ObjInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 					w.Header()[xhttp.AmzVersionID] = []string{gr.ObjInfo.VersionID} | 
					
						
							|  |  |  |  | 					w.Header()[xhttp.AmzDeleteMarker] = []string{strconv.FormatBool(gr.ObjInfo.DeleteMarker)} | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-07-13 14:51:33 +08:00
										 |  |  |  | 				QueueReplicationHeal(ctx, bucket, gr.ObjInfo, 0) | 
					
						
							| 
									
										
										
										
											2020-07-03 07:17:27 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-02-11 09:25:04 +08:00
										 |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2020-07-03 07:17:27 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-27 09:10:08 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	defer gr.Close() | 
					
						
							| 
									
										
										
										
											2021-01-04 08:27:34 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	objInfo := gr.ObjInfo | 
					
						
							| 
									
										
										
										
											2016-06-27 09:10:08 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 03:29:29 +08:00
										 |  |  |  | 	if objInfo.UserTags != "" { | 
					
						
							|  |  |  |  | 		r.Header.Set(xhttp.AmzObjectTagging, objInfo.UserTags) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if s3Error := authorizeRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	if !proxy.Proxy { // apply lifecycle rules only for local requests
 | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 		// Automatically remove the object/version if an expiry lifecycle rule can be applied
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 		if lc, err := globalLifecycleSys.Get(bucket); err == nil { | 
					
						
							|  |  |  |  | 			rcfg, _ := globalBucketObjectLockSys.Get(bucket) | 
					
						
							| 
									
										
										
										
											2023-10-06 20:55:15 +08:00
										 |  |  |  | 			replcfg, _ := getReplicationConfig(ctx, bucket) | 
					
						
							|  |  |  |  | 			event := evalActionFromLifecycle(ctx, *lc, rcfg, replcfg, objInfo) | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  |  | 			if event.Action.Delete() { | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 				// apply whatever the expiry rule is.
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  |  | 				applyExpiryRule(event, lcEventSrc_s3GetObject, objInfo) | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  |  | 				if !event.Action.DeleteRestored() { | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 					// If the ILM action is not on restored object return error.
 | 
					
						
							|  |  |  |  | 					writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrNoSuchKey)) | 
					
						
							|  |  |  |  | 					return | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-13 14:51:33 +08:00
										 |  |  |  | 		QueueReplicationHeal(ctx, bucket, gr.ObjInfo, 0) | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	getRetPerms := checkRequestAuthType(ctx, r, policy.GetObjectRetentionAction, bucket, object) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	legalHoldPerms := checkRequestAuthType(ctx, r, policy.GetObjectLegalHoldAction, bucket, object) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	// Set encryption response headers
 | 
					
						
							| 
									
										
										
										
											2023-06-01 04:10:25 +08:00
										 |  |  |  | 	if kind, isEncrypted := crypto.IsEncrypted(objInfo.UserDefined); isEncrypted { | 
					
						
							|  |  |  |  | 		switch kind { | 
					
						
							|  |  |  |  | 		case crypto.S3: | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) | 
					
						
							|  |  |  |  | 		case crypto.S3KMS: | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionKmsID, objInfo.KMSKeyID()) | 
					
						
							|  |  |  |  | 			if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok { | 
					
						
							|  |  |  |  | 				w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		case crypto.SSEC: | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) | 
					
						
							| 
									
										
										
										
											2016-07-11 08:12:22 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-01 04:10:25 +08:00
										 |  |  |  | 		objInfo.ETag = getDecryptedETag(r.Header, objInfo, false) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-28 23:26:32 +08:00
										 |  |  |  | 	if r.Header.Get(xhttp.AmzChecksumMode) == "ENABLED" && rs == nil { | 
					
						
							|  |  |  |  | 		// AWS S3 silently drops checksums on range requests.
 | 
					
						
							|  |  |  |  | 		hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber)) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	var buf *bytebufferpool.ByteBuffer | 
					
						
							|  |  |  |  | 	if update { | 
					
						
							|  |  |  |  | 		if globalCacheConfig.MatchesSize(objInfo.Size) { | 
					
						
							|  |  |  |  | 			buf = bytebufferpool.Get() | 
					
						
							|  |  |  |  | 			defer bytebufferpool.Put(buf) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		defer func() { | 
					
						
							|  |  |  |  | 			var data []byte | 
					
						
							|  |  |  |  | 			if buf != nil { | 
					
						
							|  |  |  |  | 				data = buf.Bytes() | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			asize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				asize = objInfo.Size | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			globalCacheConfig.Set(&cache.ObjectInfo{ | 
					
						
							|  |  |  |  | 				Key:          objInfo.Name, | 
					
						
							|  |  |  |  | 				Bucket:       objInfo.Bucket, | 
					
						
							|  |  |  |  | 				ETag:         objInfo.ETag, | 
					
						
							|  |  |  |  | 				ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  |  | 				Expires:      objInfo.ExpiresStr(), | 
					
						
							|  |  |  |  | 				CacheControl: objInfo.CacheControl, | 
					
						
							|  |  |  |  | 				Metadata:     cleanReservedKeys(objInfo.UserDefined), | 
					
						
							|  |  |  |  | 				Range:        rangeHeader, | 
					
						
							|  |  |  |  | 				PartNumber:   opts.PartNumber, | 
					
						
							|  |  |  |  | 				Size:         asize, | 
					
						
							|  |  |  |  | 				Data:         data, | 
					
						
							|  |  |  |  | 			}) | 
					
						
							|  |  |  |  | 		}() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 	if err = setObjectHeaders(w, objInfo, rs, opts); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-09-01 04:10:12 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 00:22:15 +08:00
										 |  |  |  | 	// Set Parts Count Header
 | 
					
						
							|  |  |  |  | 	if opts.PartNumber > 0 && len(objInfo.Parts) > 0 { | 
					
						
							|  |  |  |  | 		setPartsCountHeaders(w, objInfo) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  |  | 	setHeadGetRespHeaders(w, r.Form) | 
					
						
							| 
									
										
										
										
											2018-08-09 06:39:47 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	var iw io.Writer | 
					
						
							|  |  |  |  | 	iw = w | 
					
						
							|  |  |  |  | 	if buf != nil { | 
					
						
							|  |  |  |  | 		iw = io.MultiWriter(w, buf) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	statusCodeWritten := false | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	httpWriter := xioutil.WriteOnClose(iw) | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 	if rs != nil || opts.PartNumber > 0 { | 
					
						
							| 
									
										
										
										
											2018-08-09 06:39:47 +08:00
										 |  |  |  | 		statusCodeWritten = true | 
					
						
							|  |  |  |  | 		w.WriteHeader(http.StatusPartialContent) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-03 07:17:27 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 	// Write object content to response body
 | 
					
						
							| 
									
										
										
										
											2021-11-02 23:11:50 +08:00
										 |  |  |  | 	if _, err = xioutil.Copy(httpWriter, gr); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 		if !httpWriter.HasWritten() && !statusCodeWritten { | 
					
						
							|  |  |  |  | 			// write error response only if no data or headers has been written to client yet
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-01 07:33:03 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
											  
											
												fs: Break fs package to top-level and introduce ObjectAPI interface.
ObjectAPI interface brings in changes needed for XL ObjectAPI layer.
The new interface for any ObjectAPI layer is as below
```
// ObjectAPI interface.
type ObjectAPI interface {
        // Bucket resource API.
        DeleteBucket(bucket string) *probe.Error
        ListBuckets() ([]BucketInfo, *probe.Error)
        MakeBucket(bucket string) *probe.Error
        GetBucketInfo(bucket string) (BucketInfo, *probe.Error)
        // Bucket query API.
        ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsResult, *probe.Error)
        ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, *probe.Error)
        // Object resource API.
        GetObject(bucket, object string, startOffset int64) (io.ReadCloser, *probe.Error)
        GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Error)
        PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (ObjectInfo, *probe.Error)
        DeleteObject(bucket, object string) *probe.Error
        // Object query API.
        NewMultipartUpload(bucket, object string) (string, *probe.Error)
        PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, *probe.Error)
        ListObjectParts(bucket, object string, resources ObjectResourcesMetadata) (ObjectResourcesMetadata, *probe.Error)
        CompleteMultipartUpload(bucket string, object string, uploadID string, parts []CompletePart) (ObjectInfo, *probe.Error)
        AbortMultipartUpload(bucket, object, uploadID string) *probe.Error
}
```
											
										 
											2016-03-31 07:15:28 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	if err = httpWriter.Close(); err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-09 06:39:47 +08:00
										 |  |  |  | 		if !httpWriter.HasWritten() && !statusCodeWritten { // write error response only if no data or headers has been written to client yet
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-01 07:33:03 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-07-11 08:12:22 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-22 01:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Notify object accessed via a GET request.
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  |  | 		EventName:    event.ObjectAccessedGet, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							| 
									
										
										
										
											2019-03-26 02:45:42 +08:00
										 |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2017-03-22 01:32:17 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-06 02:43:06 +08:00
										 |  |  |  | // GetObjectAttributes ...
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) getObjectAttributesHandler(ctx context.Context, objectAPI ObjectLayer, bucket, object string, w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	opts, valid := getAndValidateAttributesOpts(ctx, w, r, bucket, object) | 
					
						
							|  |  |  |  | 	if !valid { | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var s3Error APIErrorCode | 
					
						
							|  |  |  |  | 	if opts.VersionID != "" { | 
					
						
							|  |  |  |  | 		s3Error = checkRequestAuthType(ctx, r, policy.GetObjectVersionAttributesAction, bucket, object) | 
					
						
							|  |  |  |  | 		if s3Error == ErrNone { | 
					
						
							|  |  |  |  | 			s3Error = checkRequestAuthType(ctx, r, policy.GetObjectVersionAction, bucket, object) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							|  |  |  |  | 		s3Error = checkRequestAuthType(ctx, r, policy.GetObjectAttributesAction, bucket, object) | 
					
						
							|  |  |  |  | 		if s3Error == ErrNone { | 
					
						
							|  |  |  |  | 			s3Error = checkRequestAuthType(ctx, r, policy.GetObjectAction, bucket, object) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objInfo, err := objectAPI.GetObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		s3Error = checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, object) | 
					
						
							|  |  |  |  | 		if s3Error == ErrNone { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, err = DecryptObjectInfo(&objInfo, r); err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if checkPreconditions(ctx, w, r, objInfo, opts) { | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	OA := new(getObjectAttributesResponse) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if opts.Versioned { | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzVersionID, objInfo.VersionID) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	lastModified := objInfo.ModTime.UTC().Format(http.TimeFormat) | 
					
						
							|  |  |  |  | 	w.Header().Set(xhttp.LastModified, lastModified) | 
					
						
							|  |  |  |  | 	w.Header().Del(xhttp.ContentType) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, ok := opts.ObjectAttributes[xhttp.Checksum]; ok { | 
					
						
							|  |  |  |  | 		chkSums := objInfo.decryptChecksums(0) | 
					
						
							|  |  |  |  | 		// AWS does not appear to append part number on this API call.
 | 
					
						
							|  |  |  |  | 		switch { | 
					
						
							|  |  |  |  | 		case chkSums["CRC32"] != "": | 
					
						
							|  |  |  |  | 			OA.Checksum = new(objectAttributesChecksum) | 
					
						
							|  |  |  |  | 			OA.Checksum.ChecksumCRC32 = strings.Split(chkSums["CRC32"], "-")[0] | 
					
						
							|  |  |  |  | 		case chkSums["CRC32C"] != "": | 
					
						
							|  |  |  |  | 			OA.Checksum = new(objectAttributesChecksum) | 
					
						
							|  |  |  |  | 			OA.Checksum.ChecksumCRC32C = strings.Split(chkSums["CRC32C"], "-")[0] | 
					
						
							|  |  |  |  | 		case chkSums["SHA256"] != "": | 
					
						
							|  |  |  |  | 			OA.Checksum = new(objectAttributesChecksum) | 
					
						
							|  |  |  |  | 			OA.Checksum.ChecksumSHA1 = strings.Split(chkSums["SHA1"], "-")[0] | 
					
						
							|  |  |  |  | 		case chkSums["SHA1"] != "": | 
					
						
							|  |  |  |  | 			OA.Checksum = new(objectAttributesChecksum) | 
					
						
							|  |  |  |  | 			OA.Checksum.ChecksumSHA256 = strings.Split(chkSums["SHA256"], "-")[0] | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, ok := opts.ObjectAttributes[xhttp.ETag]; ok { | 
					
						
							|  |  |  |  | 		OA.ETag = objInfo.ETag | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, ok := opts.ObjectAttributes[xhttp.ObjectSize]; ok { | 
					
						
							|  |  |  |  | 		OA.ObjectSize, _ = objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, ok := opts.ObjectAttributes[xhttp.StorageClass]; ok { | 
					
						
							|  |  |  |  | 		OA.StorageClass = objInfo.StorageClass | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objInfo.decryptPartsChecksums() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if _, ok := opts.ObjectAttributes[xhttp.ObjectParts]; ok { | 
					
						
							|  |  |  |  | 		OA.ObjectParts = new(objectAttributesParts) | 
					
						
							|  |  |  |  | 		OA.ObjectParts.PartNumberMarker = opts.PartNumberMarker | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		OA.ObjectParts.MaxParts = opts.MaxParts | 
					
						
							|  |  |  |  | 		partsLength := len(objInfo.Parts) | 
					
						
							|  |  |  |  | 		OA.ObjectParts.PartsCount = partsLength | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if opts.MaxParts > -1 { | 
					
						
							|  |  |  |  | 			for i, v := range objInfo.Parts { | 
					
						
							|  |  |  |  | 				if v.Number <= opts.PartNumberMarker { | 
					
						
							|  |  |  |  | 					continue | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 				if len(OA.ObjectParts.Parts) == opts.MaxParts { | 
					
						
							|  |  |  |  | 					break | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 				OA.ObjectParts.NextPartNumberMarker = v.Number | 
					
						
							|  |  |  |  | 				OA.ObjectParts.Parts = append(OA.ObjectParts.Parts, &objectAttributesPart{ | 
					
						
							|  |  |  |  | 					ChecksumSHA1:   objInfo.Parts[i].Checksums["SHA1"], | 
					
						
							|  |  |  |  | 					ChecksumSHA256: objInfo.Parts[i].Checksums["SHA256"], | 
					
						
							|  |  |  |  | 					ChecksumCRC32:  objInfo.Parts[i].Checksums["CRC32"], | 
					
						
							|  |  |  |  | 					ChecksumCRC32C: objInfo.Parts[i].Checksums["CRC32C"], | 
					
						
							|  |  |  |  | 					PartNumber:     objInfo.Parts[i].Number, | 
					
						
							|  |  |  |  | 					Size:           objInfo.Parts[i].Size, | 
					
						
							|  |  |  |  | 				}) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if OA.ObjectParts.NextPartNumberMarker != partsLength { | 
					
						
							|  |  |  |  | 			OA.ObjectParts.IsTruncated = true | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-06 04:50:07 +08:00
										 |  |  |  | 	writeSuccessResponseXML(w, encodeResponse(OA)) | 
					
						
							| 
									
										
										
										
											2024-01-06 02:43:06 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectAccessedAttributes, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | // GetObjectHandler - GET Object
 | 
					
						
							|  |  |  |  | // ----------
 | 
					
						
							|  |  |  |  | // This implementation of the GET operation retrieves object. To use GET,
 | 
					
						
							|  |  |  |  | // you must have READ access to the object.
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "GetObject") | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-02-15 01:19:01 +08:00
										 |  |  |  | 	if !globalAPIConfig.shouldGzipObjects() { | 
					
						
							|  |  |  |  | 		w.Header().Set(gzhttp.HeaderNoCompression, "true") | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if r.Header.Get(xMinIOExtract) == "true" && strings.Contains(object, archivePattern) { | 
					
						
							|  |  |  |  | 		api.getObjectInArchiveFileHandler(ctx, objectAPI, bucket, object, w, r) | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							|  |  |  |  | 		api.getObjectHandler(ctx, objectAPI, bucket, object, w, r) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) headObjectHandler(ctx context.Context, objectAPI ObjectLayer, bucket, object string, w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 	if crypto.S3.IsRequested(r.Header) || crypto.S3KMS.IsRequested(r.Header) { // If SSE-S3 or SSE-KMS present -> AWS fails with undefined error
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest)) | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-22 04:48:58 +08:00
										 |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 	// Check for auth type to return S3 compatible error.
 | 
					
						
							|  |  |  |  | 	// type to return the correct error (NoSuchKey vs AccessDenied)
 | 
					
						
							|  |  |  |  | 	if s3Error := authenticateRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		if getRequestAuthType(r) == authTypeAnonymous { | 
					
						
							|  |  |  |  | 			// As per "Permission" section in
 | 
					
						
							|  |  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html
 | 
					
						
							|  |  |  |  | 			// If the object you request does not exist,
 | 
					
						
							|  |  |  |  | 			// the error Amazon S3 returns depends on
 | 
					
						
							|  |  |  |  | 			// whether you also have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			// permission.
 | 
					
						
							|  |  |  |  | 			// * If you have the s3:ListBucket permission
 | 
					
						
							|  |  |  |  | 			//   on the bucket, Amazon S3 will return an
 | 
					
						
							|  |  |  |  | 			//   HTTP status code 404 ("no such key")
 | 
					
						
							|  |  |  |  | 			//   error.
 | 
					
						
							|  |  |  |  | 			// * if you don’t have the s3:ListBucket
 | 
					
						
							|  |  |  |  | 			//   permission, Amazon S3 will return an HTTP
 | 
					
						
							|  |  |  |  | 			//   status code 403 ("access denied") error.`
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 			if globalPolicySys.IsAllowed(policy.BucketPolicyArgs{ | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 				Action:          policy.ListBucketAction, | 
					
						
							|  |  |  |  | 				BucketName:      bucket, | 
					
						
							| 
									
										
										
										
											2023-02-07 01:27:29 +08:00
										 |  |  |  | 				ConditionValues: getConditionValues(r, "", auth.AnonymousCredentials), | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 				IsOwner:         false, | 
					
						
							|  |  |  |  | 			}) { | 
					
						
							|  |  |  |  | 				getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 				_, err = getObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 				if toAPIError(ctx, err).Code == "NoSuchKey" { | 
					
						
							|  |  |  |  | 					s3Error = ErrNoSuchKey | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-19 09:20:15 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error)) | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	// Get request range.
 | 
					
						
							|  |  |  |  | 	var rs *HTTPRangeSpec | 
					
						
							|  |  |  |  | 	rangeHeader := r.Header.Get(xhttp.Range) | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	if rangeHeader != "" { | 
					
						
							|  |  |  |  | 		rs, _ = parseRequestRangeSpec(rangeHeader) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if rangeHeader != "" { | 
					
						
							|  |  |  |  | 		// Both 'Range' and 'partNumber' cannot be specified at the same time
 | 
					
						
							|  |  |  |  | 		if opts.PartNumber > 0 { | 
					
						
							|  |  |  |  | 			writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInvalidRangePartNumber)) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if rs, err = parseRequestRangeSpec(rangeHeader); err != nil { | 
					
						
							|  |  |  |  | 			// Handle only errInvalidRange. Ignore other
 | 
					
						
							|  |  |  |  | 			// parse error and treat it as regular Get
 | 
					
						
							|  |  |  |  | 			// request like Amazon S3.
 | 
					
						
							|  |  |  |  | 			if errors.Is(err, errInvalidRange) { | 
					
						
							|  |  |  |  | 				writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInvalidRange)) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	cachedResult := globalCacheConfig.Enabled() && opts.VersionID == "" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var update bool | 
					
						
							|  |  |  |  | 	if cachedResult { | 
					
						
							|  |  |  |  | 		rc := &cache.CondCheck{} | 
					
						
							|  |  |  |  | 		h := r.Header.Clone() | 
					
						
							|  |  |  |  | 		if opts.PartNumber > 0 { | 
					
						
							|  |  |  |  | 			h.Set(xhttp.PartNumber, strconv.Itoa(opts.PartNumber)) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		rc.Init(bucket, object, h) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		ci, err := globalCacheConfig.Get(rc) | 
					
						
							|  |  |  |  | 		if ci != nil { | 
					
						
							|  |  |  |  | 			tgs, ok := ci.Metadata[xhttp.AmzObjectTagging] | 
					
						
							|  |  |  |  | 			if ok { | 
					
						
							|  |  |  |  | 				// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 				r.Header.Set(xhttp.AmzObjectTagging, tgs) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			if s3Error := authorizeRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 				writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error)) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			okSt := (ci.StatusCode == http.StatusOK || ci.StatusCode == http.StatusPartialContent || | 
					
						
							|  |  |  |  | 				ci.StatusCode == http.StatusPreconditionFailed || ci.StatusCode == http.StatusNotModified) | 
					
						
							|  |  |  |  | 			if okSt { | 
					
						
							|  |  |  |  | 				ci.WriteHeaders(w, func() { | 
					
						
							|  |  |  |  | 					// set common headers
 | 
					
						
							|  |  |  |  | 					setCommonHeaders(w) | 
					
						
							|  |  |  |  | 				}, func() { | 
					
						
							|  |  |  |  | 					okSt := (ci.StatusCode == http.StatusOK || ci.StatusCode == http.StatusPartialContent) | 
					
						
							|  |  |  |  | 					if okSt { | 
					
						
							|  |  |  |  | 						for k, v := range ci.Metadata { | 
					
						
							|  |  |  |  | 							w.Header().Set(k, v) | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						// For providing ranged content
 | 
					
						
							|  |  |  |  | 						start, rangeLen, err := rs.GetOffsetLength(ci.Size) | 
					
						
							|  |  |  |  | 						if err != nil { | 
					
						
							|  |  |  |  | 							start, rangeLen = 0, ci.Size | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						if opts.PartNumber > 0 && strings.Contains(ci.ETag, "-") { | 
					
						
							|  |  |  |  | 							w.Header()[xhttp.AmzMpPartsCount] = []string{ | 
					
						
							|  |  |  |  | 								strings.TrimLeftFunc(ci.ETag, func(r rune) bool { | 
					
						
							|  |  |  |  | 									return !unicode.IsNumber(r) | 
					
						
							|  |  |  |  | 								}), | 
					
						
							|  |  |  |  | 							} | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						// Set content length for the range.
 | 
					
						
							|  |  |  |  | 						w.Header().Set(xhttp.ContentLength, strconv.FormatInt(rangeLen, 10)) | 
					
						
							|  |  |  |  | 						if rs != nil { | 
					
						
							|  |  |  |  | 							contentRange := fmt.Sprintf("bytes %d-%d/%d", start, start+rangeLen-1, ci.Size) | 
					
						
							|  |  |  |  | 							w.Header().Set(xhttp.ContentRange, contentRange) | 
					
						
							|  |  |  |  | 						} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 						return | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 					if ci.StatusCode == http.StatusPreconditionFailed { | 
					
						
							|  |  |  |  | 						writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPreconditionFailed), r.URL) | 
					
						
							|  |  |  |  | 						return | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 					w.WriteHeader(ci.StatusCode) | 
					
						
							|  |  |  |  | 				}) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if errors.Is(err, cache.ErrKeyMissing) { | 
					
						
							|  |  |  |  | 			update = true | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	opts.FastGetObjInfo = true | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objInfo, err := getObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	var proxy proxyResult | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		// proxy HEAD to replication target if active-active replication configured on bucket
 | 
					
						
							|  |  |  |  | 		proxytgts := getProxyTargets(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 		if !proxytgts.Empty() { | 
					
						
							|  |  |  |  | 			var oi ObjectInfo | 
					
						
							|  |  |  |  | 			oi, proxy = proxyHeadToReplicationTarget(ctx, bucket, object, rs, opts, proxytgts) | 
					
						
							|  |  |  |  | 			if proxy.Proxy { | 
					
						
							|  |  |  |  | 				objInfo = oi | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			if proxy.Err != nil { | 
					
						
							| 
									
										
										
										
											2023-05-27 01:13:18 +08:00
										 |  |  |  | 				writeErrorResponseHeadersOnly(w, toAPIError(ctx, proxy.Err)) | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 	if objInfo.UserTags != "" { | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 		// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 		r.Header.Set(xhttp.AmzObjectTagging, objInfo.UserTags) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 12:45:53 +08:00
										 |  |  |  | 	if s3Error := authorizeRequest(ctx, r, policy.GetObjectAction); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2023-06-19 09:20:15 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error)) | 
					
						
							| 
									
										
										
										
											2018-05-02 14:43:27 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	if err != nil && !proxy.Proxy { | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 		switch { | 
					
						
							|  |  |  |  | 		case !objInfo.VersionPurgeStatus.Empty(): | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.MinIODeleteReplicationStatus] = []string{string(objInfo.VersionPurgeStatus)} | 
					
						
							|  |  |  |  | 		case !objInfo.ReplicationStatus.Empty() && objInfo.DeleteMarker: | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.MinIODeleteMarkerReplicationStatus] = []string{string(objInfo.ReplicationStatus)} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		// Versioning enabled quite possibly object is deleted might be delete-marker
 | 
					
						
							|  |  |  |  | 		// if present set the headers, no idea why AWS S3 sets these headers.
 | 
					
						
							|  |  |  |  | 		if objInfo.VersionID != "" && objInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID} | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzDeleteMarker] = []string{strconv.FormatBool(objInfo.DeleteMarker)} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		QueueReplicationHeal(ctx, bucket, objInfo, 0) | 
					
						
							|  |  |  |  | 		// do an additional verification whether object exists when object is deletemarker and request
 | 
					
						
							|  |  |  |  | 		// is from replication
 | 
					
						
							|  |  |  |  | 		if opts.CheckDMReplicationReady { | 
					
						
							|  |  |  |  | 			topts := opts | 
					
						
							|  |  |  |  | 			topts.VersionID = "" | 
					
						
							|  |  |  |  | 			goi, gerr := getObjectInfo(ctx, bucket, object, topts) | 
					
						
							|  |  |  |  | 			if gerr == nil || goi.VersionID != "" { // object layer returned more info because object is deleted
 | 
					
						
							|  |  |  |  | 				w.Header().Set(xhttp.MinIOTargetReplicationReady, "true") | 
					
						
							| 
									
										
										
										
											2022-10-14 07:43:36 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							|  |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2015-08-04 07:17:21 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	if !proxy.Proxy { // apply lifecycle rules only locally not for proxied requests
 | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 		// Automatically remove the object/version if an expiry lifecycle rule can be applied
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 		if lc, err := globalLifecycleSys.Get(bucket); err == nil { | 
					
						
							|  |  |  |  | 			rcfg, _ := globalBucketObjectLockSys.Get(bucket) | 
					
						
							| 
									
										
										
										
											2023-10-06 20:55:15 +08:00
										 |  |  |  | 			replcfg, _ := getReplicationConfig(ctx, bucket) | 
					
						
							|  |  |  |  | 			event := evalActionFromLifecycle(ctx, *lc, rcfg, replcfg, objInfo) | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  |  | 			if event.Action.Delete() { | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 				// apply whatever the expiry rule is.
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  |  | 				applyExpiryRule(event, lcEventSrc_s3HeadObject, objInfo) | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  |  | 				if !event.Action.DeleteRestored() { | 
					
						
							| 
									
										
										
										
											2023-03-10 07:15:30 +08:00
										 |  |  |  | 					// If the ILM action is not on restored object return error.
 | 
					
						
							|  |  |  |  | 					writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrNoSuchKey)) | 
					
						
							|  |  |  |  | 					return | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-13 14:51:33 +08:00
										 |  |  |  | 		QueueReplicationHeal(ctx, bucket, objInfo, 0) | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	getRetPerms := checkRequestAuthType(ctx, r, policy.GetObjectRetentionAction, bucket, object) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	legalHoldPerms := checkRequestAuthType(ctx, r, policy.GetObjectLegalHoldAction, bucket, object) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// filter object lock metadata if permission does not permit
 | 
					
						
							|  |  |  |  | 	objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	if _, err = DecryptObjectInfo(&objInfo, r); err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							|  |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-09-24 01:24:10 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	if update { | 
					
						
							|  |  |  |  | 		asize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			asize = objInfo.Size | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		defer globalCacheConfig.Set(&cache.ObjectInfo{ | 
					
						
							|  |  |  |  | 			Key:          objInfo.Name, | 
					
						
							|  |  |  |  | 			Bucket:       objInfo.Bucket, | 
					
						
							|  |  |  |  | 			ETag:         objInfo.ETag, | 
					
						
							|  |  |  |  | 			ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  |  | 			Expires:      objInfo.ExpiresStr(), | 
					
						
							|  |  |  |  | 			CacheControl: objInfo.CacheControl, | 
					
						
							|  |  |  |  | 			Size:         asize, | 
					
						
							|  |  |  |  | 			Metadata:     cleanReservedKeys(objInfo.UserDefined), | 
					
						
							|  |  |  |  | 		}) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 	// Validate pre-conditions if any.
 | 
					
						
							|  |  |  |  | 	if checkPreconditions(ctx, w, r, objInfo, opts) { | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-24 01:24:10 +08:00
										 |  |  |  | 	// Set encryption response headers
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind { | 
					
						
							|  |  |  |  | 	case crypto.S3: | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) | 
					
						
							|  |  |  |  | 	case crypto.S3KMS: | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionKmsID, objInfo.KMSKeyID()) | 
					
						
							|  |  |  |  | 		if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok { | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	case crypto.SSEC: | 
					
						
							|  |  |  |  | 		// Validate the SSE-C Key set in the header.
 | 
					
						
							|  |  |  |  | 		if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							|  |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) | 
					
						
							|  |  |  |  | 		w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-29 10:10:37 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-28 23:26:32 +08:00
										 |  |  |  | 	if r.Header.Get(xhttp.AmzChecksumMode) == "ENABLED" && rs == nil { | 
					
						
							|  |  |  |  | 		// AWS S3 silently drops checksums on range requests.
 | 
					
						
							|  |  |  |  | 		hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber)) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-11 08:12:22 +08:00
										 |  |  |  | 	// Set standard object headers.
 | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 	if err = setObjectHeaders(w, objInfo, rs, opts); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  |  | 		writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-29 10:10:37 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 00:22:15 +08:00
										 |  |  |  | 	// Set Parts Count Header
 | 
					
						
							|  |  |  |  | 	if opts.PartNumber > 0 && len(objInfo.Parts) > 0 { | 
					
						
							|  |  |  |  | 		setPartsCountHeaders(w, objInfo) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 02:04:04 +08:00
										 |  |  |  | 	// Set any additional requested response headers.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  |  | 	setHeadGetRespHeaders(w, r.Form) | 
					
						
							| 
									
										
										
										
											2017-08-09 02:04:04 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-28 02:57:08 +08:00
										 |  |  |  | 	// Successful response.
 | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  |  | 	if rs != nil || opts.PartNumber > 0 { | 
					
						
							| 
									
										
										
										
											2018-09-24 01:24:10 +08:00
										 |  |  |  | 		w.WriteHeader(http.StatusPartialContent) | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							|  |  |  |  | 		w.WriteHeader(http.StatusOK) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-22 01:32:17 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Notify object accessed via a HEAD request.
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  |  | 		EventName:    event.ObjectAccessedHead, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							| 
									
										
										
										
											2019-03-26 02:45:42 +08:00
										 |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2017-03-22 01:32:17 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-06 02:43:06 +08:00
										 |  |  |  | // GetObjectAttributesHandles - GET Object
 | 
					
						
							|  |  |  |  | // -----------
 | 
					
						
							|  |  |  |  | // This operation retrieves metadata and part metadata from an object without returning the object itself.
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) GetObjectAttributesHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "GetObjectAttributes") | 
					
						
							|  |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  |  | 		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrServerNotInitialized)) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	api.getObjectAttributesHandler(ctx, objectAPI, bucket, object, w, r) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | // HeadObjectHandler - HEAD Object
 | 
					
						
							|  |  |  |  | // -----------
 | 
					
						
							|  |  |  |  | // The HEAD operation retrieves metadata from an object without returning the object itself.
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "HeadObject") | 
					
						
							|  |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  |  | 		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrServerNotInitialized)) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if r.Header.Get(xMinIOExtract) == "true" && strings.Contains(object, archivePattern) { | 
					
						
							|  |  |  |  | 		api.headObjectInArchiveFileHandler(ctx, objectAPI, bucket, object, w, r) | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							|  |  |  |  | 		api.headObjectHandler(ctx, objectAPI, bucket, object, w, r) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | // Extract metadata relevant for an CopyObject operation based on conditional
 | 
					
						
							|  |  |  |  | // header values specified in X-Amz-Metadata-Directive.
 | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  |  | func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta map[string]string) (map[string]string, error) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 	// Make a copy of the supplied metadata to avoid
 | 
					
						
							|  |  |  |  | 	// to change the original one.
 | 
					
						
							|  |  |  |  | 	defaultMeta := make(map[string]string, len(userMeta)) | 
					
						
							|  |  |  |  | 	for k, v := range userMeta { | 
					
						
							| 
									
										
										
										
											2022-10-25 07:32:31 +08:00
										 |  |  |  | 		// skip tier metadata when copying metadata from source object
 | 
					
						
							|  |  |  |  | 		switch k { | 
					
						
							|  |  |  |  | 		case metaTierName, metaTierStatus, metaTierObjName, metaTierVersionID: | 
					
						
							|  |  |  |  | 			continue | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 		defaultMeta[k] = v | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	// remove SSE Headers from source info
 | 
					
						
							|  |  |  |  | 	crypto.RemoveSSEHeaders(defaultMeta) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 	// Storage class is special, it can be replaced regardless of the
 | 
					
						
							|  |  |  |  | 	// metadata directive, if set should be preserved and replaced
 | 
					
						
							|  |  |  |  | 	// to the destination metadata.
 | 
					
						
							|  |  |  |  | 	sc := r.Header.Get(xhttp.AmzStorageClass) | 
					
						
							|  |  |  |  | 	if sc == "" { | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  |  | 		sc = r.Form.Get(xhttp.AmzStorageClass) | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	// if x-amz-metadata-directive says REPLACE then
 | 
					
						
							|  |  |  |  | 	// we extract metadata from the input headers.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if isDirectiveReplace(r.Header.Get(xhttp.AmzMetadataDirective)) { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 		emetadata, err := extractMetadataFromReq(ctx, r) | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if sc != "" { | 
					
						
							|  |  |  |  | 			emetadata[xhttp.AmzStorageClass] = sc | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return emetadata, nil | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if sc != "" { | 
					
						
							|  |  |  |  | 		defaultMeta[xhttp.AmzStorageClass] = sc | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	// if x-amz-metadata-directive says COPY then we
 | 
					
						
							|  |  |  |  | 	// return the default metadata.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if isDirectiveCopy(r.Header.Get(xhttp.AmzMetadataDirective)) { | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 		return defaultMeta, nil | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	// Copy is default behavior if not x-amz-metadata-directive is set.
 | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 	return defaultMeta, nil | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  |  | // getRemoteInstanceTransport contains a singleton roundtripper.
 | 
					
						
							| 
									
										
										
										
											2020-09-12 14:03:08 +08:00
										 |  |  |  | var ( | 
					
						
							|  |  |  |  | 	getRemoteInstanceTransport     *http.Transport | 
					
						
							|  |  |  |  | 	getRemoteInstanceTransportOnce sync.Once | 
					
						
							|  |  |  |  | ) | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  |  | // Returns a minio-go Client configured to access remote host described by destDNSRecord
 | 
					
						
							|  |  |  |  | // Applicable only in a federated deployment
 | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core, error) { | 
					
						
							| 
									
										
										
										
											2021-11-26 05:06:25 +08:00
										 |  |  |  | 	cred := getReqAccessCred(r, globalSite.Region) | 
					
						
							| 
									
										
										
										
											2020-07-08 03:19:57 +08:00
										 |  |  |  | 	// In a federated deployment, all the instances share config files
 | 
					
						
							|  |  |  |  | 	// and hence expected to have same credentials.
 | 
					
						
							| 
									
										
										
										
											2023-02-15 05:19:30 +08:00
										 |  |  |  | 	core, err := miniogo.NewCore(host, &miniogo.Options{ | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 		Creds:     credentials.NewStaticV4(cred.AccessKey, cred.SecretKey, ""), | 
					
						
							| 
									
										
										
										
											2020-12-22 13:42:38 +08:00
										 |  |  |  | 		Secure:    globalIsTLS, | 
					
						
							| 
									
										
										
										
											2020-09-12 14:03:08 +08:00
										 |  |  |  | 		Transport: getRemoteInstanceTransport, | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-02-15 05:19:30 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	core.SetAppInfo("minio-federated", ReleaseTag) | 
					
						
							|  |  |  |  | 	return core, nil | 
					
						
							| 
									
										
										
										
											2020-07-08 03:19:57 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 01:19:22 +08:00
										 |  |  |  | // Check if the destination bucket is on a remote site, this code only gets executed
 | 
					
						
							|  |  |  |  | // when federation is enabled, ie when globalDNSConfig is non 'nil'.
 | 
					
						
							|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // This function is similar to isRemoteCallRequired but specifically for COPY object API
 | 
					
						
							| 
									
										
										
										
											2020-09-10 03:20:49 +08:00
										 |  |  |  | // if destination and source are same we do not need to check for destination bucket
 | 
					
						
							| 
									
										
										
										
											2019-08-21 01:19:22 +08:00
										 |  |  |  | // to exist locally.
 | 
					
						
							|  |  |  |  | func isRemoteCopyRequired(ctx context.Context, srcBucket, dstBucket string, objAPI ObjectLayer) bool { | 
					
						
							|  |  |  |  | 	if srcBucket == dstBucket { | 
					
						
							|  |  |  |  | 		return false | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return isRemoteCallRequired(ctx, dstBucket, objAPI) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | // Check if the bucket is on a remote site, this code only gets executed when federation is enabled.
 | 
					
						
							| 
									
										
										
										
											2019-08-21 01:19:22 +08:00
										 |  |  |  | func isRemoteCallRequired(ctx context.Context, bucket string, objAPI ObjectLayer) bool { | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 	if globalDNSConfig == nil { | 
					
						
							|  |  |  |  | 		return false | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-12-30 00:56:45 +08:00
										 |  |  |  | 	if globalBucketFederation { | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  |  | 		_, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{}) | 
					
						
							| 
									
										
										
										
											2019-12-30 00:56:45 +08:00
										 |  |  |  | 		return err == toObjectErr(errVolumeNotFound, bucket) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | // CopyObjectHandler - Copy Object
 | 
					
						
							|  |  |  |  | // ----------
 | 
					
						
							|  |  |  |  | // This implementation of the PUT operation adds an object to a bucket
 | 
					
						
							|  |  |  |  | // while reading the object from another source.
 | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | // Notice: The S3 client can send secret keys in headers for encryption related jobs,
 | 
					
						
							|  |  |  |  | // the handler should ensure to remove these keys before sending them to the object layer.
 | 
					
						
							|  |  |  |  | // Currently these keys are:
 | 
					
						
							|  |  |  |  | //   - X-Amz-Server-Side-Encryption-Customer-Key
 | 
					
						
							|  |  |  |  | //   - X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  |  | func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-07-21 09:46:32 +08:00
										 |  |  |  | 	ctx := newContext(r, w, "CopyObject") | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-16 04:57:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	dstBucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	dstObject, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.PutObjectAction, dstBucket, dstObject); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-11 05:10:10 +08:00
										 |  |  |  | 	// Read escaped copy source path to check for parameters.
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  |  | 	cpSrcPath := r.Header.Get(xhttp.AmzCopySource) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	var vid string | 
					
						
							| 
									
										
										
										
											2018-12-13 03:43:44 +08:00
										 |  |  |  | 	if u, err := url.Parse(cpSrcPath); err == nil { | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  |  | 		vid = strings.TrimSpace(u.Query().Get(xhttp.VersionID)) | 
					
						
							| 
									
										
										
										
											2019-01-11 05:10:10 +08:00
										 |  |  |  | 		// Note that url.Parse does the unescaping
 | 
					
						
							| 
									
										
										
										
											2018-12-13 03:43:44 +08:00
										 |  |  |  | 		cpSrcPath = u.Path | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  |  | 	srcBucket, srcObject := path2BucketObject(cpSrcPath) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	// If source object is empty or bucket is empty, reply back invalid copy source.
 | 
					
						
							|  |  |  |  | 	if srcObject == "" || srcBucket == "" { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopySource), r.URL) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	if vid != "" && vid != nullVersionID { | 
					
						
							|  |  |  |  | 		_, err := uuid.Parse(vid) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, VersionNotFound{ | 
					
						
							|  |  |  |  | 				Bucket:    srcBucket, | 
					
						
							|  |  |  |  | 				Object:    srcObject, | 
					
						
							|  |  |  |  | 				VersionID: vid, | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			}), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-27 07:12:44 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectAction, srcBucket, srcObject); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-27 07:12:44 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 	// Check if metadata directive is valid.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if !isDirectiveValid(r.Header.Get(xhttp.AmzMetadataDirective)) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidMetadataDirective), r.URL) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-24 04:35:04 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	// check if tag directive is valid
 | 
					
						
							|  |  |  |  | 	if !isDirectiveValid(r.Header.Get(xhttp.AmzTagDirective)) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidTagDirective), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 	// Validate storage class metadata if present
 | 
					
						
							|  |  |  |  | 	dstSc := r.Header.Get(xhttp.AmzStorageClass) | 
					
						
							|  |  |  |  | 	if dstSc != "" && !storageclass.IsValid(dstSc) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidStorageClass), r.URL) | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 17:42:34 +08:00
										 |  |  |  | 	// Check if bucket encryption is enabled
 | 
					
						
							| 
									
										
										
										
											2021-05-14 15:59:05 +08:00
										 |  |  |  | 	sseConfig, _ := globalBucketSSEConfigSys.Get(dstBucket) | 
					
						
							| 
									
										
										
										
											2021-09-22 00:02:15 +08:00
										 |  |  |  | 	sseConfig.Apply(r.Header, sse.ApplyOptions{ | 
					
						
							|  |  |  |  | 		AutoEncrypt: globalAutoEncryption, | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  |  | 	var srcOpts, dstOpts ObjectOptions | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	srcOpts, err = copySrcOpts(ctx, r, srcBucket, srcObject) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	srcOpts.VersionID = vid | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	// convert copy src encryption options for GET calls
 | 
					
						
							| 
									
										
										
										
											2022-06-07 06:14:56 +08:00
										 |  |  |  | 	getOpts := ObjectOptions{ | 
					
						
							|  |  |  |  | 		VersionID:        srcOpts.VersionID, | 
					
						
							|  |  |  |  | 		Versioned:        srcOpts.Versioned, | 
					
						
							|  |  |  |  | 		VersionSuspended: srcOpts.VersionSuspended, | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	getSSE := encrypt.SSE(srcOpts.ServerSideEncryption) | 
					
						
							|  |  |  |  | 	if getSSE != srcOpts.ServerSideEncryption { | 
					
						
							|  |  |  |  | 		getOpts.ServerSideEncryption = getSSE | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  |  | 	dstOpts, err = copyDstOpts(ctx, r, dstBucket, dstObject, nil) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-11 02:31:50 +08:00
										 |  |  |  | 	cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 	getObjectNInfo := objectAPI.GetObjectNInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 	checkCopyPrecondFn := func(o ObjectInfo) bool { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if _, err := DecryptObjectInfo(&o, r); err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return checkCopyObjectPreconditions(ctx, w, r, o) | 
					
						
							| 
									
										
										
										
											2019-03-07 04:38:41 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-18 04:01:22 +08:00
										 |  |  |  | 	getOpts.CheckPrecondFn = checkCopyPrecondFn | 
					
						
							| 
									
										
										
										
											2023-04-18 03:16:37 +08:00
										 |  |  |  | 	if cpSrcDstSame { | 
					
						
							|  |  |  |  | 		getOpts.NoLock = true | 
					
						
							| 
									
										
										
										
											2020-09-15 06:57:13 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 23:50:06 +08:00
										 |  |  |  | 	var rs *HTTPRangeSpec | 
					
						
							| 
									
										
										
										
											2023-04-18 03:16:37 +08:00
										 |  |  |  | 	gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, getOpts) | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-07 04:38:41 +08:00
										 |  |  |  | 		if isErrPreconditionFailed(err) { | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		// Versioning enabled quite possibly object is deleted might be delete-marker
 | 
					
						
							|  |  |  |  | 		// if present set the headers, no idea why AWS S3 sets these headers.
 | 
					
						
							|  |  |  |  | 		if gr != nil && gr.ObjInfo.VersionID != "" && gr.ObjInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzVersionID] = []string{gr.ObjInfo.VersionID} | 
					
						
							|  |  |  |  | 			w.Header()[xhttp.AmzDeleteMarker] = []string{strconv.FormatBool(gr.ObjInfo.DeleteMarker)} | 
					
						
							| 
									
										
										
										
											2020-07-03 07:17:27 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-08-06 04:56:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 08:25:31 +08:00
										 |  |  |  | 		// Update context bucket & object names for correct S3 XML error response
 | 
					
						
							|  |  |  |  | 		reqInfo := logger.GetReqInfo(ctx) | 
					
						
							|  |  |  |  | 		reqInfo.BucketName = srcBucket | 
					
						
							|  |  |  |  | 		reqInfo.ObjectName = srcObject | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	defer gr.Close() | 
					
						
							|  |  |  |  | 	srcInfo := gr.ObjInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	// maximum Upload size for object in a single CopyObject operation.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 16:48:47 +08:00
										 |  |  |  | 	if isMaxObjectSize(srcInfo.Size) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 	// We have to copy metadata only if source and destination are same.
 | 
					
						
							|  |  |  |  | 	// this changes for encryption which can be observed below.
 | 
					
						
							|  |  |  |  | 	if cpSrcDstSame { | 
					
						
							|  |  |  |  | 		srcInfo.metadataOnly = true | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 	var chStorageClass bool | 
					
						
							| 
									
										
										
										
											2022-04-21 01:22:05 +08:00
										 |  |  |  | 	if dstSc != "" && dstSc != srcInfo.StorageClass { | 
					
						
							| 
									
										
										
										
											2020-06-20 04:53:45 +08:00
										 |  |  |  | 		chStorageClass = true | 
					
						
							|  |  |  |  | 		srcInfo.metadataOnly = false | 
					
						
							| 
									
										
										
										
											2022-04-21 01:22:05 +08:00
										 |  |  |  | 	} // no changes in storage-class expected so its a metadataonly operation.
 | 
					
						
							| 
									
										
										
										
											2020-04-17 08:42:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												pkg/etag: add new package for S3 ETag handling (#11577)
This commit adds a new package `etag` for dealing
with S3 ETags.
Even though ETag is often viewed as MD5 checksum of
an object, handling S3 ETags correctly is a surprisingly
complex task. While it is true that the ETag corresponds
to the MD5 for the most basic S3 API operations, there are
many exceptions in case of multipart uploads or encryption.
In worse, some S3 clients expect very specific behavior when
it comes to ETags. For example, some clients expect that the
ETag is a double-quoted string and fail otherwise.
Non-AWS compliant ETag handling has been a source of many bugs
in the past.
Therefore, this commit adds a dedicated `etag` package that provides
functionality for parsing, generating and converting S3 ETags.
Further, this commit removes the ETag computation from the `hash`
package. Instead, the `hash` package (i.e. `hash.Reader`) should
focus only on computing and verifying the content-sha256.
One core feature of this commit is to provide a mechanism to
communicate a computed ETag from a low-level `io.Reader` to
a high-level `io.Reader`.
This problem occurs when an S3 server receives a request and
has to compute the ETag of the content. However, the server
may also wrap the initial body with several other `io.Reader`,
e.g. when encrypting or compressing the content:
```
   reader := Encrypt(Compress(ETag(content)))
```
In such a case, the ETag should be accessible by the high-level
`io.Reader`.
The `etag` provides a mechanism to wrap `io.Reader` implementations
such that the `ETag` can be accessed by a type-check.
This technique is applied to the PUT, COPY and Upload handlers.
											
										 
											2021-02-24 04:31:53 +08:00
										 |  |  |  | 	var reader io.Reader = gr | 
					
						
							| 
									
										
										
										
											2018-10-24 02:46:20 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-06 12:08:35 +08:00
										 |  |  |  | 	// Set the actual size to the compressed/decrypted size if encrypted.
 | 
					
						
							|  |  |  |  | 	actualSize, err := srcInfo.GetActualSize() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-01-06 12:08:35 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-10-24 02:46:20 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-06 12:08:35 +08:00
										 |  |  |  | 	length := actualSize | 
					
						
							| 
									
										
										
										
											2020-07-25 03:24:21 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-17 10:27:33 +08:00
										 |  |  |  | 	if !cpSrcDstSame { | 
					
						
							| 
									
										
										
										
											2022-02-01 03:07:04 +08:00
										 |  |  |  | 		if err := enforceBucketQuotaHard(ctx, dstBucket, actualSize); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-05-17 10:27:33 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-01 06:55:54 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-24 02:46:20 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 	// Check if either the source is encrypted or the destination will be encrypted.
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:52:15 +08:00
										 |  |  |  | 	objectEncryption := crypto.Requested(r.Header) | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 	objectEncryption = objectEncryption || crypto.IsSourceEncrypted(srcInfo.UserDefined) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 04:05:41 +08:00
										 |  |  |  | 	var compressMetadata map[string]string | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 	// No need to compress for remote etcd calls
 | 
					
						
							|  |  |  |  | 	// Pass the decompressed stream to such calls.
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	isDstCompressed := isCompressible(r.Header, dstObject) && | 
					
						
							| 
									
										
										
										
											2022-07-12 22:42:04 +08:00
										 |  |  |  | 		length > minCompressibleSize && | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 		!isRemoteCopyRequired(ctx, srcBucket, dstBucket, objectAPI) && !cpSrcDstSame && !objectEncryption | 
					
						
							| 
									
										
										
										
											2021-01-06 12:08:35 +08:00
										 |  |  |  | 	if isDstCompressed { | 
					
						
							| 
									
										
										
										
											2018-12-12 04:05:41 +08:00
										 |  |  |  | 		compressMetadata = make(map[string]string, 2) | 
					
						
							|  |  |  |  | 		// Preserving the compression metadata.
 | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  |  | 		compressMetadata[ReservedMetadataPrefix+"compression"] = compressionAlgorithmV2 | 
					
						
							| 
									
										
										
										
											2018-12-12 04:05:41 +08:00
										 |  |  |  | 		compressMetadata[ReservedMetadataPrefix+"actual-size"] = strconv.FormatInt(actualSize, 10) | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 		reader = etag.NewReader(ctx, reader, nil, nil) | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		wantEncryption := crypto.Requested(r.Header) | 
					
						
							| 
									
										
										
										
											2022-07-13 22:52:15 +08:00
										 |  |  |  | 		s2c, cb := newS2CompressReader(reader, actualSize, wantEncryption) | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 		dstOpts.IndexCB = cb | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  |  | 		defer s2c.Close() | 
					
						
							| 
									
										
											  
											
												pkg/etag: add new package for S3 ETag handling (#11577)
This commit adds a new package `etag` for dealing
with S3 ETags.
Even though ETag is often viewed as MD5 checksum of
an object, handling S3 ETags correctly is a surprisingly
complex task. While it is true that the ETag corresponds
to the MD5 for the most basic S3 API operations, there are
many exceptions in case of multipart uploads or encryption.
In worse, some S3 clients expect very specific behavior when
it comes to ETags. For example, some clients expect that the
ETag is a double-quoted string and fail otherwise.
Non-AWS compliant ETag handling has been a source of many bugs
in the past.
Therefore, this commit adds a dedicated `etag` package that provides
functionality for parsing, generating and converting S3 ETags.
Further, this commit removes the ETag computation from the `hash`
package. Instead, the `hash` package (i.e. `hash.Reader`) should
focus only on computing and verifying the content-sha256.
One core feature of this commit is to provide a mechanism to
communicate a computed ETag from a low-level `io.Reader` to
a high-level `io.Reader`.
This problem occurs when an S3 server receives a request and
has to compute the ETag of the content. However, the server
may also wrap the initial body with several other `io.Reader`,
e.g. when encrypting or compressing the content:
```
   reader := Encrypt(Compress(ETag(content)))
```
In such a case, the ETag should be accessible by the high-level
`io.Reader`.
The `etag` provides a mechanism to wrap `io.Reader` implementations
such that the `ETag` can be accessed by a type-check.
This technique is applied to the PUT, COPY and Upload handlers.
											
										 
											2021-02-24 04:31:53 +08:00
										 |  |  |  | 		reader = etag.Wrap(s2c, reader) | 
					
						
							| 
									
										
										
										
											2019-03-06 00:35:37 +08:00
										 |  |  |  | 		length = -1 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 		delete(srcInfo.UserDefined, ReservedMetadataPrefix+"compression") | 
					
						
							|  |  |  |  | 		delete(srcInfo.UserDefined, ReservedMetadataPrefix+"actual-size") | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		reader = gr | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 	srcInfo.Reader, err = hash.NewReader(ctx, reader, length, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-28 05:23:32 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 00:52:50 +08:00
										 |  |  |  | 	pReader := NewPutObjReader(srcInfo.Reader) | 
					
						
							| 
									
										
										
										
											2018-11-28 05:23:32 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 	// Handle encryption
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  |  | 	encMetadata := make(map[string]string) | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	// Encryption parameters not applicable for this object.
 | 
					
						
							|  |  |  |  | 	if _, ok := crypto.IsEncrypted(srcInfo.UserDefined); !ok && crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	// Encryption parameters not present for this object.
 | 
					
						
							|  |  |  |  | 	if crypto.SSEC.IsEncrypted(srcInfo.UserDefined) && !crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidSSECustomerAlgorithm), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var oldKey, newKey []byte | 
					
						
							|  |  |  |  | 	var newKeyID string | 
					
						
							|  |  |  |  | 	var kmsCtx kms.Context | 
					
						
							|  |  |  |  | 	var objEncKey crypto.ObjectKey | 
					
						
							|  |  |  |  | 	sseCopyKMS := crypto.S3KMS.IsEncrypted(srcInfo.UserDefined) | 
					
						
							|  |  |  |  | 	sseCopyS3 := crypto.S3.IsEncrypted(srcInfo.UserDefined) | 
					
						
							|  |  |  |  | 	sseCopyC := crypto.SSEC.IsEncrypted(srcInfo.UserDefined) && crypto.SSECopy.IsRequested(r.Header) | 
					
						
							|  |  |  |  | 	sseC := crypto.SSEC.IsRequested(r.Header) | 
					
						
							|  |  |  |  | 	sseS3 := crypto.S3.IsRequested(r.Header) | 
					
						
							|  |  |  |  | 	sseKMS := crypto.S3KMS.IsRequested(r.Header) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	isSourceEncrypted := sseCopyC || sseCopyS3 || sseCopyKMS | 
					
						
							|  |  |  |  | 	isTargetEncrypted := sseC || sseS3 || sseKMS | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if sseC { | 
					
						
							|  |  |  |  | 		newKey, err = ParseSSECustomerRequest(r) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-20 01:41:13 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if crypto.S3KMS.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 		newKeyID, kmsCtx, err = crypto.S3KMS.ParseHTTP(r.Header) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-20 01:41:13 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	// If src == dst and either
 | 
					
						
							|  |  |  |  | 	// - the object is encrypted using SSE-C and two different SSE-C keys are present
 | 
					
						
							|  |  |  |  | 	// - the object is encrypted using SSE-S3 and the SSE-S3 header is present
 | 
					
						
							|  |  |  |  | 	// - the object storage class is not changing
 | 
					
						
							|  |  |  |  | 	// then execute a key rotation.
 | 
					
						
							|  |  |  |  | 	if cpSrcDstSame && (sseCopyC && sseC) && !chStorageClass { | 
					
						
							|  |  |  |  | 		oldKey, err = ParseSSECopyCustomerRequest(r.Header, srcInfo.UserDefined) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-16 02:07:36 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		for k, v := range srcInfo.UserDefined { | 
					
						
							| 
									
										
										
										
											2023-07-07 07:02:08 +08:00
										 |  |  |  | 			if stringsHasPrefixFold(k, ReservedMetadataPrefixLower) { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 				encMetadata[k] = v | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if err = rotateKey(ctx, oldKey, newKeyID, newKey, srcBucket, srcObject, encMetadata, kmsCtx); err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2021-05-07 06:24:01 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-11-06 02:26:10 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		// Since we are rotating the keys, make sure to update the metadata.
 | 
					
						
							|  |  |  |  | 		srcInfo.metadataOnly = true | 
					
						
							|  |  |  |  | 		srcInfo.keyRotation = true | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							|  |  |  |  | 		if isSourceEncrypted || isTargetEncrypted { | 
					
						
							|  |  |  |  | 			// We are not only copying just metadata instead
 | 
					
						
							|  |  |  |  | 			// we are creating a new object at this point, even
 | 
					
						
							|  |  |  |  | 			// if source and destination are same objects.
 | 
					
						
							|  |  |  |  | 			if !srcInfo.keyRotation { | 
					
						
							|  |  |  |  | 				srcInfo.metadataOnly = false | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		// Calculate the size of the target object
 | 
					
						
							|  |  |  |  | 		var targetSize int64 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		switch { | 
					
						
							|  |  |  |  | 		case isDstCompressed: | 
					
						
							|  |  |  |  | 			targetSize = -1 | 
					
						
							|  |  |  |  | 		case !isSourceEncrypted && !isTargetEncrypted: | 
					
						
							|  |  |  |  | 			targetSize, _ = srcInfo.GetActualSize() | 
					
						
							|  |  |  |  | 		case isSourceEncrypted && isTargetEncrypted: | 
					
						
							|  |  |  |  | 			objInfo := ObjectInfo{Size: actualSize} | 
					
						
							|  |  |  |  | 			targetSize = objInfo.EncryptedSize() | 
					
						
							|  |  |  |  | 		case !isSourceEncrypted && isTargetEncrypted: | 
					
						
							|  |  |  |  | 			targetSize = srcInfo.EncryptedSize() | 
					
						
							|  |  |  |  | 		case isSourceEncrypted && !isTargetEncrypted: | 
					
						
							|  |  |  |  | 			targetSize, _ = srcInfo.DecryptedSize() | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if isTargetEncrypted { | 
					
						
							|  |  |  |  | 			var encReader io.Reader | 
					
						
							|  |  |  |  | 			kind, _ := crypto.IsRequested(r.Header) | 
					
						
							|  |  |  |  | 			encReader, objEncKey, err = newEncryptReader(ctx, srcInfo.Reader, kind, newKeyID, newKey, dstBucket, dstObject, encMetadata, kmsCtx) | 
					
						
							| 
									
										
										
										
											2019-09-24 04:35:04 +08:00
										 |  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-09-24 04:35:04 +08:00
										 |  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			reader = etag.Wrap(encReader, srcInfo.Reader) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-20 06:12:53 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if isSourceEncrypted { | 
					
						
							|  |  |  |  | 			// Remove all source encrypted related metadata to
 | 
					
						
							|  |  |  |  | 			// avoid copying them in target object.
 | 
					
						
							|  |  |  |  | 			crypto.RemoveInternalEntries(srcInfo.UserDefined) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		// do not try to verify encrypted content
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 		srcInfo.Reader, err = hash.NewReader(ctx, reader, targetSize, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-16 02:07:36 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if isTargetEncrypted { | 
					
						
							|  |  |  |  | 			pReader, err = pReader.WithEncryption(srcInfo.Reader, &objEncKey) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			if dstOpts.IndexCB != nil { | 
					
						
							|  |  |  |  | 				dstOpts.IndexCB = compressionIndexEncrypter(objEncKey, dstOpts.IndexCB) | 
					
						
							| 
									
										
										
										
											2020-04-12 11:20:30 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 	srcInfo.PutObjReader = pReader | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  |  | 	srcInfo.UserDefined, err = getCpObjMetadataFromHeader(ctx, r, srcInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-10-04 01:38:25 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  |  | 	objTags := srcInfo.UserTags | 
					
						
							|  |  |  |  | 	// If x-amz-tagging-directive header is REPLACE, get passed tags.
 | 
					
						
							|  |  |  |  | 	if isDirectiveReplace(r.Header.Get(xhttp.AmzTagDirective)) { | 
					
						
							|  |  |  |  | 		objTags = r.Header.Get(xhttp.AmzObjectTagging) | 
					
						
							|  |  |  |  | 		if _, err := tags.ParseObjectTags(objTags); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  |  | 	if objTags != "" { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		lastTaggingTimestamp := srcInfo.UserDefined[ReservedMetadataPrefixLower+TaggingTimestamp] | 
					
						
							|  |  |  |  | 		if dstOpts.ReplicationRequest { | 
					
						
							|  |  |  |  | 			srcTimestamp := dstOpts.ReplicationSourceTaggingTimestamp | 
					
						
							|  |  |  |  | 			if !srcTimestamp.IsZero() { | 
					
						
							|  |  |  |  | 				ondiskTimestamp, err := time.Parse(lastTaggingTimestamp, time.RFC3339Nano) | 
					
						
							|  |  |  |  | 				// update tagging metadata only if replica  timestamp is newer than what's on disk
 | 
					
						
							|  |  |  |  | 				if err != nil || (err == nil && ondiskTimestamp.Before(srcTimestamp)) { | 
					
						
							| 
									
										
										
										
											2022-10-28 00:46:52 +08:00
										 |  |  |  | 					srcInfo.UserDefined[ReservedMetadataPrefixLower+TaggingTimestamp] = srcTimestamp.UTC().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 					srcInfo.UserDefined[xhttp.AmzObjectTagging] = objTags | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							|  |  |  |  | 			srcInfo.UserDefined[xhttp.AmzObjectTagging] = objTags | 
					
						
							|  |  |  |  | 			srcInfo.UserDefined[ReservedMetadataPrefixLower+TaggingTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-13 08:32:24 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	srcInfo.UserDefined = filterReplicationStatusMetadata(srcInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	srcInfo.UserDefined = objectlock.FilterObjectLockMetadata(srcInfo.UserDefined, true, true) | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), dstBucket, dstObject, r, policy.PutObjectRetentionAction) | 
					
						
							|  |  |  |  | 	holdPerms := isPutActionAllowed(ctx, getRequestAuthType(r), dstBucket, dstObject, r, policy.PutObjectLegalHoldAction) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	// apply default bucket configuration/governance headers for dest side.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	retentionMode, retentionDate, legalHold, s3Err := checkPutObjectLockAllowed(ctx, r, dstBucket, dstObject, getObjectInfo, retPerms, holdPerms) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	if s3Err == ErrNone && retentionMode.Valid() { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		lastretentionTimestamp := srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] | 
					
						
							|  |  |  |  | 		if dstOpts.ReplicationRequest { | 
					
						
							|  |  |  |  | 			srcTimestamp := dstOpts.ReplicationSourceRetentionTimestamp | 
					
						
							|  |  |  |  | 			if !srcTimestamp.IsZero() { | 
					
						
							|  |  |  |  | 				ondiskTimestamp, err := time.Parse(lastretentionTimestamp, time.RFC3339Nano) | 
					
						
							|  |  |  |  | 				// update retention metadata only if replica  timestamp is newer than what's on disk
 | 
					
						
							|  |  |  |  | 				if err != nil || (err == nil && ondiskTimestamp.Before(srcTimestamp)) { | 
					
						
							|  |  |  |  | 					srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = string(retentionMode) | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 					srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = amztime.ISO8601Format(retentionDate.UTC()) | 
					
						
							| 
									
										
										
										
											2022-10-28 00:46:52 +08:00
										 |  |  |  | 					srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] = srcTimestamp.UTC().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							|  |  |  |  | 			srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = string(retentionMode) | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 			srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = amztime.ISO8601Format(retentionDate.UTC()) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 			srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	if s3Err == ErrNone && legalHold.Status.Valid() { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		lastLegalHoldTimestamp := srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockLegalHoldTimestamp] | 
					
						
							|  |  |  |  | 		if dstOpts.ReplicationRequest { | 
					
						
							|  |  |  |  | 			srcTimestamp := dstOpts.ReplicationSourceLegalholdTimestamp | 
					
						
							|  |  |  |  | 			if !srcTimestamp.IsZero() { | 
					
						
							|  |  |  |  | 				ondiskTimestamp, err := time.Parse(lastLegalHoldTimestamp, time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  |  | 				// update legalhold metadata only if replica timestamp is newer than what's on disk
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 				if err != nil || (err == nil && ondiskTimestamp.Before(srcTimestamp)) { | 
					
						
							|  |  |  |  | 					srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = string(legalHold.Status) | 
					
						
							|  |  |  |  | 					srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] = srcTimestamp.Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							|  |  |  |  | 			srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = string(legalHold.Status) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	if rs := r.Header.Get(xhttp.AmzBucketReplicationStatus); rs != "" { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		srcInfo.UserDefined[ReservedMetadataPrefixLower+ReplicaStatus] = replication.Replica.String() | 
					
						
							|  |  |  |  | 		srcInfo.UserDefined[ReservedMetadataPrefixLower+ReplicaTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 		srcInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = rs | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	op := replication.ObjectReplicationType | 
					
						
							|  |  |  |  | 	if srcInfo.metadataOnly { | 
					
						
							|  |  |  |  | 		op = replication.MetadataReplicationType | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if dsc := mustReplicate(ctx, dstBucket, dstObject, srcInfo.getMustReplicateOptions(op, dstOpts)); dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		srcInfo.UserDefined[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							|  |  |  |  | 		srcInfo.UserDefined[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-12 04:05:41 +08:00
										 |  |  |  | 	// Store the preserved compression metadata.
 | 
					
						
							|  |  |  |  | 	for k, v := range compressMetadata { | 
					
						
							|  |  |  |  | 		srcInfo.UserDefined[k] = v | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 	// We need to preserve the encryption headers set in EncryptRequest,
 | 
					
						
							|  |  |  |  | 	// so we do not want to override them, copy them instead.
 | 
					
						
							|  |  |  |  | 	for k, v := range encMetadata { | 
					
						
							|  |  |  |  | 		srcInfo.UserDefined[k] = v | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 	// Ensure that metadata does not contain sensitive information
 | 
					
						
							|  |  |  |  | 	crypto.RemoveSensitiveEntries(srcInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2021-03-09 04:57:54 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 05:29:27 +08:00
										 |  |  |  | 	// If we see legacy source, metadataOnly we have to overwrite the content.
 | 
					
						
							|  |  |  |  | 	if srcInfo.Legacy { | 
					
						
							|  |  |  |  | 		srcInfo.metadataOnly = false | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	// Check if x-amz-metadata-directive or x-amz-tagging-directive was not set to REPLACE and source,
 | 
					
						
							|  |  |  |  | 	// destination are same objects. Apply this restriction also when
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 	// metadataOnly is true indicating that we are not overwriting the object.
 | 
					
						
							| 
									
										
										
										
											2018-03-07 08:04:48 +08:00
										 |  |  |  | 	// if encryption is enabled we do not need explicit "REPLACE" metadata to
 | 
					
						
							|  |  |  |  | 	// be enabled as well - this is to allow for key-rotation.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if !isDirectiveReplace(r.Header.Get(xhttp.AmzMetadataDirective)) && !isDirectiveReplace(r.Header.Get(xhttp.AmzTagDirective)) && | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  |  | 		srcInfo.metadataOnly && srcOpts.VersionID == "" && !objectEncryption { | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 		// If x-amz-metadata-directive is not set to REPLACE then we need
 | 
					
						
							|  |  |  |  | 		// to error out if source and destination are same.
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopyDest), r.URL) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-11 08:15:12 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 	remoteCallRequired := isRemoteCopyRequired(ctx, srcBucket, dstBucket, objectAPI) | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 	var objInfo ObjectInfo | 
					
						
							|  |  |  |  | 	var os *objSweeper | 
					
						
							|  |  |  |  | 	if remoteCallRequired { | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 		var dstRecords []dns.SrvRecord | 
					
						
							|  |  |  |  | 		dstRecords, err = globalDNSConfig.Get(dstBucket) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		// Send PutObject request to appropriate instance (in federated deployment)
 | 
					
						
							| 
									
										
										
										
											2020-09-12 14:03:08 +08:00
										 |  |  |  | 		core, rerr := getRemoteInstanceClient(r, getHostFromSrv(dstRecords)) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 		if rerr != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  |  | 		tag, err := tags.ParseObjectTags(objTags) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-01-23 04:09:24 +08:00
										 |  |  |  | 		// Remove the metadata for remote calls.
 | 
					
						
							|  |  |  |  | 		delete(srcInfo.UserDefined, ReservedMetadataPrefix+"compression") | 
					
						
							|  |  |  |  | 		delete(srcInfo.UserDefined, ReservedMetadataPrefix+"actual-size") | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  |  | 		opts := miniogo.PutObjectOptions{ | 
					
						
							|  |  |  |  | 			UserMetadata:         srcInfo.UserDefined, | 
					
						
							|  |  |  |  | 			ServerSideEncryption: dstOpts.ServerSideEncryption, | 
					
						
							|  |  |  |  | 			UserTags:             tag.ToMap(), | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  |  | 		remoteObjInfo, rerr := core.PutObject(ctx, dstBucket, dstObject, srcInfo.Reader, | 
					
						
							| 
									
										
										
										
											2020-05-24 02:09:35 +08:00
										 |  |  |  | 			srcInfo.Size, "", "", opts) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 		if rerr != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		objInfo.UserDefined = cloneMSS(opts.UserMetadata) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  |  | 		objInfo.ETag = remoteObjInfo.ETag | 
					
						
							|  |  |  |  | 		objInfo.ModTime = remoteObjInfo.LastModified | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 		os = newObjSweeper(dstBucket, dstObject).WithVersioning(dstOpts.Versioned, dstOpts.VersionSuspended) | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 		// Get appropriate object info to identify the remote object to delete
 | 
					
						
							|  |  |  |  | 		if !srcInfo.metadataOnly { | 
					
						
							|  |  |  |  | 			goiOpts := os.GetOpts() | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 			if !globalTierConfigMgr.Empty() { | 
					
						
							|  |  |  |  | 				if goi, gerr := getObjectInfo(ctx, dstBucket, dstObject, goiOpts); gerr == nil { | 
					
						
							|  |  |  |  | 					os.SetTransitionState(goi.TransitionedObject) | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-08 06:36:46 +08:00
										 |  |  |  | 		copyObjectFn := objectAPI.CopyObject | 
					
						
							| 
									
										
										
										
											2020-09-15 06:57:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 		// Copy source object to destination, if source and destination
 | 
					
						
							|  |  |  |  | 		// object is same then only metadata is updated.
 | 
					
						
							| 
									
										
										
										
											2020-02-08 06:36:46 +08:00
										 |  |  |  | 		objInfo, err = copyObjectFn(ctx, srcBucket, srcObject, dstBucket, dstObject, srcInfo, srcOpts, dstOpts) | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-05-12 03:02:30 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-13 09:57:37 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 05:40:50 +08:00
										 |  |  |  | 	origETag := objInfo.ETag | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	objInfo.ETag = getDecryptedETag(r.Header, objInfo, false) | 
					
						
							|  |  |  |  | 	response := generateCopyObjectResponse(objInfo.ETag, objInfo.ModTime) | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  |  | 	encodedSuccessResponse := encodeResponse(response) | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	if dsc := mustReplicate(ctx, dstBucket, dstObject, objInfo.getMustReplicateOptions(replication.ObjectReplicationType, dstOpts)); dsc.ReplicateAny() { | 
					
						
							|  |  |  |  | 		scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 07:04:55 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	setPutObjHeaders(w, objInfo, false) | 
					
						
							| 
									
										
										
										
											2020-06-20 04:53:45 +08:00
										 |  |  |  | 	// We must not use the http.Header().Set method here because some (broken)
 | 
					
						
							|  |  |  |  | 	// clients expect the x-amz-copy-source-version-id header key to be literally
 | 
					
						
							|  |  |  |  | 	// "x-amz-copy-source-version-id"- not in canonicalized form, preserve it.
 | 
					
						
							|  |  |  |  | 	if srcOpts.VersionID != "" { | 
					
						
							|  |  |  |  | 		w.Header()[strings.ToLower(xhttp.AmzCopySourceVersionID)] = []string{srcOpts.VersionID} | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  |  | 	// Write success response.
 | 
					
						
							|  |  |  |  | 	writeSuccessResponseXML(w, encodedSuccessResponse) | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-29 13:46:19 +08:00
										 |  |  |  | 	// Notify object created event.
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  |  | 		EventName:    event.ObjectCreatedCopy, | 
					
						
							|  |  |  |  | 		BucketName:   dstBucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							| 
									
										
										
										
											2019-03-26 02:45:42 +08:00
										 |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2016-09-29 13:46:19 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	asize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		asize = objInfo.Size | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	defer globalCacheConfig.Set(&cache.ObjectInfo{ | 
					
						
							|  |  |  |  | 		Key:          objInfo.Name, | 
					
						
							|  |  |  |  | 		Bucket:       objInfo.Bucket, | 
					
						
							|  |  |  |  | 		ETag:         objInfo.ETag, | 
					
						
							|  |  |  |  | 		ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  |  | 		Expires:      objInfo.ExpiresStr(), | 
					
						
							|  |  |  |  | 		CacheControl: objInfo.CacheControl, | 
					
						
							|  |  |  |  | 		Size:         asize, | 
					
						
							|  |  |  |  | 		Metadata:     cleanReservedKeys(objInfo.UserDefined), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 	if !remoteCallRequired && !globalTierConfigMgr.Empty() { | 
					
						
							| 
									
										
										
										
											2021-10-02 02:58:17 +08:00
										 |  |  |  | 		// Schedule object for immediate transition if eligible.
 | 
					
						
							| 
									
										
										
										
											2022-10-22 05:40:50 +08:00
										 |  |  |  | 		objInfo.ETag = origETag | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  |  | 		enqueueTransitionImmediate(objInfo, lcEventSrc_s3CopyObject) | 
					
						
							| 
									
										
										
										
											2022-06-28 03:11:33 +08:00
										 |  |  |  | 		// Remove the transitioned object whose object version is being overwritten.
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  |  | 		os.Sweep() | 
					
						
							| 
									
										
										
										
											2021-10-02 02:58:17 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-01 11:15:48 +08:00
										 |  |  |  | // PutObjectHandler - PUT Object
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  |  | // ----------
 | 
					
						
							|  |  |  |  | // This implementation of the PUT operation adds an object to a bucket.
 | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | // Notice: The S3 client can send secret keys in headers for encryption related jobs,
 | 
					
						
							|  |  |  |  | // the handler should ensure to remove these keys before sending them to the object layer.
 | 
					
						
							|  |  |  |  | // Currently these keys are:
 | 
					
						
							|  |  |  |  | //   - X-Amz-Server-Side-Encryption-Customer-Key
 | 
					
						
							|  |  |  |  | //   - X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  |  | func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-07-21 09:46:32 +08:00
										 |  |  |  | 	ctx := newContext(r, w, "PutObject") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-16 04:57:15 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-23 10:29:39 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 	// X-Amz-Copy-Source shouldn't be set for this call.
 | 
					
						
							| 
									
										
										
										
											2019-08-22 16:02:39 +08:00
										 |  |  |  | 	if _, ok := r.Header[xhttp.AmzCopySource]; ok { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopySource), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-18 12:07:19 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-27 12:36:16 +08:00
										 |  |  |  | 	// Validate storage class metadata if present
 | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  |  | 	if sc := r.Header.Get(xhttp.AmzStorageClass); sc != "" { | 
					
						
							|  |  |  |  | 		if !storageclass.IsValid(sc) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidStorageClass), r.URL) | 
					
						
							| 
									
										
										
										
											2017-12-27 12:36:16 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 04:58:28 +08:00
										 |  |  |  | 	clientETag, err := etag.FromContentMD5(r.Header) | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL) | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-16 14:07:52 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  |  | 	// if Content-Length is unknown/missing, deny the request
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  |  | 	size := r.ContentLength | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  |  | 	rAuthType := getRequestAuthType(r) | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 	switch rAuthType { | 
					
						
							|  |  |  |  | 	// Check signature types that must have content length
 | 
					
						
							|  |  |  |  | 	case authTypeStreamingSigned, authTypeStreamingSignedTrailer, authTypeStreamingUnsignedTrailer: | 
					
						
							| 
									
										
										
										
											2019-08-22 16:02:39 +08:00
										 |  |  |  | 		if sizeStr, ok := r.Header[xhttp.AmzDecodedContentLength]; ok { | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  |  | 			if sizeStr[0] == "" { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			size, err = strconv.ParseInt(sizeStr[0], 10, 64) | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-21 08:33:01 +08:00
										 |  |  |  | 	if size == -1 { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  |  | 	// maximum Upload size for objects in a single operation
 | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  |  | 	if isMaxObjectSize(size) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-03 11:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 	metadata, err := extractMetadataFromReq(ctx, r) | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-07-06 07:56:10 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  |  | 	if objTags := r.Header.Get(xhttp.AmzObjectTagging); objTags != "" { | 
					
						
							|  |  |  |  | 		if _, err := tags.ParseObjectTags(objTags); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-06 05:18:13 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		metadata[xhttp.AmzObjectTagging] = objTags | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2021-03-04 04:58:28 +08:00
										 |  |  |  | 		md5hex              = clientETag.String() | 
					
						
							| 
									
										
											  
											
												pkg/etag: add new package for S3 ETag handling (#11577)
This commit adds a new package `etag` for dealing
with S3 ETags.
Even though ETag is often viewed as MD5 checksum of
an object, handling S3 ETags correctly is a surprisingly
complex task. While it is true that the ETag corresponds
to the MD5 for the most basic S3 API operations, there are
many exceptions in case of multipart uploads or encryption.
In worse, some S3 clients expect very specific behavior when
it comes to ETags. For example, some clients expect that the
ETag is a double-quoted string and fail otherwise.
Non-AWS compliant ETag handling has been a source of many bugs
in the past.
Therefore, this commit adds a dedicated `etag` package that provides
functionality for parsing, generating and converting S3 ETags.
Further, this commit removes the ETag computation from the `hash`
package. Instead, the `hash` package (i.e. `hash.Reader`) should
focus only on computing and verifying the content-sha256.
One core feature of this commit is to provide a mechanism to
communicate a computed ETag from a low-level `io.Reader` to
a high-level `io.Reader`.
This problem occurs when an S3 server receives a request and
has to compute the ETag of the content. However, the server
may also wrap the initial body with several other `io.Reader`,
e.g. when encrypting or compressing the content:
```
   reader := Encrypt(Compress(ETag(content)))
```
In such a case, the ETag should be accessible by the high-level
`io.Reader`.
The `etag` provides a mechanism to wrap `io.Reader` implementations
such that the `ETag` can be accessed by a type-check.
This technique is applied to the PUT, COPY and Upload handlers.
											
										 
											2021-02-24 04:31:53 +08:00
										 |  |  |  | 		sha256hex           = "" | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 		rd        io.Reader = r.Body | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 		s3Err     APIErrorCode | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  |  | 		putObject = objectAPI.PutObject | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Check if put is allowed
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	if s3Err = isPutActionAllowed(ctx, rAuthType, bucket, object, r, policy.PutObjectAction); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  |  | 	switch rAuthType { | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 	case authTypeStreamingSigned, authTypeStreamingSignedTrailer: | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  |  | 		// Initialize stream signature verifier.
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 		rd, s3Err = newSignV4ChunkedReader(r, rAuthType == authTypeStreamingSignedTrailer) | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 		if s3Err != ErrNone { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	case authTypeStreamingUnsignedTrailer: | 
					
						
							|  |  |  |  | 		// Initialize stream chunked reader with optional trailers.
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 		rd, s3Err = newUnsignedV4ChunkedReader(r, true) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 		if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  |  | 	case authTypeSignedV2, authTypePresignedV2: | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 		s3Err = isReqAuthenticatedV2(r) | 
					
						
							|  |  |  |  | 		if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2016-10-01 05:32:13 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-09 15:19:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-07 18:04:18 +08:00
										 |  |  |  | 	case authTypePresigned, authTypeSigned: | 
					
						
							| 
									
										
										
										
											2021-11-26 05:06:25 +08:00
										 |  |  |  | 		if s3Err = reqSignatureV4Verify(r, globalSite.Region, serviceS3); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2016-10-03 06:51:49 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if !skipContentSha256Cksum(r) { | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  |  | 			sha256hex = getContentSha256Cksum(r, serviceS3) | 
					
						
							| 
									
										
										
										
											2016-10-03 06:51:49 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-02-17 10:50:36 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 11:09:20 +08:00
										 |  |  |  | 	if _, ok := r.Header[xhttp.MinIOSourceReplicationCheck]; ok { | 
					
						
							|  |  |  |  | 		// requests to just validate replication settings and permissions are not allowed to write data
 | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrReplicationPermissionCheckError), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-01 03:07:04 +08:00
										 |  |  |  | 	if err := enforceBucketQuotaHard(ctx, bucket, size); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-05-01 06:55:54 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if r.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() { | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 		if s3Err = isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.ReplicateObjectAction); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		metadata[ReservedMetadataPrefixLower+ReplicaStatus] = replication.Replica.String() | 
					
						
							|  |  |  |  | 		metadata[ReservedMetadataPrefixLower+ReplicaTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		defer globalReplicationStats.UpdateReplicaStat(bucket, size) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-05 17:42:34 +08:00
										 |  |  |  | 	// Check if bucket encryption is enabled
 | 
					
						
							| 
									
										
										
										
											2021-05-14 15:59:05 +08:00
										 |  |  |  | 	sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) | 
					
						
							| 
									
										
										
										
											2021-09-22 00:02:15 +08:00
										 |  |  |  | 	sseConfig.Apply(r.Header, sse.ApplyOptions{ | 
					
						
							|  |  |  |  | 		AutoEncrypt: globalAutoEncryption, | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-05-02 23:28:18 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	var buf *bytebufferpool.ByteBuffer | 
					
						
							|  |  |  |  | 	if globalCacheConfig.MatchesSize(size) { | 
					
						
							|  |  |  |  | 		buf = bytebufferpool.Get() | 
					
						
							|  |  |  |  | 		defer bytebufferpool.Put(buf) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var reader io.Reader | 
					
						
							|  |  |  |  | 	reader = rd | 
					
						
							|  |  |  |  | 	if buf != nil { | 
					
						
							|  |  |  |  | 		reader = io.TeeReader(rd, buf) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 	actualSize := size | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 	var idxCb func() []byte | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 	if isCompressible(r.Header, object) && size > minCompressibleSize { | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		// Storing the compression metadata.
 | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  |  | 		metadata[ReservedMetadataPrefix+"compression"] = compressionAlgorithmV2 | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		metadata[ReservedMetadataPrefix+"actual-size"] = strconv.FormatInt(size, 10) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 		actualReader, err := hash.NewReader(ctx, reader, size, md5hex, sha256hex, actualSize) | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-10-25 08:44:15 +08:00
										 |  |  |  | 		if err = actualReader.AddChecksum(r, false); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidChecksum), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		// Set compression metrics.
 | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 		var s2c io.ReadCloser | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		wantEncryption := crypto.Requested(r.Header) | 
					
						
							| 
									
										
										
										
											2022-07-13 22:52:15 +08:00
										 |  |  |  | 		s2c, idxCb = newS2CompressReader(actualReader, actualSize, wantEncryption) | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  |  | 		defer s2c.Close() | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												pkg/etag: add new package for S3 ETag handling (#11577)
This commit adds a new package `etag` for dealing
with S3 ETags.
Even though ETag is often viewed as MD5 checksum of
an object, handling S3 ETags correctly is a surprisingly
complex task. While it is true that the ETag corresponds
to the MD5 for the most basic S3 API operations, there are
many exceptions in case of multipart uploads or encryption.
In worse, some S3 clients expect very specific behavior when
it comes to ETags. For example, some clients expect that the
ETag is a double-quoted string and fail otherwise.
Non-AWS compliant ETag handling has been a source of many bugs
in the past.
Therefore, this commit adds a dedicated `etag` package that provides
functionality for parsing, generating and converting S3 ETags.
Further, this commit removes the ETag computation from the `hash`
package. Instead, the `hash` package (i.e. `hash.Reader`) should
focus only on computing and verifying the content-sha256.
One core feature of this commit is to provide a mechanism to
communicate a computed ETag from a low-level `io.Reader` to
a high-level `io.Reader`.
This problem occurs when an S3 server receives a request and
has to compute the ETag of the content. However, the server
may also wrap the initial body with several other `io.Reader`,
e.g. when encrypting or compressing the content:
```
   reader := Encrypt(Compress(ETag(content)))
```
In such a case, the ETag should be accessible by the high-level
`io.Reader`.
The `etag` provides a mechanism to wrap `io.Reader` implementations
such that the `ETag` can be accessed by a type-check.
This technique is applied to the PUT, COPY and Upload handlers.
											
										 
											2021-02-24 04:31:53 +08:00
										 |  |  |  | 		reader = etag.Wrap(s2c, actualReader) | 
					
						
							| 
									
										
										
										
											2018-09-28 11:36:17 +08:00
										 |  |  |  | 		size = -1   // Since compressed size is un-predictable.
 | 
					
						
							|  |  |  |  | 		md5hex = "" // Do not try to verify the content.
 | 
					
						
							|  |  |  |  | 		sha256hex = "" | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-12-22 00:59:38 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var forceMD5 []byte | 
					
						
							|  |  |  |  | 	// Optimization: If SSE-KMS and SSE-C did not request Content-Md5. Use uuid as etag. Optionally enable this also
 | 
					
						
							|  |  |  |  | 	// for server that is started with `--no-compat`.
 | 
					
						
							|  |  |  |  | 	if !etag.ContentMD5Requested(r.Header) && (crypto.S3KMS.IsRequested(r.Header) || crypto.SSEC.IsRequested(r.Header) || !globalServerCtxt.StrictS3Compat) { | 
					
						
							|  |  |  |  | 		forceMD5 = mustGetUUIDBytes() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	hashReader, err := hash.NewReaderWithOpts(ctx, reader, hash.Options{ | 
					
						
							|  |  |  |  | 		Size:       size, | 
					
						
							|  |  |  |  | 		MD5Hex:     md5hex, | 
					
						
							|  |  |  |  | 		SHA256Hex:  sha256hex, | 
					
						
							|  |  |  |  | 		ActualSize: actualSize, | 
					
						
							|  |  |  |  | 		DisableMD5: false, | 
					
						
							|  |  |  |  | 		ForceMD5:   forceMD5, | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-10-22 13:30:34 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-25 08:44:15 +08:00
										 |  |  |  | 	if err := hashReader.AddChecksum(r, size < 0); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidChecksum), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	rawReader := hashReader | 
					
						
							| 
									
										
										
										
											2021-02-11 00:52:50 +08:00
										 |  |  |  | 	pReader := NewPutObjReader(rawReader) | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var opts ObjectOptions | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 	opts, err = putOptsFromReq(ctx, r, bucket, object, metadata) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-06 06:16:43 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 	opts.IndexCB = idxCb | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 05:33:58 +08:00
										 |  |  |  | 	if opts.PreserveETag != "" || | 
					
						
							| 
									
										
										
										
											2023-02-07 10:58:29 +08:00
										 |  |  |  | 		r.Header.Get(xhttp.IfMatch) != "" || | 
					
						
							|  |  |  |  | 		r.Header.Get(xhttp.IfNoneMatch) != "" { | 
					
						
							| 
									
										
										
										
											2022-09-15 09:44:04 +08:00
										 |  |  |  | 		opts.CheckPrecondFn = func(oi ObjectInfo) bool { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			if _, err := DecryptObjectInfo(&oi, r); err != nil { | 
					
						
							|  |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 				return true | 
					
						
							| 
									
										
										
										
											2022-09-15 09:44:04 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 			return checkPreconditionsPUT(ctx, w, r, oi, opts) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.PutObjectRetentionAction) | 
					
						
							|  |  |  |  | 	holdPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.PutObjectLegalHoldAction) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	retentionMode, retentionDate, legalHold, s3Err := checkPutObjectLockAllowed(ctx, r, bucket, object, getObjectInfo, retPerms, holdPerms) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	if s3Err == ErrNone && retentionMode.Valid() { | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		metadata[strings.ToLower(xhttp.AmzObjectLockMode)] = string(retentionMode) | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 		metadata[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = amztime.ISO8601Format(retentionDate.UTC()) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	if s3Err == ErrNone && legalHold.Status.Valid() { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		metadata[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = string(legalHold.Status) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-03-28 07:44:45 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	if dsc := mustReplicate(ctx, bucket, object, getMustReplicateOptions(metadata, "", "", replication.ObjectReplicationType, opts)); dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		metadata[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		metadata[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-10 08:01:45 +08:00
										 |  |  |  | 	var objectEncryptionKey crypto.ObjectKey | 
					
						
							| 
									
										
										
										
											2023-01-25 07:46:33 +08:00
										 |  |  |  | 	if crypto.Requested(r.Header) { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-10 08:01:45 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 04:52:33 +08:00
										 |  |  |  | 		if crypto.SSEC.IsRequested(r.Header) && crypto.S3.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, crypto.ErrIncompatibleEncryptionMethod), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if crypto.SSEC.IsRequested(r.Header) && crypto.S3KMS.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, crypto.ErrIncompatibleEncryptionMethod), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 07:46:33 +08:00
										 |  |  |  | 		if crypto.SSEC.IsRequested(r.Header) && isReplicationEnabled(ctx, bucket) { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParametersSSEC), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		reader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-01-06 12:08:35 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		wantSize := int64(-1) | 
					
						
							|  |  |  |  | 		if size >= 0 { | 
					
						
							|  |  |  |  | 			info := ObjectInfo{Size: size} | 
					
						
							|  |  |  |  | 			wantSize = info.EncryptedSize() | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-10 08:01:45 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		// do not try to verify encrypted content
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 		hashReader, err = hash.NewReader(ctx, etag.Wrap(reader, hashReader), wantSize, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		pReader, err = pReader.WithEncryption(hashReader, &objectEncryptionKey) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if opts.IndexCB != nil { | 
					
						
							|  |  |  |  | 			opts.IndexCB = compressionIndexEncrypter(objectEncryptionKey, opts.IndexCB) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		opts.EncryptFn = metadataEncrypter(objectEncryptionKey) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  |  | 	// Ensure that metadata does not contain sensitive information
 | 
					
						
							|  |  |  |  | 	crypto.RemoveSensitiveEntries(metadata) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	os := newObjSweeper(bucket, object).WithVersioning(opts.Versioned, opts.VersionSuspended) | 
					
						
							|  |  |  |  | 	if !globalTierConfigMgr.Empty() { | 
					
						
							|  |  |  |  | 		// Get appropriate object info to identify the remote object to delete
 | 
					
						
							|  |  |  |  | 		goiOpts := os.GetOpts() | 
					
						
							|  |  |  |  | 		if goi, gerr := getObjectInfo(ctx, bucket, object, goiOpts); gerr == nil { | 
					
						
							|  |  |  |  | 			os.SetTransitionState(goi.TransitionedObject) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  |  | 	// Create the object..
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  |  | 	objInfo, err := putObject(ctx, bucket, object, pReader, opts) | 
					
						
							| 
									
										
										
										
											2015-09-19 18:20:07 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2015-08-04 07:17:21 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 07:46:33 +08:00
										 |  |  |  | 	if r.Header.Get(xMinIOExtract) == "true" && HasSuffix(object, archiveExt) { | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 		opts := ObjectOptions{VersionID: objInfo.VersionID, MTime: objInfo.ModTime} | 
					
						
							|  |  |  |  | 		if _, err := updateObjectMetadataWithZipInfo(ctx, objectAPI, bucket, object, opts); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-06-10 23:17:03 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 05:40:50 +08:00
										 |  |  |  | 	origETag := objInfo.ETag | 
					
						
							| 
									
										
										
										
											2021-05-27 23:18:41 +08:00
										 |  |  |  | 	if kind, encrypted := crypto.IsEncrypted(objInfo.UserDefined); encrypted { | 
					
						
							| 
									
										
										
										
											2021-02-04 07:19:08 +08:00
										 |  |  |  | 		switch kind { | 
					
						
							|  |  |  |  | 		case crypto.S3: | 
					
						
							| 
									
										
										
										
											2020-12-23 01:19:32 +08:00
										 |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 			objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag}) | 
					
						
							| 
									
										
										
										
											2021-05-07 06:24:01 +08:00
										 |  |  |  | 		case crypto.S3KMS: | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) | 
					
						
							| 
									
										
										
										
											2021-12-06 16:39:32 +08:00
										 |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionKmsID, objInfo.KMSKeyID()) | 
					
						
							| 
									
										
										
										
											2021-05-07 06:24:01 +08:00
										 |  |  |  | 			if kmsCtx, ok := objInfo.UserDefined[crypto.MetaContext]; ok { | 
					
						
							|  |  |  |  | 				w.Header().Set(xhttp.AmzServerSideEncryptionKmsContext, kmsCtx) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			if len(objInfo.ETag) >= 32 && strings.Count(objInfo.ETag, "-") != 1 { | 
					
						
							|  |  |  |  | 				objInfo.ETag = objInfo.ETag[len(objInfo.ETag)-32:] | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-04 07:19:08 +08:00
										 |  |  |  | 		case crypto.SSEC: | 
					
						
							| 
									
										
										
										
											2020-12-23 01:19:32 +08:00
										 |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) | 
					
						
							|  |  |  |  | 			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) | 
					
						
							| 
									
										
										
										
											2020-04-10 08:01:45 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 			if len(objInfo.ETag) >= 32 && strings.Count(objInfo.ETag, "-") != 1 { | 
					
						
							|  |  |  |  | 				objInfo.ETag = objInfo.ETag[len(objInfo.ETag)-32:] | 
					
						
							| 
									
										
										
										
											2018-09-24 01:24:10 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-02-10 07:19:30 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	if dsc := mustReplicate(ctx, bucket, object, getMustReplicateOptions(metadata, "", "", replication.ObjectReplicationType, opts)); dsc.ReplicateAny() { | 
					
						
							|  |  |  |  | 		scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	setPutObjHeaders(w, objInfo, false) | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	defer func() { | 
					
						
							|  |  |  |  | 		var data []byte | 
					
						
							|  |  |  |  | 		if buf != nil { | 
					
						
							|  |  |  |  | 			data = buf.Bytes() | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		asize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			asize = objInfo.Size | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		globalCacheConfig.Set(&cache.ObjectInfo{ | 
					
						
							|  |  |  |  | 			Key:          objInfo.Name, | 
					
						
							|  |  |  |  | 			Bucket:       objInfo.Bucket, | 
					
						
							|  |  |  |  | 			ETag:         objInfo.ETag, | 
					
						
							|  |  |  |  | 			ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  |  | 			Expires:      objInfo.ExpiresStr(), | 
					
						
							|  |  |  |  | 			CacheControl: objInfo.CacheControl, | 
					
						
							|  |  |  |  | 			Size:         asize, | 
					
						
							|  |  |  |  | 			Metadata:     cleanReservedKeys(objInfo.UserDefined), | 
					
						
							|  |  |  |  | 			Data:         data, | 
					
						
							|  |  |  |  | 		}) | 
					
						
							|  |  |  |  | 	}() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-29 13:46:19 +08:00
										 |  |  |  | 	// Notify object created event.
 | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  |  | 	evt := eventArgs{ | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  |  | 		EventName:    event.ObjectCreatedPut, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							| 
									
										
										
										
											2019-03-26 02:45:42 +08:00
										 |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	sendEvent(evt) | 
					
						
							|  |  |  |  | 	if objInfo.NumVersions > dataScannerExcessiveVersionsThreshold { | 
					
						
							|  |  |  |  | 		evt.EventName = event.ObjectManyVersions | 
					
						
							|  |  |  |  | 		sendEvent(evt) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-13 09:57:37 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 	// Do not send checksums in events to avoid leaks.
 | 
					
						
							|  |  |  |  | 	hash.TransferChecksumHeader(w, r) | 
					
						
							|  |  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:57:37 +08:00
										 |  |  |  | 	// Remove the transitioned object whose object version is being overwritten.
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	if !globalTierConfigMgr.Empty() { | 
					
						
							| 
									
										
										
										
											2021-10-02 02:58:17 +08:00
										 |  |  |  | 		// Schedule object for immediate transition if eligible.
 | 
					
						
							| 
									
										
										
										
											2022-10-22 05:40:50 +08:00
										 |  |  |  | 		objInfo.ETag = origETag | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  |  | 		enqueueTransitionImmediate(objInfo, lcEventSrc_s3PutObject) | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  |  | 		os.Sweep() | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-05-08 10:55:30 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | // PutObjectExtractHandler - PUT Object extract is an extended API
 | 
					
						
							|  |  |  |  | // based off from AWS Snowball feature to auto extract compressed
 | 
					
						
							|  |  |  |  | // stream will be extracted in the same directory it is stored in
 | 
					
						
							|  |  |  |  | // and the folder structures will be built out accordingly.
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "PutObjectExtract") | 
					
						
							|  |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// X-Amz-Copy-Source shouldn't be set for this call.
 | 
					
						
							|  |  |  |  | 	if _, ok := r.Header[xhttp.AmzCopySource]; ok { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopySource), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Validate storage class metadata if present
 | 
					
						
							|  |  |  |  | 	sc := r.Header.Get(xhttp.AmzStorageClass) | 
					
						
							|  |  |  |  | 	if sc != "" { | 
					
						
							|  |  |  |  | 		if !storageclass.IsValid(sc) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidStorageClass), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	clientETag, err := etag.FromContentMD5(r.Header) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  |  | 	// if Content-Length is unknown/missing, deny the request
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	size := r.ContentLength | 
					
						
							|  |  |  |  | 	rAuthType := getRequestAuthType(r) | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 	if rAuthType == authTypeStreamingSigned || rAuthType == authTypeStreamingSignedTrailer { | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		if sizeStr, ok := r.Header[xhttp.AmzDecodedContentLength]; ok { | 
					
						
							|  |  |  |  | 			if sizeStr[0] == "" { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			size, err = strconv.ParseInt(sizeStr[0], 10, 64) | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if size == -1 { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  |  | 	// maximum Upload size for objects in a single operation
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	if isMaxObjectSize(size) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var ( | 
					
						
							|  |  |  |  | 		md5hex              = clientETag.String() | 
					
						
							|  |  |  |  | 		sha256hex           = "" | 
					
						
							|  |  |  |  | 		reader    io.Reader = r.Body | 
					
						
							|  |  |  |  | 		s3Err     APIErrorCode | 
					
						
							|  |  |  |  | 		putObject = objectAPI.PutObject | 
					
						
							|  |  |  |  | 	) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Check if put is allowed
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	if s3Err = isPutActionAllowed(ctx, rAuthType, bucket, object, r, policy.PutObjectAction); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	switch rAuthType { | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 	case authTypeStreamingSigned, authTypeStreamingSignedTrailer: | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		// Initialize stream signature verifier.
 | 
					
						
							| 
									
										
										
										
											2023-05-06 10:53:12 +08:00
										 |  |  |  | 		reader, s3Err = newSignV4ChunkedReader(r, rAuthType == authTypeStreamingSignedTrailer) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	case authTypeSignedV2, authTypePresignedV2: | 
					
						
							|  |  |  |  | 		s3Err = isReqAuthenticatedV2(r) | 
					
						
							|  |  |  |  | 		if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	case authTypePresigned, authTypeSigned: | 
					
						
							| 
									
										
										
										
											2021-11-26 05:06:25 +08:00
										 |  |  |  | 		if s3Err = reqSignatureV4Verify(r, globalSite.Region, serviceS3); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if !skipContentSha256Cksum(r) { | 
					
						
							|  |  |  |  | 			sha256hex = getContentSha256Cksum(r, serviceS3) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 	hreader, err := hash.NewReader(ctx, reader, size, md5hex, sha256hex, size) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-25 08:44:15 +08:00
										 |  |  |  | 	if err = hreader.AddChecksum(r, false); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidChecksum), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-01 03:07:04 +08:00
										 |  |  |  | 	if err := enforceBucketQuotaHard(ctx, bucket, size); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Check if bucket encryption is enabled
 | 
					
						
							| 
									
										
										
										
											2021-05-14 15:59:05 +08:00
										 |  |  |  | 	sseConfig, _ := globalBucketSSEConfigSys.Get(bucket) | 
					
						
							| 
									
										
										
										
											2021-09-22 00:02:15 +08:00
										 |  |  |  | 	sseConfig.Apply(r.Header, sse.ApplyOptions{ | 
					
						
							|  |  |  |  | 		AutoEncrypt: globalAutoEncryption, | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 	retPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.PutObjectRetentionAction) | 
					
						
							|  |  |  |  | 	holdPerms := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.PutObjectLegalHoldAction) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 23:36:44 +08:00
										 |  |  |  | 	// These are static for all objects extracted.
 | 
					
						
							|  |  |  |  | 	reqParams := extractReqParams(r) | 
					
						
							|  |  |  |  | 	respElements := map[string]string{ | 
					
						
							|  |  |  |  | 		"requestId": w.Header().Get(xhttp.AmzRequestID), | 
					
						
							|  |  |  |  | 		"nodeId":    w.Header().Get(xhttp.AmzRequestHostID), | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if sc == "" { | 
					
						
							|  |  |  |  | 		sc = storageclass.STANDARD | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 	putObjectTar := func(reader io.Reader, info os.FileInfo, object string) error { | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		size := info.Size() | 
					
						
							| 
									
										
										
										
											2023-04-18 03:16:54 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 08:44:07 +08:00
										 |  |  |  | 		metadata := map[string]string{ | 
					
						
							|  |  |  |  | 			xhttp.AmzStorageClass: sc, // save same storage-class as incoming stream.
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		actualSize := size | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 		var idxCb func() []byte | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if isCompressible(r.Header, object) && size > minCompressibleSize { | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			// Storing the compression metadata.
 | 
					
						
							|  |  |  |  | 			metadata[ReservedMetadataPrefix+"compression"] = compressionAlgorithmV2 | 
					
						
							|  |  |  |  | 			metadata[ReservedMetadataPrefix+"actual-size"] = strconv.FormatInt(size, 10) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 			actualReader, err := hash.NewReader(ctx, reader, size, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 				return err | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 			// Set compression metrics.
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			wantEncryption := crypto.Requested(r.Header) | 
					
						
							| 
									
										
										
										
											2022-07-13 22:52:15 +08:00
										 |  |  |  | 			s2c, cb := newS2CompressReader(actualReader, actualSize, wantEncryption) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			defer s2c.Close() | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 			idxCb = cb | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			reader = etag.Wrap(s2c, actualReader) | 
					
						
							|  |  |  |  | 			size = -1 // Since compressed size is un-predictable.
 | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 		hashReader, err := hash.NewReader(ctx, reader, size, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		rawReader := hashReader | 
					
						
							|  |  |  |  | 		pReader := NewPutObjReader(rawReader) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		if r.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() { | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  |  | 			if s3Err = isPutActionAllowed(ctx, getRequestAuthType(r), bucket, object, r, policy.ReplicateObjectAction); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 				return err | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 			metadata[ReservedMetadataPrefixLower+ReplicaStatus] = replication.Replica.String() | 
					
						
							|  |  |  |  | 			metadata[ReservedMetadataPrefixLower+ReplicaTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 		var ( | 
					
						
							|  |  |  |  | 			versionID string | 
					
						
							|  |  |  |  | 			hdrs      http.Header | 
					
						
							|  |  |  |  | 		) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if tarHdrs, ok := info.Sys().(*tar.Header); ok && len(tarHdrs.PAXRecords) > 0 { | 
					
						
							|  |  |  |  | 			versionID = tarHdrs.PAXRecords["minio.versionId"] | 
					
						
							|  |  |  |  | 			hdrs = make(http.Header) | 
					
						
							|  |  |  |  | 			for k, v := range tarHdrs.PAXRecords { | 
					
						
							|  |  |  |  | 				if k == "minio.versionId" { | 
					
						
							|  |  |  |  | 					continue | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				if strings.HasPrefix(k, "minio.metadata.") { | 
					
						
							|  |  |  |  | 					k = strings.TrimPrefix(k, "minio.metadata.") | 
					
						
							|  |  |  |  | 					hdrs.Set(k, v) | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			m, err := extractMetadata(ctx, textproto.MIMEHeader(hdrs)) | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			for k, v := range m { | 
					
						
							|  |  |  |  | 				metadata[k] = v | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							|  |  |  |  | 			versionID = r.Form.Get(xhttp.VersionID) | 
					
						
							|  |  |  |  | 			hdrs = r.Header | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		opts, err := putOpts(ctx, bucket, object, versionID, hdrs, metadata) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		opts.MTime = info.ModTime() | 
					
						
							| 
									
										
										
										
											2023-05-04 22:29:33 +08:00
										 |  |  |  | 		if opts.MTime.Unix() <= 0 { | 
					
						
							|  |  |  |  | 			opts.MTime = UTCNow() | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 		opts.IndexCB = idxCb | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 		retentionMode, retentionDate, legalHold, s3err := checkPutObjectLockAllowed(ctx, r, bucket, object, getObjectInfo, retPerms, holdPerms) | 
					
						
							|  |  |  |  | 		if s3err == ErrNone && retentionMode.Valid() { | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			metadata[strings.ToLower(xhttp.AmzObjectLockMode)] = string(retentionMode) | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 			metadata[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = amztime.ISO8601Format(retentionDate.UTC()) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 		if s3err == ErrNone && legalHold.Status.Valid() { | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			metadata[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = string(legalHold.Status) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 		if s3err != ErrNone { | 
					
						
							|  |  |  |  | 			s3Err = s3err | 
					
						
							|  |  |  |  | 			return ObjectLocked{} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		if dsc := mustReplicate(ctx, bucket, object, getMustReplicateOptions(metadata, "", "", replication.ObjectReplicationType, opts)); dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 			metadata[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 			metadata[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		var objectEncryptionKey crypto.ObjectKey | 
					
						
							| 
									
										
										
										
											2023-01-25 07:46:33 +08:00
										 |  |  |  | 		if crypto.Requested(r.Header) { | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			if crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  |  | 				return errInvalidEncryptionParameters | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 07:46:33 +08:00
										 |  |  |  | 			if crypto.SSEC.IsRequested(r.Header) && isReplicationEnabled(ctx, bucket) { | 
					
						
							|  |  |  |  | 				return errInvalidEncryptionParametersSSEC | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			reader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata) | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			wantSize := int64(-1) | 
					
						
							|  |  |  |  | 			if size >= 0 { | 
					
						
							|  |  |  |  | 				info := ObjectInfo{Size: size} | 
					
						
							|  |  |  |  | 				wantSize = info.EncryptedSize() | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			// do not try to verify encrypted content
 | 
					
						
							| 
									
										
										
										
											2023-09-19 01:00:54 +08:00
										 |  |  |  | 			hashReader, err = hash.NewReader(ctx, etag.Wrap(reader, hashReader), wantSize, "", "", actualSize) | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 			pReader, err = pReader.WithEncryption(hashReader, &objectEncryptionKey) | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							| 
									
										
										
										
											2022-07-12 08:30:56 +08:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 		if opts.IndexCB != nil { | 
					
						
							|  |  |  |  | 			opts.IndexCB = compressionIndexEncrypter(objectEncryptionKey, opts.IndexCB) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		// Ensure that metadata does not contain sensitive information
 | 
					
						
							|  |  |  |  | 		crypto.RemoveSensitiveEntries(metadata) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 08:33:11 +08:00
										 |  |  |  | 		os := newObjSweeper(bucket, object).WithVersioning(opts.Versioned, opts.VersionSuspended) | 
					
						
							|  |  |  |  | 		if !globalTierConfigMgr.Empty() { | 
					
						
							|  |  |  |  | 			// Get appropriate object info to identify the remote object to delete
 | 
					
						
							|  |  |  |  | 			goiOpts := os.GetOpts() | 
					
						
							|  |  |  |  | 			if goi, gerr := getObjectInfo(ctx, bucket, object, goiOpts); gerr == nil { | 
					
						
							|  |  |  |  | 				os.SetTransitionState(goi.TransitionedObject) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		// Create the object..
 | 
					
						
							|  |  |  |  | 		objInfo, err := putObject(ctx, bucket, object, pReader, opts) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 08:33:11 +08:00
										 |  |  |  | 		origETag := objInfo.ETag | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 		objInfo.ETag = getDecryptedETag(r.Header, objInfo, false) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		if dsc := mustReplicate(ctx, bucket, object, getMustReplicateOptions(metadata, "", "", replication.ObjectReplicationType, opts)); dsc.ReplicateAny() { | 
					
						
							|  |  |  |  | 			scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-31 23:33:51 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 		asize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			asize = objInfo.Size | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		defer globalCacheConfig.Set(&cache.ObjectInfo{ | 
					
						
							|  |  |  |  | 			Key:          objInfo.Name, | 
					
						
							|  |  |  |  | 			Bucket:       objInfo.Bucket, | 
					
						
							|  |  |  |  | 			ETag:         objInfo.ETag, | 
					
						
							|  |  |  |  | 			ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  |  | 			Expires:      objInfo.ExpiresStr(), | 
					
						
							|  |  |  |  | 			CacheControl: objInfo.CacheControl, | 
					
						
							|  |  |  |  | 			Size:         asize, | 
					
						
							|  |  |  |  | 			Metadata:     cleanReservedKeys(objInfo.UserDefined), | 
					
						
							|  |  |  |  | 		}) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 23:33:51 +08:00
										 |  |  |  | 		// Notify object created event.
 | 
					
						
							|  |  |  |  | 		evt := eventArgs{ | 
					
						
							|  |  |  |  | 			EventName:    event.ObjectCreatedPut, | 
					
						
							|  |  |  |  | 			BucketName:   bucket, | 
					
						
							|  |  |  |  | 			Object:       objInfo, | 
					
						
							| 
									
										
										
										
											2023-10-10 23:36:44 +08:00
										 |  |  |  | 			ReqParams:    reqParams, | 
					
						
							|  |  |  |  | 			RespElements: respElements, | 
					
						
							| 
									
										
										
										
											2023-07-31 23:33:51 +08:00
										 |  |  |  | 			UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 			Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		sendEvent(evt) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 08:33:11 +08:00
										 |  |  |  | 		// Remove the transitioned object whose object version is being overwritten.
 | 
					
						
							|  |  |  |  | 		if !globalTierConfigMgr.Empty() { | 
					
						
							|  |  |  |  | 			objInfo.ETag = origETag | 
					
						
							|  |  |  |  | 			// Schedule object for immediate transition if eligible.
 | 
					
						
							|  |  |  |  | 			enqueueTransitionImmediate(objInfo, lcEventSrc_s3PutObject) | 
					
						
							|  |  |  |  | 			os.Sweep() | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-17 22:07:47 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 04:50:21 +08:00
										 |  |  |  | 	var opts untarOptions | 
					
						
							|  |  |  |  | 	opts.ignoreDirs = strings.EqualFold(r.Header.Get(xhttp.MinIOSnowballIgnoreDirs), "true") | 
					
						
							|  |  |  |  | 	opts.ignoreErrs = strings.EqualFold(r.Header.Get(xhttp.MinIOSnowballIgnoreErrors), "true") | 
					
						
							|  |  |  |  | 	opts.prefixAll = r.Header.Get(xhttp.MinIOSnowballPrefix) | 
					
						
							|  |  |  |  | 	if opts.prefixAll != "" { | 
					
						
							|  |  |  |  | 		opts.prefixAll = trimLeadingSlash(pathJoin(opts.prefixAll, slashSeparator)) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 04:50:21 +08:00
										 |  |  |  | 	if err = untar(ctx, hreader, putObjectTar, opts); err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-07 01:45:23 +08:00
										 |  |  |  | 		apiErr := errorCodes.ToAPIErr(s3Err) | 
					
						
							|  |  |  |  | 		// If not set, convert or use BadRequest
 | 
					
						
							|  |  |  |  | 		if s3Err == ErrNone { | 
					
						
							|  |  |  |  | 			apiErr = toAPIError(ctx, err) | 
					
						
							|  |  |  |  | 			if apiErr.Code == "InternalError" { | 
					
						
							|  |  |  |  | 				// Convert generic internal errors to bad requests.
 | 
					
						
							|  |  |  |  | 				apiErr = APIError{ | 
					
						
							|  |  |  |  | 					Code:           "BadRequest", | 
					
						
							|  |  |  |  | 					Description:    err.Error(), | 
					
						
							|  |  |  |  | 					HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, apiErr, r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	w.Header()[xhttp.ETag] = []string{`"` + hex.EncodeToString(hreader.MD5Current()) + `"`} | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  |  | 	hash.TransferChecksumHeader(w, r) | 
					
						
							| 
									
										
										
										
											2021-03-27 08:15:09 +08:00
										 |  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  |  | // Delete objectAPIHandlers
 | 
					
						
							| 
									
										
										
										
											2015-06-09 02:06:06 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 08:43:48 +08:00
										 |  |  |  | // DeleteObjectHandler - delete an object
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  |  | func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-07-21 09:46:32 +08:00
										 |  |  |  | 	ctx := newContext(r, w, "DeleteObject") | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2016-08-11 09:47:49 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.DeleteObjectAction, bucket, object); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
											
												accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
   "*":                             true,
   "s3:*":                          true,
   "s3:GetObject":                  true,
   "s3:ListBucket":                 true,
   "s3:PutObject":                  true,
   "s3:CreateBucket":               true,
   "s3:GetBucketLocation":          true,
   "s3:DeleteBucket":               true,
   "s3:DeleteObject":               true,
   "s3:AbortMultipartUpload":       true,
   "s3:ListBucketMultipartUploads": true,
   "s3:ListMultipartUploadParts":   true,
following conditions for "StringEquals" and "StringNotEquals"
   "s3:prefix", "s3:max-keys"
											
										 
											2016-02-04 08:46:56 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-02-05 04:52:25 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-11 11:09:20 +08:00
										 |  |  |  | 	if _, ok := r.Header[xhttp.MinIOSourceReplicationCheck]; ok { | 
					
						
							|  |  |  |  | 		// requests to just validate replication settings and permissions are not allowed to delete data
 | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrReplicationPermissionCheckError), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-28 00:45:50 +08:00
										 |  |  |  | 	replica := r.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() | 
					
						
							|  |  |  |  | 	if replica { | 
					
						
							|  |  |  |  | 		if s3Error := checkRequestAuthType(ctx, r, policy.ReplicateDeleteAction, bucket, object); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-01 13:35:43 +08:00
										 |  |  |  | 	if globalDNSConfig != nil { | 
					
						
							|  |  |  |  | 		_, err := globalDNSConfig.Get(bucket) | 
					
						
							| 
									
										
										
										
											2020-09-10 03:20:49 +08:00
										 |  |  |  | 		if err != nil && err != dns.ErrNotImplemented { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-07-01 13:35:43 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 22:54:27 +08:00
										 |  |  |  | 	opts, err := delOpts(ctx, r, bucket, object) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-28 00:45:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	rcfg, _ := globalBucketObjectLockSys.Get(bucket) | 
					
						
							|  |  |  |  | 	if rcfg.LockEnabled && opts.DeletePrefix { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, errors.New("force-delete is forbidden in a locked-enabled bucket")), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	os := newObjSweeper(bucket, object).WithVersion(opts.VersionID).WithVersioning(opts.Versioned, opts.VersionSuspended) | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	opts.SetEvalMetadataFn(func(oi *ObjectInfo, gerr error) (dsc ReplicateDecision, err error) { | 
					
						
							|  |  |  |  | 		if replica { // no need to check replication on receiver
 | 
					
						
							|  |  |  |  | 			return dsc, nil | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		dsc = checkReplicateDelete(ctx, bucket, ObjectToDelete{ | 
					
						
							|  |  |  |  | 			ObjectV: ObjectV{ | 
					
						
							|  |  |  |  | 				ObjectName: object, | 
					
						
							|  |  |  |  | 				VersionID:  opts.VersionID, | 
					
						
							|  |  |  |  | 			}, | 
					
						
							|  |  |  |  | 		}, *oi, opts, gerr) | 
					
						
							|  |  |  |  | 		// Mutations of objects on versioning suspended buckets
 | 
					
						
							|  |  |  |  | 		// affect its null version. Through opts below we select
 | 
					
						
							|  |  |  |  | 		// the null version's remote object to delete if
 | 
					
						
							|  |  |  |  | 		// transitioned.
 | 
					
						
							|  |  |  |  | 		if gerr == nil { | 
					
						
							|  |  |  |  | 			os.SetTransitionState(oi.TransitionedObject) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return dsc, nil | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-06-28 00:45:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vID := opts.VersionID | 
					
						
							|  |  |  |  | 	if replica { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		opts.SetReplicaStatus(replication.Replica) | 
					
						
							|  |  |  |  | 		if opts.VersionPurgeStatus().Empty() { | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  |  | 			// opts.VersionID holds delete marker version ID to replicate and not yet present on disk
 | 
					
						
							|  |  |  |  | 			vID = "" | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 	opts.SetEvalRetentionBypassFn(func(goi ObjectInfo, gerr error) (err error) { | 
					
						
							|  |  |  |  | 		err = nil | 
					
						
							|  |  |  |  | 		if vID != "" { | 
					
						
							|  |  |  |  | 			err := enforceRetentionBypassForDelete(ctx, r, bucket, ObjectToDelete{ | 
					
						
							|  |  |  |  | 				ObjectV: ObjectV{ | 
					
						
							|  |  |  |  | 					ObjectName: object, | 
					
						
							|  |  |  |  | 					VersionID:  vID, | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 			}, goi, gerr) | 
					
						
							|  |  |  |  | 			if err != nil && !isErrObjectNotFound(err) { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-06-16 09:43:14 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 14:00:42 +08:00
										 |  |  |  | 	deleteObject := objectAPI.DeleteObject | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 	// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
 | 
					
						
							| 
									
										
										
										
											2021-02-11 14:00:42 +08:00
										 |  |  |  | 	objInfo, err := deleteObject(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-03-07 00:56:10 +08:00
										 |  |  |  | 		if _, ok := err.(BucketNotFound); ok { | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 			// When bucket doesn't exist specially handle it.
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-09-10 09:23:08 +08:00
										 |  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-09-27 05:02:56 +08:00
										 |  |  |  | 		if isErrObjectNotFound(err) || isErrVersionNotFound(err) { | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 			writeSuccessNoContent(w) | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-13 03:43:04 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 14:00:42 +08:00
										 |  |  |  | 	if objInfo.Name == "" { | 
					
						
							|  |  |  |  | 		writeSuccessNoContent(w) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 05:46:17 +08:00
										 |  |  |  | 	defer globalCacheConfig.Delete(bucket, object) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 14:00:42 +08:00
										 |  |  |  | 	setPutObjHeaders(w, objInfo, true) | 
					
						
							|  |  |  |  | 	writeSuccessNoContent(w) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	eventName := event.ObjectRemovedDelete | 
					
						
							|  |  |  |  | 	if objInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 		eventName = event.ObjectRemovedDeleteMarkerCreated | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Notify object deleted event.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    eventName, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 	if objInfo.ReplicationStatus == replication.Pending || objInfo.VersionPurgeStatus == Pending { | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  |  | 		dmVersionID := "" | 
					
						
							|  |  |  |  | 		versionID := "" | 
					
						
							|  |  |  |  | 		if objInfo.DeleteMarker { | 
					
						
							|  |  |  |  | 			dmVersionID = objInfo.VersionID | 
					
						
							|  |  |  |  | 		} else { | 
					
						
							|  |  |  |  | 			versionID = objInfo.VersionID | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  |  | 		dobj := DeletedObjectReplicationInfo{ | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  |  | 			DeletedObject: DeletedObject{ | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 				ObjectName:            object, | 
					
						
							|  |  |  |  | 				VersionID:             versionID, | 
					
						
							|  |  |  |  | 				DeleteMarkerVersionID: dmVersionID, | 
					
						
							|  |  |  |  | 				DeleteMarkerMTime:     DeleteMarkerMTime{objInfo.ModTime}, | 
					
						
							|  |  |  |  | 				DeleteMarker:          objInfo.DeleteMarker, | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 				ReplicationState:      objInfo.ReplicationState(), | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-07-13 01:43:32 +08:00
										 |  |  |  | 			Bucket:    bucket, | 
					
						
							|  |  |  |  | 			EventType: ReplicateIncomingDelete, | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		scheduleReplicationDelete(ctx, dobj, objectAPI) | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-29 13:15:45 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 	// Remove the transitioned object whose object version is being overwritten.
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	if !globalTierConfigMgr.Empty() { | 
					
						
							|  |  |  |  | 		os.Sweep() | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-09 02:06:06 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | // PutObjectLegalHoldHandler - set legal hold configuration to object,
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "PutObjectLegalHold") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	// Check permissions to perform this legal hold operation
 | 
					
						
							| 
									
										
										
										
											2020-03-04 23:04:12 +08:00
										 |  |  |  | 	if s3Err := checkRequestAuthType(ctx, r, policy.PutObjectLegalHoldAction, bucket, object); s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-04 23:04:12 +08:00
										 |  |  |  | 	if !hasContentMD5(r.Header) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-16 14:07:52 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  |  | 	if rcfg, _ := globalBucketObjectLockSys.Get(bucket); !rcfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-06 03:36:26 +08:00
										 |  |  |  | 	legalHold, err := objectlock.ParseObjectLegalHold(io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-12-06 03:36:26 +08:00
										 |  |  |  | 		apiErr := errorCodes.ToAPIErr(ErrMalformedXML) | 
					
						
							|  |  |  |  | 		apiErr.Description = err.Error() | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, apiErr, r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 	popts := ObjectOptions{ | 
					
						
							|  |  |  |  | 		MTime:     opts.MTime, | 
					
						
							|  |  |  |  | 		VersionID: opts.VersionID, | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 		EvalMetadataFn: func(oi *ObjectInfo, gerr error) (ReplicateDecision, error) { | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 			oi.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = strings.ToUpper(string(legalHold.Status)) | 
					
						
							|  |  |  |  | 			oi.UserDefined[ReservedMetadataPrefixLower+ObjectLockLegalHoldTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 			dsc := mustReplicate(ctx, bucket, object, oi.getMustReplicateOptions(replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 			if dsc.ReplicateAny() { | 
					
						
							|  |  |  |  | 				oi.UserDefined[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 				oi.UserDefined[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 			return dsc, nil | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 		}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objInfo, err := objectAPI.PutObjectMetadata(ctx, bucket, object, popts) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	dsc := mustReplicate(ctx, bucket, object, objInfo.getMustReplicateOptions(replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.MetadataReplicationType) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-19 10:45:48 +08:00
										 |  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Notify object event.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectCreatedPutLegalHold, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // GetObjectLegalHoldHandler - get legal hold configuration to object,
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "GetObjectLegalHold") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectLegalHoldAction, bucket, object); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  |  | 	if rcfg, _ := globalBucketObjectLockSys.Get(bucket); !rcfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	objInfo, err := getObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	legalHold := objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2020-02-19 10:45:48 +08:00
										 |  |  |  | 	if legalHold.IsEmpty() { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNoSuchObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-19 10:45:48 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	writeSuccessResponseXML(w, encodeResponse(legalHold)) | 
					
						
							|  |  |  |  | 	// Notify object legal hold accessed via a GET request.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectAccessedGetLegalHold, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | // PutObjectRetentionHandler - set object hold configuration to object,
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "PutObjectRetention") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  |  | 	cred, owner, s3Err := validateSignature(getRequestAuthType(r), r) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 	if s3Err != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-04 23:04:12 +08:00
										 |  |  |  | 	if !hasContentMD5(r.Header) { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-16 14:07:52 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  |  | 	if rcfg, _ := globalBucketObjectLockSys.Get(bucket); !rcfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	objRetention, err := objectlock.ParseObjectRetention(r.Body) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		apiErr := errorCodes.ToAPIErr(ErrMalformedXML) | 
					
						
							|  |  |  |  | 		apiErr.Description = err.Error() | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, apiErr, r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 	popts := ObjectOptions{ | 
					
						
							|  |  |  |  | 		MTime:     opts.MTime, | 
					
						
							|  |  |  |  | 		VersionID: opts.VersionID, | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 		EvalMetadataFn: func(oi *ObjectInfo, gerr error) (dsc ReplicateDecision, err error) { | 
					
						
							| 
									
										
										
										
											2022-12-03 00:35:04 +08:00
										 |  |  |  | 			if err := enforceRetentionBypassForPut(ctx, r, *oi, objRetention, cred, owner); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 				return dsc, err | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 			} | 
					
						
							|  |  |  |  | 			if objRetention.Mode.Valid() { | 
					
						
							|  |  |  |  | 				oi.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = string(objRetention.Mode) | 
					
						
							| 
									
										
										
										
											2022-12-13 02:28:30 +08:00
										 |  |  |  | 				oi.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = amztime.ISO8601Format(objRetention.RetainUntilDate.UTC()) | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 			} else { | 
					
						
							|  |  |  |  | 				oi.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = "" | 
					
						
							|  |  |  |  | 				oi.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = "" | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			oi.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 			dsc = mustReplicate(ctx, bucket, object, oi.getMustReplicateOptions(replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 			if dsc.ReplicateAny() { | 
					
						
							|  |  |  |  | 				oi.UserDefined[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 				oi.UserDefined[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-07-10 22:57:56 +08:00
										 |  |  |  | 			return dsc, nil | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 23:22:04 +08:00
										 |  |  |  | 	objInfo, err := objectAPI.PutObjectMetadata(ctx, bucket, object, popts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-04-05 04:32:31 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	dsc := mustReplicate(ctx, bucket, object, objInfo.getMustReplicateOptions(replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.MetadataReplicationType) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-13 04:22:07 +08:00
										 |  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	// Notify object  event.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectCreatedPutRetention, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | // GetObjectRetentionHandler - get object retention configuration of object,
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | func (api objectAPIHandlers) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "GetObjectRetention") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectRetentionAction, bucket, object); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-14 15:01:53 +08:00
										 |  |  |  | 	if rcfg, _ := globalBucketObjectLockSys.Get(bucket); !rcfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2021-04-14 15:01:53 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	objInfo, err := getObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  |  | 	retention := objectlock.GetObjectRetentionMeta(objInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2020-07-01 07:44:24 +08:00
										 |  |  |  | 	if !retention.Mode.Valid() { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNoSuchObjectLockConfiguration), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-01 07:44:24 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  |  | 	writeSuccessResponseXML(w, encodeResponse(retention)) | 
					
						
							|  |  |  |  | 	// Notify object retention accessed via a GET request.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectAccessedGetRetention, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 08:04:11 +08:00
										 |  |  |  | // ObjectTagSet key value tags
 | 
					
						
							|  |  |  |  | type ObjectTagSet struct { | 
					
						
							|  |  |  |  | 	Tags []tags.Tag `xml:"Tag"` | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | type objectTagging struct { | 
					
						
							|  |  |  |  | 	XMLName xml.Name      `xml:"Tagging"` | 
					
						
							|  |  |  |  | 	TagSet  *ObjectTagSet `xml:"TagSet"` | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | // GetObjectTaggingHandler - GET object tagging
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "GetObjectTagging") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-13 04:22:07 +08:00
										 |  |  |  | 	if s3Error := authenticateRequest(ctx, r, policy.GetObjectTaggingAction); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 08:04:11 +08:00
										 |  |  |  | 	ot, err := objAPI.GetObjectTags(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-13 04:22:07 +08:00
										 |  |  |  | 	// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 	if tags := ot.String(); tags != "" { | 
					
						
							|  |  |  |  | 		r.Header.Set(xhttp.AmzObjectTagging, tags) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if s3Error := authorizeRequest(ctx, r, policy.GetObjectTaggingAction); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 04:52:33 +08:00
										 |  |  |  | 	if opts.VersionID != "" && opts.VersionID != nullVersionID { | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 		w.Header()[xhttp.AmzVersionID] = []string{opts.VersionID} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 08:04:11 +08:00
										 |  |  |  | 	otags := &objectTagging{ | 
					
						
							|  |  |  |  | 		TagSet: &ObjectTagSet{}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var list []tags.Tag | 
					
						
							|  |  |  |  | 	for k, v := range ot.ToMap() { | 
					
						
							|  |  |  |  | 		list = append(list, tags.Tag{ | 
					
						
							|  |  |  |  | 			Key:   k, | 
					
						
							|  |  |  |  | 			Value: v, | 
					
						
							|  |  |  |  | 		}) | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 04:22:07 +08:00
										 |  |  |  | 	// Always return in sorted order for tags.
 | 
					
						
							|  |  |  |  | 	sort.Slice(list, func(i, j int) bool { | 
					
						
							|  |  |  |  | 		return list[i].Key < list[j].Key | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-08-24 08:04:11 +08:00
										 |  |  |  | 	otags.TagSet.Tags = list | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	writeSuccessResponseXML(w, encodeResponse(otags)) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // PutObjectTaggingHandler - PUT object tagging
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "PutObjectTagging") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	// Tags XML will not be bigger than 1MiB in size, fail if its bigger.
 | 
					
						
							|  |  |  |  | 	tags, err := tags.ParseObjectXML(io.LimitReader(r.Body, 1<<20)) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 	// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 	r.Header.Set(xhttp.AmzObjectTagging, tags.String()) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Allow putObjectTagging if policy action is set
 | 
					
						
							|  |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.PutObjectTaggingAction, bucket, object); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:20:45 +08:00
										 |  |  |  | 	objInfo, err := objAPI.GetObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-05-14 10:20:45 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  |  | 	tagsStr := tags.String() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	dsc := mustReplicate(ctx, bucket, object, getMustReplicateOptions(objInfo.UserDefined, tagsStr, objInfo.ReplicationStatus, replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 		opts.UserDefined = make(map[string]string) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		opts.UserDefined[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		opts.UserDefined[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							|  |  |  |  | 		opts.UserDefined[ReservedMetadataPrefixLower+TaggingTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	// Put object tags
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:20:45 +08:00
										 |  |  |  | 	objInfo, err = objAPI.PutObjectTags(ctx, bucket, object, tagsStr, opts) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		scheduleReplication(ctx, objInfo, objAPI, dsc, replication.MetadataReplicationType) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 04:52:33 +08:00
										 |  |  |  | 	if objInfo.VersionID != "" && objInfo.VersionID != nullVersionID { | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  |  | 		w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID} | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectCreatedPutTagging, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // DeleteObjectTaggingHandler - DELETE object tagging
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "DeleteObjectTagging") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	objAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	oi, err := objAPI.GetObjectInfo(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-29 02:25:46 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if userTags := oi.UserTags; userTags != "" { | 
					
						
							|  |  |  |  | 		// Set this such that authorization policies can be applied on the object tags.
 | 
					
						
							|  |  |  |  | 		r.Header.Set(xhttp.AmzObjectTagging, oi.UserTags) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Allow deleteObjectTagging if policy action is set
 | 
					
						
							|  |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.DeleteObjectTaggingAction, bucket, object); s3Error != ErrNone { | 
					
						
							|  |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							|  |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 	dsc := mustReplicate(ctx, bucket, object, oi.getMustReplicateOptions(replication.MetadataReplicationType, opts)) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 		opts.UserDefined = make(map[string]string) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 		opts.UserDefined[ReservedMetadataPrefixLower+ReplicationTimestamp] = UTCNow().Format(time.RFC3339Nano) | 
					
						
							|  |  |  |  | 		opts.UserDefined[ReservedMetadataPrefixLower+ReplicationStatus] = dsc.PendingStatus() | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  |  | 	oi, err = objAPI.DeleteObjectTags(ctx, bucket, object, opts) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-17 13:38:58 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  |  | 	if dsc.ReplicateAny() { | 
					
						
							| 
									
										
										
										
											2023-09-16 17:28:06 +08:00
										 |  |  |  | 		scheduleReplication(ctx, oi, objAPI, dsc, replication.MetadataReplicationType) | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 04:52:33 +08:00
										 |  |  |  | 	if oi.VersionID != "" && oi.VersionID != nullVersionID { | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  |  | 		w.Header()[xhttp.AmzVersionID] = []string{oi.VersionID} | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-17 14:21:24 +08:00
										 |  |  |  | 	writeSuccessNoContent(w) | 
					
						
							| 
									
										
										
										
											2021-02-02 05:52:51 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							|  |  |  |  | 		EventName:    event.ObjectCreatedDeleteTagging, | 
					
						
							|  |  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  |  | 		Object:       oi, | 
					
						
							|  |  |  |  | 		ReqParams:    extractReqParams(r), | 
					
						
							|  |  |  |  | 		RespElements: extractRespElements(w), | 
					
						
							|  |  |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:         handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | // RestoreObjectHandler - POST restore object handler.
 | 
					
						
							|  |  |  |  | // ----------
 | 
					
						
							|  |  |  |  | func (api objectAPIHandlers) PostRestoreObjectHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  |  | 	ctx := newContext(r, w, "PostRestoreObject") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  |  | 	object, err := unescapePath(vars["object"]) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Fetch object stat info.
 | 
					
						
							|  |  |  |  | 	objectAPI := api.ObjectAPI() | 
					
						
							|  |  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	getObjectInfo := objectAPI.GetObjectInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// Check for auth type to return S3 compatible error.
 | 
					
						
							|  |  |  |  | 	if s3Error := checkRequestAuthType(ctx, r, policy.RestoreObjectAction, bucket, object); s3Error != ErrNone { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if r.ContentLength <= 0 { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrEmptyRequestBody), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 	opts, err := postRestoreOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 	objInfo, err := getObjectInfo(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:50:00 +08:00
										 |  |  |  | 	if objInfo.TransitionedObject.Status != lifecycle.TransitionComplete { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidObjectState), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	rreq, err := parseRestoreRequest(io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		apiErr := errorCodes.ToAPIErr(ErrMalformedXML) | 
					
						
							|  |  |  |  | 		apiErr.Description = err.Error() | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, apiErr, r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	// validate the request
 | 
					
						
							|  |  |  |  | 	if err := rreq.validate(ctx, objectAPI); err != nil { | 
					
						
							|  |  |  |  | 		apiErr := errorCodes.ToAPIErr(ErrMalformedXML) | 
					
						
							|  |  |  |  | 		apiErr.Description = err.Error() | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 		writeErrorResponse(ctx, w, apiErr, r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		return | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	statusCode := http.StatusOK | 
					
						
							|  |  |  |  | 	alreadyRestored := false | 
					
						
							|  |  |  |  | 	if err == nil { | 
					
						
							|  |  |  |  | 		if objInfo.RestoreOngoing && rreq.Type != SelectRestoreRequest { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrObjectRestoreAlreadyInProgress), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if !objInfo.RestoreOngoing && !objInfo.RestoreExpires.IsZero() { | 
					
						
							|  |  |  |  | 			statusCode = http.StatusAccepted | 
					
						
							|  |  |  |  | 			alreadyRestored = true | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	// set or upgrade restore expiry
 | 
					
						
							| 
									
										
										
										
											2023-01-31 10:23:45 +08:00
										 |  |  |  | 	restoreExpiry := lifecycle.ExpectedExpiryTime(time.Now().UTC(), rreq.Days) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 	metadata := cloneMSS(objInfo.UserDefined) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// update self with restore metadata
 | 
					
						
							|  |  |  |  | 	if rreq.Type != SelectRestoreRequest { | 
					
						
							|  |  |  |  | 		objInfo.metadataOnly = true // Perform only metadata updates.
 | 
					
						
							|  |  |  |  | 		metadata[xhttp.AmzRestoreExpiryDays] = strconv.Itoa(rreq.Days) | 
					
						
							|  |  |  |  | 		metadata[xhttp.AmzRestoreRequestDate] = time.Now().UTC().Format(http.TimeFormat) | 
					
						
							|  |  |  |  | 		if alreadyRestored { | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 			metadata[xhttp.AmzRestore] = completedRestoreObj(restoreExpiry).String() | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 			metadata[xhttp.AmzRestore] = ongoingRestoreObj().String() | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 		objInfo.UserDefined = metadata | 
					
						
							|  |  |  |  | 		if _, err := objectAPI.CopyObject(GlobalContext, bucket, object, bucket, object, objInfo, ObjectOptions{ | 
					
						
							|  |  |  |  | 			VersionID: objInfo.VersionID, | 
					
						
							|  |  |  |  | 		}, ObjectOptions{ | 
					
						
							|  |  |  |  | 			VersionID: objInfo.VersionID, | 
					
						
							|  |  |  |  | 		}); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidObjectState), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		// for previously restored object, just update the restore expiry
 | 
					
						
							|  |  |  |  | 		if alreadyRestored { | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	restoreObject := mustGetUUID() | 
					
						
							|  |  |  |  | 	if rreq.OutputLocation.S3.BucketName != "" { | 
					
						
							|  |  |  |  | 		w.Header()[xhttp.AmzRestoreOutputPath] = []string{pathJoin(rreq.OutputLocation.S3.BucketName, rreq.OutputLocation.S3.Prefix, restoreObject)} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	w.WriteHeader(statusCode) | 
					
						
							|  |  |  |  | 	// Notify object restore started via a POST request.
 | 
					
						
							|  |  |  |  | 	sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2022-12-14 21:12:07 +08:00
										 |  |  |  | 		EventName:  event.ObjectRestorePost, | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 		BucketName: bucket, | 
					
						
							|  |  |  |  | 		Object:     objInfo, | 
					
						
							|  |  |  |  | 		ReqParams:  extractReqParams(r), | 
					
						
							|  |  |  |  | 		UserAgent:  r.UserAgent(), | 
					
						
							|  |  |  |  | 		Host:       handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 	}) | 
					
						
							|  |  |  |  | 	// now process the restore in background
 | 
					
						
							|  |  |  |  | 	go func() { | 
					
						
							|  |  |  |  | 		rctx := GlobalContext | 
					
						
							|  |  |  |  | 		if !rreq.SelectParameters.IsEmpty() { | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 			actualSize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  |  | 			if err != nil { | 
					
						
							|  |  |  |  | 				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 			objectRSC := s3select.NewObjectReadSeekCloser( | 
					
						
							|  |  |  |  | 				func(offset int64) (io.ReadCloser, error) { | 
					
						
							|  |  |  |  | 					rs := &HTTPRangeSpec{ | 
					
						
							|  |  |  |  | 						IsSuffixLength: false, | 
					
						
							|  |  |  |  | 						Start:          offset, | 
					
						
							|  |  |  |  | 						End:            -1, | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 					return getTransitionedObjectReader(rctx, bucket, object, rs, r.Header, | 
					
						
							|  |  |  |  | 						objInfo, ObjectOptions{VersionID: objInfo.VersionID}) | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 				actualSize, | 
					
						
							|  |  |  |  | 			) | 
					
						
							| 
									
										
										
										
											2023-02-17 17:44:40 +08:00
										 |  |  |  | 			defer objectRSC.Close() | 
					
						
							| 
									
										
										
										
											2022-04-14 21:54:47 +08:00
										 |  |  |  | 			if err = rreq.SelectParameters.Open(objectRSC); err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 				if serr, ok := err.(s3select.SelectError); ok { | 
					
						
							|  |  |  |  | 					encodedErrorResponse := encodeResponse(APIErrorResponse{ | 
					
						
							|  |  |  |  | 						Code:       serr.ErrorCode(), | 
					
						
							|  |  |  |  | 						Message:    serr.ErrorMessage(), | 
					
						
							|  |  |  |  | 						BucketName: bucket, | 
					
						
							|  |  |  |  | 						Key:        object, | 
					
						
							|  |  |  |  | 						Resource:   r.URL.Path, | 
					
						
							|  |  |  |  | 						RequestID:  w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2023-10-18 23:06:57 +08:00
										 |  |  |  | 						HostID:     globalDeploymentID(), | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 					}) | 
					
						
							|  |  |  |  | 					writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML) | 
					
						
							|  |  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  |  | 					writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 				} | 
					
						
							|  |  |  |  | 				return | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			nr := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2022-11-29 02:20:27 +08:00
										 |  |  |  | 			rw := xhttp.NewResponseRecorder(nr) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 			rw.LogErrBody = true | 
					
						
							|  |  |  |  | 			rw.LogAllBody = true | 
					
						
							|  |  |  |  | 			rreq.SelectParameters.Evaluate(rw) | 
					
						
							|  |  |  |  | 			rreq.SelectParameters.Close() | 
					
						
							|  |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  |  | 		opts := ObjectOptions{ | 
					
						
							|  |  |  |  | 			Transition: TransitionOptions{ | 
					
						
							|  |  |  |  | 				RestoreRequest: rreq, | 
					
						
							|  |  |  |  | 				RestoreExpiry:  restoreExpiry, | 
					
						
							|  |  |  |  | 			}, | 
					
						
							|  |  |  |  | 			VersionID: objInfo.VersionID, | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if err := objectAPI.RestoreTransitionedObject(rctx, bucket, object, opts); err != nil { | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  |  | 			logger.LogIf(ctx, fmt.Errorf("Unable to restore transitioned bucket/object %s/%s: %w", bucket, object, err)) | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 			return | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		// Notify object restore completed via a POST request.
 | 
					
						
							|  |  |  |  | 		sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2022-12-14 21:12:07 +08:00
										 |  |  |  | 			EventName:  event.ObjectRestoreCompleted, | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  |  | 			BucketName: bucket, | 
					
						
							|  |  |  |  | 			Object:     objInfo, | 
					
						
							|  |  |  |  | 			ReqParams:  extractReqParams(r), | 
					
						
							|  |  |  |  | 			UserAgent:  r.UserAgent(), | 
					
						
							|  |  |  |  | 			Host:       handlers.GetSourceIP(r), | 
					
						
							|  |  |  |  | 		}) | 
					
						
							|  |  |  |  | 	}() | 
					
						
							|  |  |  |  | } |