From e1aad066c610386e2369bc4b22ece439682c6765 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sun, 19 Jun 2016 14:51:20 -0700 Subject: [PATCH] XL: CompleteMultipart should ignore last part is 0bytes. (#1931) Fixes #1917 --- erasure-createfile.go | 22 ++++++++++++++++++---- xl-v1-metadata.go | 5 ----- xl-v1-multipart.go | 11 +++++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/erasure-createfile.go b/erasure-createfile.go index 83a76f2f5..43306dba4 100644 --- a/erasure-createfile.go +++ b/erasure-createfile.go @@ -39,20 +39,34 @@ func erasureCreateFile(disks []StorageAPI, volume string, path string, partName // Read until io.EOF, erasure codes data and writes to all disks. for { var n int + var blocks [][]byte n, err = io.ReadFull(data, buf) if err == io.EOF { + // We have reached EOF on the first byte read, io.Reader + // must be 0bytes, we don't need to erasure code + // data. Will create a 0byte file instead. + if size == 0 { + blocks = make([][]byte, len(disks)) + err = appendFile(disks, volume, path, blocks, eInfo.Distribution, hashWriters, writeQuorum) + if err != nil { + return nil, 0, err + } + } // else we have reached EOF after few reads, no need to + // add an additional 0bytes at the end. break } if err != nil && err != io.ErrUnexpectedEOF { return nil, 0, err } size += int64(n) - var blocks [][]byte // Returns encoded blocks. - blocks, err = encodeData(buf[:n], eInfo.DataBlocks, eInfo.ParityBlocks) - if err != nil { - return nil, 0, err + var enErr error + blocks, enErr = encodeData(buf[:n], eInfo.DataBlocks, eInfo.ParityBlocks) + if enErr != nil { + return nil, 0, enErr } + + // Write to all disks. err = appendFile(disks, volume, path, blocks, eInfo.Distribution, hashWriters, writeQuorum) if err != nil { return nil, 0, err diff --git a/xl-v1-metadata.go b/xl-v1-metadata.go index 03ec57227..3a3f6a159 100644 --- a/xl-v1-metadata.go +++ b/xl-v1-metadata.go @@ -171,11 +171,6 @@ func (m xlMetaV1) ObjectToPartOffset(offset int64) (partIndex int, partOffset in // Seek until object offset maps to a particular part offset. for i, part := range m.Parts { partIndex = i - // Last part can be of '0' bytes, treat it specially and - // return right here. - if part.Size == 0 { - return partIndex, partOffset, nil - } // Offset is smaller than size we have reached the proper part offset. if partOffset < part.Size { return partIndex, partOffset, nil diff --git a/xl-v1-multipart.go b/xl-v1-multipart.go index a403afb2e..d96cd44de 100644 --- a/xl-v1-multipart.go +++ b/xl-v1-multipart.go @@ -579,17 +579,28 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload // Validate each part and then commit to disk. for i, part := range parts { partIdx := currentXLMeta.ObjectPartIndex(part.PartNumber) + // All parts should have same part number. if partIdx == -1 { return "", InvalidPart{} } + + // All parts should have same ETag as previously generated. if currentXLMeta.Parts[partIdx].ETag != part.ETag { return "", BadDigest{} } + // All parts except the last part has to be atleast 5MB. if (i < len(parts)-1) && !isMinAllowedPartSize(currentXLMeta.Parts[partIdx].Size) { return "", PartTooSmall{} } + // Last part could have been uploaded as 0bytes, do not need + // to save it in final `xl.json`. + if (i == len(parts)-1) && currentXLMeta.Parts[partIdx].Size == 0 { + xlMeta.Parts = xlMeta.Parts[:i] // Skip the part. + continue + } + // Save for total object size. objectSize += currentXLMeta.Parts[partIdx].Size