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
master
Harshavardhana 6 years ago committed by kannappanr
parent 12a6523fb2
commit 83fe70f710
  1. 14
      cmd/object-api-utils.go
  2. 10
      cmd/object-handlers.go
  3. 29
      cmd/object-handlers_test.go

@ -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…
Cancel
Save