From e79874f58ed6078e72091f5371ab3274feb80ff2 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 17 Jun 2020 11:13:41 -0700 Subject: [PATCH] [feat] Preserve version supplied by client (#9854) Just like GET/DELETE APIs it is possible to preserve client supplied versionId's, of course the versionIds have to be uuid, if an existing versionId is found it is overwritten if no object locking policies are found. - PUT /bucketname/objectname?versionId= - POST /bucketname/objectname?uploads=&versionId= - PUT /bucketname/objectname?verisonId= (with x-amz-copy-source) --- cmd/encryption-v1.go | 29 +++++++++++++++++++++++++++-- cmd/erasure-multipart.go | 5 ++++- cmd/erasure-object.go | 5 ++++- cmd/erasure-sets.go | 7 ++++++- cmd/erasure-zones.go | 8 +++++++- cmd/object-handlers.go | 1 + cmd/xl-storage-format-v2.go | 14 ++++++++------ 7 files changed, 57 insertions(+), 12 deletions(-) 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