diff --git a/api-errors.go b/api-errors.go index cc34c25a7..a629b2ebd 100644 --- a/api-errors.go +++ b/api-errors.go @@ -458,6 +458,8 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) { apiErr = ErrWriteQuorum case InsufficientReadQuorum: apiErr = ErrReadQuorum + case PartTooSmall: + apiErr = ErrEntityTooSmall default: apiErr = ErrInternalError } diff --git a/object-errors.go b/object-errors.go index dd807989a..766b0b753 100644 --- a/object-errors.go +++ b/object-errors.go @@ -233,3 +233,19 @@ type InvalidPart struct{} func (e InvalidPart) Error() string { return "One or more of the specified parts could not be found" } + +// InvalidPartOrder parts are not ordered as Requested +type InvalidPartOrder struct { + UploadID string +} + +func (e InvalidPartOrder) Error() string { + return "Invalid part order sent for " + e.UploadID +} + +// PartTooSmall - error if part size is less than 5MB. +type PartTooSmall struct{} + +func (e PartTooSmall) Error() string { + return "Part size should be atleast 5MB" +} diff --git a/object_api_suite_test.go b/object_api_suite_test.go index c14708b60..988a02151 100644 --- a/object_api_suite_test.go +++ b/object_api_suite_test.go @@ -63,22 +63,23 @@ func testMultipartObjectCreation(c *check.C, create func() ObjectLayer) { c.Assert(err, check.IsNil) uploadID, err := obj.NewMultipartUpload("bucket", "key") c.Assert(err, check.IsNil) - + // Create a byte array of 5MB. + data := bytes.Repeat([]byte("0123456789abcdef"), 5*1024*1024/16) completedParts := completeMultipartUpload{} for i := 1; i <= 10; i++ { hasher := md5.New() - hasher.Write([]byte("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")) + hasher.Write(data) expectedMD5Sumhex := hex.EncodeToString(hasher.Sum(nil)) var calculatedMD5sum string - calculatedMD5sum, err = obj.PutObjectPart("bucket", "key", uploadID, i, int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")), bytes.NewBufferString("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."), expectedMD5Sumhex) + calculatedMD5sum, err = obj.PutObjectPart("bucket", "key", uploadID, i, int64(len(data)), bytes.NewBuffer(data), expectedMD5Sumhex) c.Assert(err, check.IsNil) c.Assert(calculatedMD5sum, check.Equals, expectedMD5Sumhex) completedParts.Parts = append(completedParts.Parts, completePart{PartNumber: i, ETag: calculatedMD5sum}) } md5Sum, err := obj.CompleteMultipartUpload("bucket", "key", uploadID, completedParts.Parts) c.Assert(err, check.IsNil) - c.Assert(md5Sum, check.Equals, "7dd76eded6f7c3580a78463a7cf539bd-10") + c.Assert(md5Sum, check.Equals, "7d364cb728ce42a74a96d22949beefb2-10") } // Tests validate abortion of Multipart operation. diff --git a/server_xl_test.go b/server_xl_test.go index f5ea50db1..d852a03de 100644 --- a/server_xl_test.go +++ b/server_xl_test.go @@ -1262,11 +1262,14 @@ func (s *MyAPIXLSuite) TestObjectMultipart(c *C) { c.Assert(len(newResponse.UploadID) > 0, Equals, true) uploadID := newResponse.UploadID + // Create a byte array of 5MB. + data := bytes.Repeat([]byte("0123456789abcdef"), 5*1024*1024/16) + hasher := md5.New() - hasher.Write([]byte("hello world")) + hasher.Write(data) md5Sum := hasher.Sum(nil) - buffer1 := bytes.NewReader([]byte("hello world")) + buffer1 := bytes.NewReader(data) request, err = s.newRequest("PUT", testAPIXLServer.URL+"/objectmultiparts/object?uploadId="+uploadID+"&partNumber=1", int64(buffer1.Len()), buffer1) request.Header.Set("Content-Md5", base64.StdEncoding.EncodeToString(md5Sum)) c.Assert(err, IsNil) @@ -1276,7 +1279,7 @@ func (s *MyAPIXLSuite) TestObjectMultipart(c *C) { c.Assert(err, IsNil) c.Assert(response1.StatusCode, Equals, http.StatusOK) - buffer2 := bytes.NewReader([]byte("hello world")) + buffer2 := bytes.NewReader(data) request, err = s.newRequest("PUT", testAPIXLServer.URL+"/objectmultiparts/object?uploadId="+uploadID+"&partNumber=2", int64(buffer2.Len()), buffer2) request.Header.Set("Content-Md5", base64.StdEncoding.EncodeToString(md5Sum)) c.Assert(err, IsNil) diff --git a/utils.go b/utils.go index aea79e3b6..ed42eaf4c 100644 --- a/utils.go +++ b/utils.go @@ -38,6 +38,8 @@ func checkValidMD5(md5 string) ([]byte, error) { const ( // maximum object size per PUT request is 5GiB maxObjectSize = 1024 * 1024 * 1024 * 5 + // minimum Part size for multipart upload is 5MB + minPartSize = 1024 * 1024 * 5 ) // isMaxObjectSize - verify if max object size @@ -45,6 +47,11 @@ func isMaxObjectSize(size int64) bool { return size > maxObjectSize } +// Check if part size is more than or equal to minimum allowed size. +func isMinAllowedPartSize(size int64) bool { + return size >= minPartSize +} + func contains(stringList []string, element string) bool { for _, e := range stringList { if e == element { diff --git a/xl-objects-multipart.go b/xl-objects-multipart.go index 30e81baa2..b3f8ea81c 100644 --- a/xl-objects-multipart.go +++ b/xl-objects-multipart.go @@ -136,6 +136,9 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload } return "", err } + if !isMinAllowedPartSize(fi.Size) { + return "", PartTooSmall{} + } // Update metadata parts. metadata.Parts = append(metadata.Parts, MultipartPartInfo{ PartNumber: part.PartNumber,