mirror of
https://github.com/minio/minio.git
synced 2025-04-10 22:47:52 -04:00
Fix CopyObject regression calculating md5sum (#6868)
CopyObject() failed to calculate proper md5sum when without encryption headers. This is a regression fix perhaps introduced in commit 5f6d717b7a Fixes https://github.com/minio/minio-go/issues/1044
This commit is contained in:
parent
12a6523fb2
commit
83fe70f710
@ -635,12 +635,13 @@ func (p *PutObjReader) MD5CurrentHexString() string {
|
|||||||
func NewPutObjReader(rawReader *hash.Reader, encReader *hash.Reader, encKey []byte) *PutObjReader {
|
func NewPutObjReader(rawReader *hash.Reader, encReader *hash.Reader, encKey []byte) *PutObjReader {
|
||||||
p := PutObjReader{Reader: rawReader, rawReader: rawReader}
|
p := PutObjReader{Reader: rawReader, rawReader: rawReader}
|
||||||
|
|
||||||
var objKey crypto.ObjectKey
|
if len(encKey) != 0 && encReader != nil {
|
||||||
copy(objKey[:], encKey)
|
var objKey crypto.ObjectKey
|
||||||
p.sealMD5Fn = sealETagFn(objKey)
|
copy(objKey[:], encKey)
|
||||||
if encReader != nil {
|
p.sealMD5Fn = sealETagFn(objKey)
|
||||||
p.Reader = encReader
|
p.Reader = encReader
|
||||||
}
|
}
|
||||||
|
|
||||||
return &p
|
return &p
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,9 +652,10 @@ func sealETag(encKey crypto.ObjectKey, md5CurrSum []byte) []byte {
|
|||||||
}
|
}
|
||||||
return encKey.SealETag(md5CurrSum)
|
return encKey.SealETag(md5CurrSum)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sealETagFn(key crypto.ObjectKey) SealMD5CurrFn {
|
func sealETagFn(key crypto.ObjectKey) SealMD5CurrFn {
|
||||||
fn1 := func(md5sumcurr []byte) []byte {
|
fn := func(md5sumcurr []byte) []byte {
|
||||||
return sealETag(key, md5sumcurr)
|
return sealETag(key, md5sumcurr)
|
||||||
}
|
}
|
||||||
return fn1
|
return fn
|
||||||
}
|
}
|
||||||
|
@ -774,6 +774,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
length = actualSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to compress for remote etcd calls
|
// No need to compress for remote etcd calls
|
||||||
@ -813,8 +814,10 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rawReader := srcInfo.Reader
|
rawReader := srcInfo.Reader
|
||||||
pReader := NewPutObjReader(srcInfo.Reader, srcInfo.Reader, nil)
|
pReader := NewPutObjReader(srcInfo.Reader, nil, nil)
|
||||||
|
|
||||||
var encMetadata = make(map[string]string)
|
var encMetadata = make(map[string]string)
|
||||||
if objectAPI.IsEncryptionSupported() && !isCompressed {
|
if objectAPI.IsEncryptionSupported() && !isCompressed {
|
||||||
// Encryption parameters not applicable for this object.
|
// Encryption parameters not applicable for this object.
|
||||||
@ -882,15 +885,15 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !isSourceEncrypted && !isTargetEncrypted:
|
case !isSourceEncrypted && !isTargetEncrypted:
|
||||||
fallthrough
|
targetSize = srcInfo.Size
|
||||||
case isSourceEncrypted && isTargetEncrypted:
|
case isSourceEncrypted && isTargetEncrypted:
|
||||||
targetSize = srcInfo.Size
|
targetSize = srcInfo.Size
|
||||||
// Source not encrypted and target encrypted
|
|
||||||
case !isSourceEncrypted && isTargetEncrypted:
|
case !isSourceEncrypted && isTargetEncrypted:
|
||||||
targetSize = srcInfo.EncryptedSize()
|
targetSize = srcInfo.EncryptedSize()
|
||||||
case isSourceEncrypted && !isTargetEncrypted:
|
case isSourceEncrypted && !isTargetEncrypted:
|
||||||
targetSize, _ = srcInfo.DecryptedSize()
|
targetSize, _ = srcInfo.DecryptedSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isTargetEncrypted {
|
if isTargetEncrypted {
|
||||||
reader, objEncKey, err = newEncryptReader(srcInfo.Reader, newKey, dstBucket, dstObject, encMetadata, sseS3)
|
reader, objEncKey, err = newEncryptReader(srcInfo.Reader, newKey, dstBucket, dstObject, encMetadata, sseS3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -910,6 +913,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pReader = NewPutObjReader(rawReader, srcInfo.Reader, objEncKey)
|
pReader = NewPutObjReader(rawReader, srcInfo.Reader, objEncKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -1811,9 +1812,13 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
// this is required even to assert the copied object,
|
// this is required even to assert the copied object,
|
||||||
bytesData := []struct {
|
bytesData := []struct {
|
||||||
byteData []byte
|
byteData []byte
|
||||||
|
md5sum string
|
||||||
}{
|
}{
|
||||||
{generateBytesData(6 * humanize.KiByte)},
|
{byteData: generateBytesData(6 * humanize.KiByte)},
|
||||||
}
|
}
|
||||||
|
h := md5.New()
|
||||||
|
h.Write(bytesData[0].byteData)
|
||||||
|
bytesData[0].md5sum = hex.EncodeToString(h.Sum(nil))
|
||||||
|
|
||||||
buffers := []*bytes.Buffer{
|
buffers := []*bytes.Buffer{
|
||||||
new(bytes.Buffer),
|
new(bytes.Buffer),
|
||||||
@ -1826,23 +1831,29 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
objectName string
|
objectName string
|
||||||
contentLength int64
|
contentLength int64
|
||||||
textData []byte
|
textData []byte
|
||||||
|
md5sum string
|
||||||
metaData map[string]string
|
metaData map[string]string
|
||||||
}{
|
}{
|
||||||
// case - 1.
|
// case - 1.
|
||||||
{bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
{bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, bytesData[0].md5sum, make(map[string]string)},
|
||||||
|
|
||||||
// case - 2.
|
// case - 2.
|
||||||
// used for anonymous HTTP request test.
|
// used for anonymous HTTP request test.
|
||||||
{bucketName, anonObject, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
{bucketName, anonObject, int64(len(bytesData[0].byteData)), bytesData[0].byteData, bytesData[0].md5sum, make(map[string]string)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate through the above set of inputs and upload the object.
|
// iterate through the above set of inputs and upload the object.
|
||||||
for i, input := range putObjectInputs {
|
for i, input := range putObjectInputs {
|
||||||
// uploading the object.
|
// uploading the object.
|
||||||
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
|
var objInfo ObjectInfo
|
||||||
|
objInfo, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
|
||||||
// if object upload fails stop the test.
|
// if object upload fails stop the test.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
|
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
|
||||||
}
|
}
|
||||||
|
if objInfo.ETag != input.md5sum {
|
||||||
|
t.Fatalf("Put Object case %d: Checksum mismatched: <ERROR> got %s, expected %s", i+1, input.md5sum, objInfo.ETag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test cases with inputs and expected result for Copy Object.
|
// test cases with inputs and expected result for Copy Object.
|
||||||
@ -2105,6 +2116,16 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
t.Fatalf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
|
t.Fatalf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
|
||||||
}
|
}
|
||||||
if rec.Code == http.StatusOK {
|
if rec.Code == http.StatusOK {
|
||||||
|
var cpObjResp CopyObjectResponse
|
||||||
|
if err = xml.Unmarshal(rec.Body.Bytes(), &cpObjResp); err != nil {
|
||||||
|
t.Fatalf("Test %d: %s: Failed to parse the CopyObjectResult response: <ERROR> %s", i+1, instanceType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retag := canonicalizeETag(cpObjResp.ETag)
|
||||||
|
if retag != bytesData[0].md5sum {
|
||||||
|
t.Fatalf("Test %d: %s: Failed to CopyObject: <ERROR> got %s, expected %s", i+1, instanceType, retag, bytesData[0].md5sum)
|
||||||
|
}
|
||||||
|
|
||||||
// See if the new object is formed.
|
// See if the new object is formed.
|
||||||
// testing whether the copy was successful.
|
// testing whether the copy was successful.
|
||||||
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.newObjectName, 0, int64(len(bytesData[0].byteData)), buffers[0], "", opts)
|
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.newObjectName, 0, int64(len(bytesData[0].byteData)), buffers[0], "", opts)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user