| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 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/>.
 | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/encrypt" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/crypto" | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 	"github.com/minio/minio/internal/hash" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getDefaultOpts(header http.Header, copySource bool, metadata map[string]string) (opts ObjectOptions, err error) { | 
					
						
							|  |  |  | 	var clientKey [32]byte | 
					
						
							|  |  |  | 	var sse encrypt.ServerSide | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts = ObjectOptions{UserDefined: metadata} | 
					
						
							|  |  |  | 	if copySource { | 
					
						
							|  |  |  | 		if crypto.SSECopy.IsRequested(header) { | 
					
						
							|  |  |  | 			clientKey, err = crypto.SSECopy.ParseHTTP(header) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if sse, err = encrypt.NewSSEC(clientKey[:]); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			opts.ServerSideEncryption = encrypt.SSECopy(sse) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if crypto.SSEC.IsRequested(header) { | 
					
						
							|  |  |  | 		clientKey, err = crypto.SSEC.ParseHTTP(header) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if sse, err = encrypt.NewSSEC(clientKey[:]); err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		opts.ServerSideEncryption = sse | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if crypto.S3.IsRequested(header) || (metadata != nil && crypto.S3.IsEncrypted(metadata)) { | 
					
						
							|  |  |  | 		opts.ServerSideEncryption = encrypt.NewSSE() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-20 17:05:54 +08:00
										 |  |  | 	if v, ok := header[xhttp.MinIOSourceProxyRequest]; ok { | 
					
						
							|  |  |  | 		opts.ProxyHeaderSet = true | 
					
						
							|  |  |  | 		opts.ProxyRequest = strings.Join(v, "") == "true" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, ok := header[xhttp.MinIOSourceReplicationRequest]; ok { | 
					
						
							|  |  |  | 		opts.ReplicationRequest = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-17 10:35:24 +08:00
										 |  |  | 	opts.Speedtest = header.Get(globalObjectPerfUserMetadata) != "" | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // get ObjectOptions for GET calls from encryption headers
 | 
					
						
							|  |  |  | func getOpts(ctx context.Context, r *http.Request, bucket, object string) (ObjectOptions, error) { | 
					
						
							| 
									
										
										
										
											2022-10-25 08:44:15 +08:00
										 |  |  | 	var opts ObjectOptions | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var partNumber int | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if pn := r.Form.Get(xhttp.PartNumber); pn != "" { | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		partNumber, err = strconv.Atoi(pn) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return opts, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-04-19 13:34:30 +08:00
										 |  |  | 		if isMaxPartID(partNumber) { | 
					
						
							|  |  |  | 			return opts, errInvalidMaxParts | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		if partNumber <= 0 { | 
					
						
							|  |  |  | 			return opts, errInvalidArgument | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	vid := strings.TrimSpace(r.Form.Get(xhttp.VersionID)) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	if vid != "" && vid != nullVersionID { | 
					
						
							| 
									
										
										
										
											2022-12-16 00:25:05 +08:00
										 |  |  | 		if _, err := uuid.Parse(vid); err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-29 13:15:45 +08:00
										 |  |  | 			return opts, InvalidVersionID{ | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Object:    object, | 
					
						
							|  |  |  | 				VersionID: vid, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// default case of passing encryption headers to backend
 | 
					
						
							|  |  |  | 	opts, err = getDefaultOpts(r.Header, false, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	opts.PartNumber = partNumber | 
					
						
							|  |  |  | 	opts.VersionID = vid | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	delMarker, err := parseBoolHeader(bucket, object, r.Header, xhttp.MinIOSourceDeleteMarker) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	opts.DeleteMarker = delMarker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	replReadyCheck, err := parseBoolHeader(bucket, object, r.Header, xhttp.MinIOCheckDMReplicationReady) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 	opts.CheckDMReplicationReady = replReadyCheck | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.Tagging = r.Header.Get(xhttp.AmzTagDirective) == accessDirective | 
					
						
							|  |  |  | 	opts.Versioned = globalBucketVersioningSys.PrefixEnabled(bucket, object) | 
					
						
							|  |  |  | 	opts.VersionSuspended = globalBucketVersioningSys.PrefixSuspended(bucket, object) | 
					
						
							|  |  |  | 	return opts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-06 02:43:06 +08:00
										 |  |  | func getAndValidateAttributesOpts(ctx context.Context, w http.ResponseWriter, r *http.Request, bucket, object string) (opts ObjectOptions, valid bool) { | 
					
						
							|  |  |  | 	var argumentName string | 
					
						
							|  |  |  | 	var argumentValue string | 
					
						
							|  |  |  | 	var apiErr APIError | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	valid = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if valid { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		errResp := objectAttributesErrorResponse{ | 
					
						
							|  |  |  | 			ArgumentName:  &argumentName, | 
					
						
							|  |  |  | 			ArgumentValue: &argumentValue, | 
					
						
							|  |  |  | 			APIErrorResponse: getAPIErrorResponse( | 
					
						
							|  |  |  | 				ctx, | 
					
						
							|  |  |  | 				apiErr, | 
					
						
							|  |  |  | 				r.URL.Path, | 
					
						
							|  |  |  | 				w.Header().Get(xhttp.AmzRequestID), | 
					
						
							|  |  |  | 				w.Header().Get(xhttp.AmzRequestHostID), | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writeResponse(w, apiErr.HTTPStatusCode, encodeResponse(errResp), mimeXML) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts, err = getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		switch vErr := err.(type) { | 
					
						
							|  |  |  | 		case InvalidVersionID: | 
					
						
							|  |  |  | 			apiErr = toAPIError(ctx, vErr) | 
					
						
							|  |  |  | 			argumentName = strings.ToLower("versionId") | 
					
						
							|  |  |  | 			argumentValue = vErr.VersionID | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			apiErr = toAPIError(ctx, vErr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		valid = false | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.MaxParts, err = parseIntHeader(bucket, object, r.Header, xhttp.AmzMaxParts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		apiErr = toAPIError(ctx, err) | 
					
						
							|  |  |  | 		argumentName = strings.ToLower(xhttp.AmzMaxParts) | 
					
						
							|  |  |  | 		valid = false | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.MaxParts == 0 { | 
					
						
							|  |  |  | 		opts.MaxParts = maxPartsList | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.PartNumberMarker, err = parseIntHeader(bucket, object, r.Header, xhttp.AmzPartNumberMarker) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		apiErr = toAPIError(ctx, err) | 
					
						
							|  |  |  | 		argumentName = strings.ToLower(xhttp.AmzPartNumberMarker) | 
					
						
							|  |  |  | 		valid = false | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.ObjectAttributes = parseObjectAttributes(r.Header) | 
					
						
							|  |  |  | 	if len(opts.ObjectAttributes) < 1 { | 
					
						
							|  |  |  | 		apiErr = errorCodes.ToAPIErr(ErrInvalidAttributeName) | 
					
						
							|  |  |  | 		argumentName = strings.ToLower(xhttp.AmzObjectAttributes) | 
					
						
							|  |  |  | 		valid = false | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for tag := range opts.ObjectAttributes { | 
					
						
							|  |  |  | 		switch tag { | 
					
						
							|  |  |  | 		case xhttp.ETag: | 
					
						
							|  |  |  | 		case xhttp.Checksum: | 
					
						
							|  |  |  | 		case xhttp.StorageClass: | 
					
						
							|  |  |  | 		case xhttp.ObjectSize: | 
					
						
							|  |  |  | 		case xhttp.ObjectParts: | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			apiErr = errorCodes.ToAPIErr(ErrInvalidAttributeName) | 
					
						
							|  |  |  | 			argumentName = strings.ToLower(xhttp.AmzObjectAttributes) | 
					
						
							|  |  |  | 			argumentValue = tag | 
					
						
							|  |  |  | 			valid = false | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseObjectAttributes(h http.Header) (attributes map[string]struct{}) { | 
					
						
							|  |  |  | 	attributes = make(map[string]struct{}) | 
					
						
							|  |  |  | 	for _, v := range strings.Split(strings.TrimSpace(h.Get(xhttp.AmzObjectAttributes)), ",") { | 
					
						
							|  |  |  | 		if v != "" { | 
					
						
							|  |  |  | 			attributes[v] = struct{}{} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseIntHeader(bucket, object string, h http.Header, headerName string) (value int, err error) { | 
					
						
							|  |  |  | 	stringInt := strings.TrimSpace(h.Get(headerName)) | 
					
						
							|  |  |  | 	if stringInt == "" { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	value, err = strconv.Atoi(stringInt) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, InvalidArgument{ | 
					
						
							|  |  |  | 			Bucket: bucket, | 
					
						
							|  |  |  | 			Object: object, | 
					
						
							|  |  |  | 			Err:    fmt.Errorf("Unable to parse %s, value should be an integer", headerName), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | func parseBoolHeader(bucket, object string, h http.Header, headerName string) (bool, error) { | 
					
						
							|  |  |  | 	value := strings.TrimSpace(h.Get(headerName)) | 
					
						
							|  |  |  | 	if value != "" { | 
					
						
							|  |  |  | 		switch value { | 
					
						
							| 
									
										
										
										
											2022-10-22 05:45:06 +08:00
										 |  |  | 		case "true": | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 			return true, nil | 
					
						
							| 
									
										
										
										
											2022-10-22 05:45:06 +08:00
										 |  |  | 		case "false": | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 			return false, InvalidArgument{ | 
					
						
							| 
									
										
										
										
											2022-10-22 05:45:06 +08:00
										 |  |  | 				Bucket: bucket, | 
					
						
							|  |  |  | 				Object: object, | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 				Err:    fmt.Errorf("Unable to parse %s, value should be either 'true' or 'false'", headerName), | 
					
						
							| 
									
										
										
										
											2022-10-22 05:45:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 	return false, nil | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func delOpts(ctx context.Context, r *http.Request, bucket, object string) (opts ObjectOptions, err error) { | 
					
						
							|  |  |  | 	opts, err = getOpts(ctx, r, bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-22 22:19:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	deletePrefix := false | 
					
						
							|  |  |  | 	if d := r.Header.Get(xhttp.MinIOForceDelete); d != "" { | 
					
						
							|  |  |  | 		if b, err := strconv.ParseBool(d); err == nil { | 
					
						
							|  |  |  | 			deletePrefix = b | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return opts, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.DeletePrefix = deletePrefix | 
					
						
							| 
									
										
										
										
											2022-05-07 10:05:28 +08:00
										 |  |  | 	opts.Versioned = globalBucketVersioningSys.PrefixEnabled(bucket, object) | 
					
						
							| 
									
										
										
										
											2022-08-25 04:46:29 +08:00
										 |  |  | 	// Objects matching prefixes should not leave delete markers,
 | 
					
						
							|  |  |  | 	// dramatically reduces namespace pollution while keeping the
 | 
					
						
							|  |  |  | 	// benefits of replication, make sure to apply version suspension
 | 
					
						
							|  |  |  | 	// only at bucket level instead.
 | 
					
						
							|  |  |  | 	opts.VersionSuspended = globalBucketVersioningSys.Suspended(bucket) | 
					
						
							| 
									
										
										
										
											2023-05-05 05:43:52 +08:00
										 |  |  | 	// For directory objects, delete `null` version permanently.
 | 
					
						
							|  |  |  | 	if isDirObject(object) && opts.VersionID == "" { | 
					
						
							|  |  |  | 		opts.VersionID = nullVersionID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 	delMarker, err := parseBoolHeader(bucket, object, r.Header, xhttp.MinIOSourceDeleteMarker) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 	opts.DeleteMarker = delMarker | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mtime := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceMTime)) | 
					
						
							|  |  |  | 	if mtime != "" { | 
					
						
							| 
									
										
										
										
											2020-12-19 03:37:28 +08:00
										 |  |  | 		opts.MTime, err = time.Parse(time.RFC3339Nano, mtime) | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return opts, InvalidArgument{ | 
					
						
							|  |  |  | 				Bucket: bucket, | 
					
						
							|  |  |  | 				Object: object, | 
					
						
							|  |  |  | 				Err:    fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceMTime, err), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	return opts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // get ObjectOptions for PUT calls from encryption headers and metadata
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | func putOptsFromReq(ctx context.Context, r *http.Request, bucket, object string, metadata map[string]string) (opts ObjectOptions, err error) { | 
					
						
							|  |  |  | 	return putOpts(ctx, bucket, object, r.Form.Get(xhttp.VersionID), r.Header, metadata) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func putOpts(ctx context.Context, bucket, object, vid string, hdrs http.Header, metadata map[string]string) (opts ObjectOptions, err error) { | 
					
						
							| 
									
										
										
										
											2022-05-07 10:05:28 +08:00
										 |  |  | 	versioned := globalBucketVersioningSys.PrefixEnabled(bucket, object) | 
					
						
							|  |  |  | 	versionSuspended := globalBucketVersioningSys.PrefixSuspended(bucket, object) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	vid = strings.TrimSpace(vid) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	if vid != "" && vid != nullVersionID { | 
					
						
							|  |  |  | 		_, err := uuid.Parse(vid) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-29 13:15:45 +08:00
										 |  |  | 			return opts, InvalidVersionID{ | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Object:    object, | 
					
						
							|  |  |  | 				VersionID: vid, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-01 04:36:37 +08:00
										 |  |  | 		if !versioned { | 
					
						
							|  |  |  | 			return opts, InvalidArgument{ | 
					
						
							|  |  |  | 				Bucket: bucket, | 
					
						
							|  |  |  | 				Object: object, | 
					
						
							| 
									
										
										
										
											2023-10-10 15:33:42 +08:00
										 |  |  | 				Err:    fmt.Errorf("VersionID specified %s, but versioning not enabled on bucket=%s", opts.VersionID, bucket), | 
					
						
							| 
									
										
										
										
											2020-10-01 04:36:37 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	opts, err = putOptsFromHeaders(ctx, hdrs, metadata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, InvalidArgument{ | 
					
						
							|  |  |  | 			Bucket: bucket, | 
					
						
							|  |  |  | 			Object: object, | 
					
						
							|  |  |  | 			Err:    err, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.VersionID = vid | 
					
						
							|  |  |  | 	opts.Versioned = versioned | 
					
						
							|  |  |  | 	opts.VersionSuspended = versionSuspended | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// For directory objects skip creating new versions.
 | 
					
						
							|  |  |  | 	if isDirObject(object) && vid == "" { | 
					
						
							|  |  |  | 		opts.VersionID = nullVersionID | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	return opts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func putOptsFromHeaders(ctx context.Context, hdr http.Header, metadata map[string]string) (opts ObjectOptions, err error) { | 
					
						
							|  |  |  | 	mtimeStr := strings.TrimSpace(hdr.Get(xhttp.MinIOSourceMTime)) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  | 	var mtime time.Time | 
					
						
							| 
									
										
										
										
											2020-10-03 09:32:22 +08:00
										 |  |  | 	if mtimeStr != "" { | 
					
						
							| 
									
										
										
										
											2023-03-28 02:51:54 +08:00
										 |  |  | 		mtime, err = time.Parse(time.RFC3339Nano, mtimeStr) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 			return opts, fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceMTime, err) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	retaintimeStr := strings.TrimSpace(hdr.Get(xhttp.MinIOSourceObjectRetentionTimestamp)) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  | 	var retaintimestmp time.Time | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	if retaintimeStr != "" { | 
					
						
							|  |  |  | 		retaintimestmp, err = time.Parse(time.RFC3339, retaintimeStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 			return opts, fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceObjectRetentionTimestamp, err) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	lholdtimeStr := strings.TrimSpace(hdr.Get(xhttp.MinIOSourceObjectLegalHoldTimestamp)) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  | 	var lholdtimestmp time.Time | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	if lholdtimeStr != "" { | 
					
						
							|  |  |  | 		lholdtimestmp, err = time.Parse(time.RFC3339, lholdtimeStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 			return opts, fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceObjectLegalHoldTimestamp, err) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	tagtimeStr := strings.TrimSpace(hdr.Get(xhttp.MinIOSourceTaggingTimestamp)) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  | 	var taggingtimestmp time.Time | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	if tagtimeStr != "" { | 
					
						
							|  |  |  | 		taggingtimestmp, err = time.Parse(time.RFC3339, tagtimeStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 			return opts, fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceTaggingTimestamp, err) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 09:44:04 +08:00
										 |  |  | 	if metadata == nil { | 
					
						
							|  |  |  | 		metadata = make(map[string]string) | 
					
						
							| 
									
										
										
										
											2020-08-13 08:32:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	wantCRC, err := hash.GetContentChecksum(hdr) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 		return opts, fmt.Errorf("invalid/unknown checksum sent: %v", err) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	etag := strings.TrimSpace(hdr.Get(xhttp.MinIOSourceETag)) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	if crypto.S3KMS.IsRequested(hdr) { | 
					
						
							|  |  |  | 		keyID, context, err := crypto.S3KMS.ParseHTTP(hdr) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return ObjectOptions{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sseKms, err := encrypt.NewSSEKMS(keyID, context) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return ObjectOptions{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return ObjectOptions{ | 
					
						
							|  |  |  | 			ServerSideEncryption: sseKms, | 
					
						
							|  |  |  | 			UserDefined:          metadata, | 
					
						
							| 
									
										
										
										
											2020-10-03 09:32:22 +08:00
										 |  |  | 			MTime:                mtime, | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 			WantChecksum:         wantCRC, | 
					
						
							| 
									
										
										
										
											2022-11-27 06:43:32 +08:00
										 |  |  | 			PreserveETag:         etag, | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 		}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// default case of passing encryption headers and UserDefined metadata to backend
 | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	opts, err = getDefaultOpts(hdr, false, metadata) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-05 05:43:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 09:32:22 +08:00
										 |  |  | 	opts.MTime = mtime | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	opts.ReplicationSourceLegalholdTimestamp = lholdtimestmp | 
					
						
							|  |  |  | 	opts.ReplicationSourceRetentionTimestamp = retaintimestmp | 
					
						
							|  |  |  | 	opts.ReplicationSourceTaggingTimestamp = taggingtimestmp | 
					
						
							| 
									
										
										
										
											2022-11-27 06:43:32 +08:00
										 |  |  | 	opts.PreserveETag = etag | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 	opts.WantChecksum = wantCRC | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 	return opts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // get ObjectOptions for Copy calls with encryption headers provided on the target side and source side metadata
 | 
					
						
							|  |  |  | func copyDstOpts(ctx context.Context, r *http.Request, bucket, object string, metadata map[string]string) (opts ObjectOptions, err error) { | 
					
						
							| 
									
										
										
										
											2023-11-23 02:51:46 +08:00
										 |  |  | 	return putOptsFromReq(ctx, r, bucket, object, metadata) | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // get ObjectOptions for Copy calls with encryption headers provided on the source side
 | 
					
						
							|  |  |  | func copySrcOpts(ctx context.Context, r *http.Request, bucket, object string) (ObjectOptions, error) { | 
					
						
							| 
									
										
										
										
											2022-10-25 08:44:15 +08:00
										 |  |  | 	var opts ObjectOptions | 
					
						
							| 
									
										
										
										
											2020-07-09 08:36:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// default case of passing encryption headers to backend
 | 
					
						
							|  |  |  | 	opts, err := getDefaultOpts(r.Header, false, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return opts, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-06-30 22:44:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // get ObjectOptions for CompleteMultipart calls
 | 
					
						
							|  |  |  | func completeMultipartOpts(ctx context.Context, r *http.Request, bucket, object string) (opts ObjectOptions, err error) { | 
					
						
							|  |  |  | 	mtimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceMTime)) | 
					
						
							| 
									
										
										
										
											2023-10-21 10:28:05 +08:00
										 |  |  | 	var mtime time.Time | 
					
						
							| 
									
										
										
										
											2021-06-30 22:44:24 +08:00
										 |  |  | 	if mtimeStr != "" { | 
					
						
							| 
									
										
										
										
											2023-03-28 02:51:54 +08:00
										 |  |  | 		mtime, err = time.Parse(time.RFC3339Nano, mtimeStr) | 
					
						
							| 
									
										
										
										
											2021-06-30 22:44:24 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return opts, InvalidArgument{ | 
					
						
							|  |  |  | 				Bucket: bucket, | 
					
						
							|  |  |  | 				Object: object, | 
					
						
							|  |  |  | 				Err:    fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceMTime, err), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-23 22:58:33 +08:00
										 |  |  | 	opts.WantChecksum, err = hash.GetContentChecksum(r.Header) | 
					
						
							| 
									
										
										
										
											2022-08-30 07:57:16 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return opts, InvalidArgument{ | 
					
						
							|  |  |  | 			Bucket: bucket, | 
					
						
							|  |  |  | 			Object: object, | 
					
						
							|  |  |  | 			Err:    fmt.Errorf("invalid/unknown checksum sent: %v", err), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-30 22:44:24 +08:00
										 |  |  | 	opts.MTime = mtime | 
					
						
							| 
									
										
										
										
											2021-09-09 13:25:23 +08:00
										 |  |  | 	opts.UserDefined = make(map[string]string) | 
					
						
							| 
									
										
										
										
											2024-03-29 01:44:56 +08:00
										 |  |  | 	opts.UserDefined[ReservedMetadataPrefix+"Actual-Object-Size"] = r.Header.Get(xhttp.MinIOReplicationActualObjectSize) | 
					
						
							| 
									
										
										
										
											2022-12-08 02:18:18 +08:00
										 |  |  | 	// Transfer SSEC key in opts.EncryptFn
 | 
					
						
							|  |  |  | 	if crypto.SSEC.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		key, err := ParseSSECustomerRequest(r) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Set EncryptFn to return SSEC key
 | 
					
						
							|  |  |  | 			opts.EncryptFn = func(baseKey string, data []byte) []byte { | 
					
						
							|  |  |  | 				return key | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-03-29 01:44:56 +08:00
										 |  |  | 	if _, ok := r.Header[xhttp.MinIOSourceReplicationRequest]; ok { | 
					
						
							|  |  |  | 		opts.ReplicationRequest = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-30 22:44:24 +08:00
										 |  |  | 	return opts, nil | 
					
						
							|  |  |  | } |