diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index da898fb49..6e23d05e6 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -963,13 +963,32 @@ func delOpts(ctx context.Context, r *http.Request, bucket, object string) (opts // get ObjectOptions for PUT calls from encryption headers and metadata func putOpts(ctx context.Context, r *http.Request, bucket, object string, metadata map[string]string) (opts ObjectOptions, err error) { versioned := globalBucketVersioningSys.Enabled(bucket) + vid := strings.TrimSpace(r.URL.Query().Get("versionId")) + if vid != "" && vid != nullVersionID { + _, err := uuid.Parse(vid) + if err != nil { + logger.LogIf(ctx, err) + return opts, VersionNotFound{ + Bucket: bucket, + Object: object, + VersionID: vid, + } + } + } + // In the case of multipart custom format, the metadata needs to be checked in addition to header to see if it // is SSE-S3 encrypted, primarily because S3 protocol does not require SSE-S3 headers in PutObjectPart calls if GlobalGatewaySSE.SSES3() && (crypto.S3.IsRequested(r.Header) || crypto.S3.IsEncrypted(metadata)) { - return ObjectOptions{ServerSideEncryption: encrypt.NewSSE(), UserDefined: metadata, Versioned: versioned}, nil + return ObjectOptions{ + ServerSideEncryption: encrypt.NewSSE(), + UserDefined: metadata, + VersionID: vid, + Versioned: versioned, + }, nil } if GlobalGatewaySSE.SSEC() && crypto.SSEC.IsRequested(r.Header) { opts, err = getOpts(ctx, r, bucket, object) + opts.VersionID = vid opts.Versioned = versioned opts.UserDefined = metadata return @@ -983,13 +1002,19 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada if err != nil { return ObjectOptions{}, err } - return ObjectOptions{ServerSideEncryption: sseKms, UserDefined: metadata, Versioned: versioned}, nil + return ObjectOptions{ + ServerSideEncryption: sseKms, + UserDefined: metadata, + VersionID: vid, + Versioned: versioned, + }, nil } // default case of passing encryption headers and UserDefined metadata to backend opts, err = getDefaultOpts(r.Header, false, metadata) if err != nil { return opts, err } + opts.VersionID = vid opts.Versioned = versioned return opts, nil } diff --git a/cmd/erasure-multipart.go b/cmd/erasure-multipart.go index c96d4a3d2..c0926b3e6 100644 --- a/cmd/erasure-multipart.go +++ b/cmd/erasure-multipart.go @@ -142,7 +142,10 @@ func (er erasureObjects) newMultipartUpload(ctx context.Context, bucket string, // Calculate the version to be saved. if opts.Versioned { - fi.VersionID = mustGetUUID() + fi.VersionID = opts.VersionID + if fi.VersionID == "" { + fi.VersionID = mustGetUUID() + } } fi.DataDir = mustGetUUID() diff --git a/cmd/erasure-object.go b/cmd/erasure-object.go index e84518459..a90cb96f4 100644 --- a/cmd/erasure-object.go +++ b/cmd/erasure-object.go @@ -608,7 +608,10 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st fi := newFileInfo(object, dataDrives, parityDrives) if opts.Versioned { - fi.VersionID = mustGetUUID() + fi.VersionID = opts.VersionID + if fi.VersionID == "" { + fi.VersionID = mustGetUUID() + } } fi.DataDir = mustGetUUID() diff --git a/cmd/erasure-sets.go b/cmd/erasure-sets.go index 2cbaa72f9..a5d58aac3 100644 --- a/cmd/erasure-sets.go +++ b/cmd/erasure-sets.go @@ -794,7 +794,12 @@ func (s *erasureSets) CopyObject(ctx context.Context, srcBucket, srcObject, dstB return srcSet.CopyObject(ctx, srcBucket, srcObject, dstBucket, dstObject, srcInfo, srcOpts, dstOpts) } - putOpts := ObjectOptions{ServerSideEncryption: dstOpts.ServerSideEncryption, UserDefined: srcInfo.UserDefined} + putOpts := ObjectOptions{ + ServerSideEncryption: dstOpts.ServerSideEncryption, + UserDefined: srcInfo.UserDefined, + Versioned: dstOpts.Versioned, + VersionID: dstOpts.VersionID, + } return dstSet.putObject(ctx, dstBucket, dstObject, srcInfo.PutObjReader, putOpts) } diff --git a/cmd/erasure-zones.go b/cmd/erasure-zones.go index c40e91243..2ce3b8e45 100644 --- a/cmd/erasure-zones.go +++ b/cmd/erasure-zones.go @@ -594,7 +594,13 @@ func (z *erasureZones) CopyObject(ctx context.Context, srcBucket, srcObject, dst return objInfo, err } - putOpts := ObjectOptions{ServerSideEncryption: dstOpts.ServerSideEncryption, UserDefined: srcInfo.UserDefined} + putOpts := ObjectOptions{ + ServerSideEncryption: dstOpts.ServerSideEncryption, + UserDefined: srcInfo.UserDefined, + Versioned: dstOpts.Versioned, + VersionID: dstOpts.VersionID, + } + if cpSrcDstSame && srcInfo.metadataOnly { return z.zones[zoneIdx].CopyObject(ctx, srcBucket, srcObject, dstBucket, dstObject, srcInfo, srcOpts, dstOpts) } diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 7fc6af3dc..e6c5e6b85 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -1735,6 +1735,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt if srcOpts.ServerSideEncryption != nil { getOpts.ServerSideEncryption = encrypt.SSE(srcOpts.ServerSideEncryption) } + dstOpts, err = copyDstOpts(ctx, r, dstBucket, dstObject, nil) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) diff --git a/cmd/xl-storage-format-v2.go b/cmd/xl-storage-format-v2.go index 895f53694..08c029ec0 100644 --- a/cmd/xl-storage-format-v2.go +++ b/cmd/xl-storage-format-v2.go @@ -237,14 +237,16 @@ func (z *xlMetaV2) AddVersion(fi FileInfo) error { return nil } - var uv uuid.UUID - var err error - // null version Id means empty version Id. - if fi.VersionID == nullVersionID { - fi.VersionID = "" + if fi.VersionID == "" { + // this means versioning is not yet + // enabled i.e all versions are basically + // default value i.e "null" + fi.VersionID = nullVersionID } - if fi.VersionID != "" { + var uv uuid.UUID + var err error + if fi.VersionID != "" && fi.VersionID != nullVersionID { uv, err = uuid.Parse(fi.VersionID) if err != nil { return err