mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
xl/fs: Move few functions into common code. (#1453)
- PutObject() - PutObjectPart() - NewMultipartUpload() - AbortMultipartUpload() Implementations across both FS and XL object layer share common implementation.
This commit is contained in:
parent
3bf3d18f1f
commit
afd59c45dc
@ -17,11 +17,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -250,124 +247,19 @@ func (fs fsObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMark
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMultipartUpload - initialize a new multipart upload, returns a unique id.
|
||||||
func (fs fsObjects) NewMultipartUpload(bucket, object string) (string, error) {
|
func (fs fsObjects) NewMultipartUpload(bucket, object string) (string, error) {
|
||||||
// Verify if bucket name is valid.
|
return newMultipartUploadCommon(fs.storage, bucket, object)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return "", (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
// Verify if object name is valid.
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
|
||||||
}
|
|
||||||
// Verify whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(fs.storage, bucket); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := fs.storage.StatVol(minioMetaBucket); err != nil {
|
|
||||||
if err == errVolumeNotFound {
|
|
||||||
err = fs.storage.MakeVol(minioMetaBucket)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
uuid, err := uuid.New()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
uploadID := uuid.String()
|
|
||||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
|
||||||
if _, err = fs.storage.StatFile(minioMetaBucket, uploadIDPath); err != nil {
|
|
||||||
if err != errFileNotFound {
|
|
||||||
return "", (toObjectErr(err, minioMetaBucket, uploadIDPath))
|
|
||||||
}
|
|
||||||
// uploadIDPath doesn't exist, so create empty file to reserve the name
|
|
||||||
var w io.WriteCloser
|
|
||||||
if w, err = fs.storage.CreateFile(minioMetaBucket, uploadIDPath); err == nil {
|
|
||||||
// Close the writer.
|
|
||||||
if err = w.Close(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "", toObjectErr(err, minioMetaBucket, uploadIDPath)
|
|
||||||
}
|
|
||||||
return uploadID, nil
|
|
||||||
}
|
|
||||||
// uploadIDPath already exists.
|
|
||||||
// loop again to try with different uuid generated.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectPart - writes the multipart upload chunks.
|
// PutObjectPart - writes the multipart upload chunks.
|
||||||
func (fs fsObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) {
|
func (fs fsObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) {
|
||||||
// Verify if bucket is valid.
|
newMD5Hex, err := putObjectPartCommon(fs.storage, bucket, object, uploadID, partID, size, data, md5Hex)
|
||||||
if !IsValidBucketName(bucket) {
|
if err != nil {
|
||||||
return "", BucketNameInvalid{Bucket: bucket}
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
|
||||||
}
|
|
||||||
// Verify whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(fs.storage, bucket); err != nil {
|
|
||||||
return "", err
|
return "", err
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if status, err := isUploadIDExists(fs.storage, bucket, object, uploadID); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !status {
|
|
||||||
return "", InvalidUploadID{UploadID: uploadID}
|
|
||||||
}
|
|
||||||
|
|
||||||
partSuffix := fmt.Sprintf("%s.%d", uploadID, partID)
|
partSuffix := fmt.Sprintf("%s.%d", uploadID, partID)
|
||||||
partSuffixPath := path.Join(bucket, object, partSuffix)
|
partSuffixPath := path.Join(bucket, object, partSuffix)
|
||||||
fileWriter, err := fs.storage.CreateFile(minioMetaBucket, partSuffixPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err, bucket, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize md5 writer.
|
|
||||||
md5Writer := md5.New()
|
|
||||||
|
|
||||||
// Instantiate a new multi writer.
|
|
||||||
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
|
||||||
|
|
||||||
// Instantiate checksum hashers and create a multiwriter.
|
|
||||||
if size > 0 {
|
|
||||||
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (toObjectErr(err))
|
|
||||||
}
|
|
||||||
// Reader shouldn't have more data what mentioned in size argument.
|
|
||||||
// reading one more byte from the reader to validate it.
|
|
||||||
// expected to fail, success validates existence of more data in the reader.
|
|
||||||
if _, err = io.CopyN(ioutil.Discard, data, 1); err == nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (UnExpectedDataSize{Size: int(size)})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(multiWriter, data); err != nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (toObjectErr(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
|
||||||
if md5Hex != "" {
|
|
||||||
if newMD5Hex != md5Hex {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (BadDigest{md5Hex, newMD5Hex})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = fileWriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
partSuffixMD5 := fmt.Sprintf("%s.%d.%s", uploadID, partID, newMD5Hex)
|
partSuffixMD5 := fmt.Sprintf("%s.%d.%s", uploadID, partID, newMD5Hex)
|
||||||
partSuffixMD5Path := path.Join(bucket, object, partSuffixMD5)
|
partSuffixMD5Path := path.Join(bucket, object, partSuffixMD5)
|
||||||
err = fs.storage.RenameFile(minioMetaBucket, partSuffixPath, minioMetaBucket, partSuffixMD5Path)
|
err = fs.storage.RenameFile(minioMetaBucket, partSuffixPath, minioMetaBucket, partSuffixMD5Path)
|
||||||
@ -495,54 +387,19 @@ func (fs fsObjects) CompleteMultipartUpload(bucket string, object string, upload
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup all the parts.
|
// Cleanup all the parts.
|
||||||
fs.removeMultipartUpload(bucket, object, uploadID)
|
fs.cleanupUploadedParts(bucket, object, uploadID)
|
||||||
|
|
||||||
// Return md5sum.
|
// Return md5sum.
|
||||||
return s3MD5, nil
|
return s3MD5, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs fsObjects) removeMultipartUpload(bucket, object, uploadID string) error {
|
// Wrapper to which removes all the uploaded parts after a successful
|
||||||
// Verify if bucket is valid.
|
// complete multipart upload.
|
||||||
if !IsValidBucketName(bucket) {
|
func (fs fsObjects) cleanupUploadedParts(bucket, object, uploadID string) error {
|
||||||
return (BucketNameInvalid{Bucket: bucket})
|
return abortMultipartUploadCommon(fs.storage, bucket, object, uploadID)
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return (ObjectNameInvalid{Bucket: bucket, Object: object})
|
|
||||||
}
|
|
||||||
|
|
||||||
marker := ""
|
|
||||||
for {
|
|
||||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
|
||||||
fileInfos, eof, err := fs.storage.ListFiles(minioMetaBucket, uploadIDPath, marker, false, 1000)
|
|
||||||
if err != nil {
|
|
||||||
if err == errFileNotFound {
|
|
||||||
return (InvalidUploadID{UploadID: uploadID})
|
|
||||||
}
|
|
||||||
return toObjectErr(err)
|
|
||||||
}
|
|
||||||
for _, fileInfo := range fileInfos {
|
|
||||||
fs.storage.DeleteFile(minioMetaBucket, fileInfo.Name)
|
|
||||||
marker = fileInfo.Name
|
|
||||||
}
|
|
||||||
if eof {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AbortMultipartUpload - aborts a multipart upload.
|
||||||
func (fs fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
func (fs fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
||||||
// Verify if bucket is valid.
|
return abortMultipartUploadCommon(fs.storage, bucket, object, uploadID)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return (ObjectNameInvalid{Bucket: bucket, Object: object})
|
|
||||||
}
|
|
||||||
if status, err := isUploadIDExists(fs.storage, bucket, object, uploadID); err != nil {
|
|
||||||
return err
|
|
||||||
} else if !status {
|
|
||||||
return (InvalidUploadID{UploadID: uploadID})
|
|
||||||
}
|
|
||||||
return fs.removeMultipartUpload(bucket, object, uploadID)
|
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -124,73 +122,9 @@ func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PutObject - create an object.
|
||||||
func (fs fsObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) {
|
func (fs fsObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) {
|
||||||
// Verify if bucket is valid.
|
return putObjectCommon(fs.storage, bucket, object, size, data, metadata)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return "", (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", (ObjectNameInvalid{
|
|
||||||
Bucket: bucket,
|
|
||||||
Object: object,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Check whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(fs.storage, bucket); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileWriter, err := fs.storage.CreateFile(bucket, object)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err, bucket, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize md5 writer.
|
|
||||||
md5Writer := md5.New()
|
|
||||||
|
|
||||||
// Instantiate a new multi writer.
|
|
||||||
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
|
||||||
|
|
||||||
// Instantiate checksum hashers and create a multiwriter.
|
|
||||||
if size > 0 {
|
|
||||||
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
|
||||||
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
|
||||||
return "", clErr
|
|
||||||
}
|
|
||||||
return "", toObjectErr(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(multiWriter, data); err != nil {
|
|
||||||
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
|
||||||
return "", clErr
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
|
||||||
// md5Hex representation.
|
|
||||||
var md5Hex string
|
|
||||||
if len(metadata) != 0 {
|
|
||||||
md5Hex = metadata["md5Sum"]
|
|
||||||
}
|
|
||||||
if md5Hex != "" {
|
|
||||||
if newMD5Hex != md5Hex {
|
|
||||||
if err = safeCloseAndRemove(fileWriter); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "", BadDigest{md5Hex, newMD5Hex}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = fileWriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return md5sum, successfully wrote object.
|
|
||||||
return newMD5Hex, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs fsObjects) DeleteObject(bucket, object string) error {
|
func (fs fsObjects) DeleteObject(bucket, object string) error {
|
||||||
|
194
object-common-multipart.go
Normal file
194
object-common-multipart.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/skyrings/skyring-common/tools/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
/// Common multipart object layer functions.
|
||||||
|
|
||||||
|
// newMultipartUploadCommon - initialize a new multipart, is a common
|
||||||
|
// function for both object layers.
|
||||||
|
func newMultipartUploadCommon(storage StorageAPI, bucket string, object string) (uploadID string, err error) {
|
||||||
|
// Verify if bucket name is valid.
|
||||||
|
if !IsValidBucketName(bucket) {
|
||||||
|
return "", (BucketNameInvalid{Bucket: bucket})
|
||||||
|
}
|
||||||
|
// Verify if object name is valid.
|
||||||
|
if !IsValidObjectName(object) {
|
||||||
|
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify whether the bucket exists.
|
||||||
|
if isExist, err := isBucketExist(storage, bucket); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !isExist {
|
||||||
|
return "", BucketNotFound{Bucket: bucket}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := storage.StatVol(minioMetaBucket); err != nil {
|
||||||
|
if err == errVolumeNotFound {
|
||||||
|
err = storage.MakeVol(minioMetaBucket)
|
||||||
|
if err != nil {
|
||||||
|
return "", toObjectErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loops through until successfully generates a new unique upload id.
|
||||||
|
for {
|
||||||
|
uuid, err := uuid.New()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
uploadID := uuid.String()
|
||||||
|
uploadIDPath := path.Join(bucket, object, uploadID)
|
||||||
|
if _, err = storage.StatFile(minioMetaBucket, uploadIDPath); err != nil {
|
||||||
|
if err != errFileNotFound {
|
||||||
|
return "", toObjectErr(err, minioMetaBucket, uploadIDPath)
|
||||||
|
}
|
||||||
|
// uploadIDPath doesn't exist, so create empty file to reserve the name
|
||||||
|
var w io.WriteCloser
|
||||||
|
if w, err = storage.CreateFile(minioMetaBucket, uploadIDPath); err == nil {
|
||||||
|
// Close the writer.
|
||||||
|
if err = w.Close(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "", toObjectErr(err, minioMetaBucket, uploadIDPath)
|
||||||
|
}
|
||||||
|
return uploadID, nil
|
||||||
|
}
|
||||||
|
// uploadIDPath already exists.
|
||||||
|
// loop again to try with different uuid generated.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// putObjectPartCommon - put object part.
|
||||||
|
func putObjectPartCommon(storage StorageAPI, bucket string, object string, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) {
|
||||||
|
// Verify if bucket is valid.
|
||||||
|
if !IsValidBucketName(bucket) {
|
||||||
|
return "", BucketNameInvalid{Bucket: bucket}
|
||||||
|
}
|
||||||
|
if !IsValidObjectName(object) {
|
||||||
|
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify whether the bucket exists.
|
||||||
|
if isExist, err := isBucketExist(storage, bucket); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !isExist {
|
||||||
|
return "", BucketNotFound{Bucket: bucket}
|
||||||
|
}
|
||||||
|
|
||||||
|
if status, err := isUploadIDExists(storage, bucket, object, uploadID); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !status {
|
||||||
|
return "", InvalidUploadID{UploadID: uploadID}
|
||||||
|
}
|
||||||
|
|
||||||
|
partSuffix := fmt.Sprintf("%s.%d", uploadID, partID)
|
||||||
|
partSuffixPath := path.Join(bucket, object, partSuffix)
|
||||||
|
fileWriter, err := storage.CreateFile(minioMetaBucket, partSuffixPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", toObjectErr(err, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize md5 writer.
|
||||||
|
md5Writer := md5.New()
|
||||||
|
|
||||||
|
// Instantiate a new multi writer.
|
||||||
|
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
||||||
|
|
||||||
|
// Instantiate checksum hashers and create a multiwriter.
|
||||||
|
if size > 0 {
|
||||||
|
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
||||||
|
safeCloseAndRemove(fileWriter)
|
||||||
|
return "", toObjectErr(err, bucket, object)
|
||||||
|
}
|
||||||
|
// Reader shouldn't have more data what mentioned in size argument.
|
||||||
|
// reading one more byte from the reader to validate it.
|
||||||
|
// expected to fail, success validates existence of more data in the reader.
|
||||||
|
if _, err = io.CopyN(ioutil.Discard, data, 1); err == nil {
|
||||||
|
safeCloseAndRemove(fileWriter)
|
||||||
|
return "", UnExpectedDataSize{Size: int(size)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err = io.Copy(multiWriter, data); err != nil {
|
||||||
|
safeCloseAndRemove(fileWriter)
|
||||||
|
return "", toObjectErr(err, bucket, object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
||||||
|
if md5Hex != "" {
|
||||||
|
if newMD5Hex != md5Hex {
|
||||||
|
safeCloseAndRemove(fileWriter)
|
||||||
|
return "", BadDigest{md5Hex, newMD5Hex}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = fileWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return newMD5Hex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// abortMultipartUploadCommon - aborts a multipart upload, common
|
||||||
|
// function used by both object layers.
|
||||||
|
func abortMultipartUploadCommon(storage StorageAPI, bucket, object, uploadID string) error {
|
||||||
|
// Verify if bucket is valid.
|
||||||
|
if !IsValidBucketName(bucket) {
|
||||||
|
return BucketNameInvalid{Bucket: bucket}
|
||||||
|
}
|
||||||
|
if !IsValidObjectName(object) {
|
||||||
|
return ObjectNameInvalid{Bucket: bucket, Object: object}
|
||||||
|
}
|
||||||
|
if status, err := isUploadIDExists(storage, bucket, object, uploadID); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !status {
|
||||||
|
return InvalidUploadID{UploadID: uploadID}
|
||||||
|
}
|
||||||
|
|
||||||
|
markerPath := ""
|
||||||
|
for {
|
||||||
|
uploadIDPath := path.Join(bucket, object, uploadID)
|
||||||
|
fileInfos, eof, err := storage.ListFiles(minioMetaBucket, uploadIDPath, markerPath, false, 1000)
|
||||||
|
if err != nil {
|
||||||
|
if err == errFileNotFound {
|
||||||
|
return InvalidUploadID{UploadID: uploadID}
|
||||||
|
}
|
||||||
|
return toObjectErr(err)
|
||||||
|
}
|
||||||
|
for _, fileInfo := range fileInfos {
|
||||||
|
storage.DeleteFile(minioMetaBucket, fileInfo.Name)
|
||||||
|
markerPath = fileInfo.Name
|
||||||
|
}
|
||||||
|
if eof {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -17,6 +17,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
@ -98,6 +101,76 @@ func deleteBucket(storage StorageAPI, bucket string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// putObjectCommon - create an object, is a common function for both object layers.
|
||||||
|
func putObjectCommon(storage StorageAPI, bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) {
|
||||||
|
// Verify if bucket is valid.
|
||||||
|
if !IsValidBucketName(bucket) {
|
||||||
|
return "", (BucketNameInvalid{Bucket: bucket})
|
||||||
|
}
|
||||||
|
if !IsValidObjectName(object) {
|
||||||
|
return "", (ObjectNameInvalid{
|
||||||
|
Bucket: bucket,
|
||||||
|
Object: object,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Check whether the bucket exists.
|
||||||
|
if isExist, err := isBucketExist(storage, bucket); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !isExist {
|
||||||
|
return "", BucketNotFound{Bucket: bucket}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileWriter, err := storage.CreateFile(bucket, object)
|
||||||
|
if err != nil {
|
||||||
|
return "", toObjectErr(err, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize md5 writer.
|
||||||
|
md5Writer := md5.New()
|
||||||
|
|
||||||
|
// Instantiate a new multi writer.
|
||||||
|
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
||||||
|
|
||||||
|
// Instantiate checksum hashers and create a multiwriter.
|
||||||
|
if size > 0 {
|
||||||
|
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
||||||
|
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
||||||
|
return "", clErr
|
||||||
|
}
|
||||||
|
return "", toObjectErr(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err = io.Copy(multiWriter, data); err != nil {
|
||||||
|
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
||||||
|
return "", clErr
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
||||||
|
// md5Hex representation.
|
||||||
|
var md5Hex string
|
||||||
|
if len(metadata) != 0 {
|
||||||
|
md5Hex = metadata["md5Sum"]
|
||||||
|
}
|
||||||
|
if md5Hex != "" {
|
||||||
|
if newMD5Hex != md5Hex {
|
||||||
|
if err = safeCloseAndRemove(fileWriter); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", BadDigest{md5Hex, newMD5Hex}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = fileWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return md5sum, successfully wrote object.
|
||||||
|
return newMD5Hex, nil
|
||||||
|
}
|
||||||
|
|
||||||
// isUploadIDExists - verify if a given uploadID exists and is valid.
|
// isUploadIDExists - verify if a given uploadID exists and is valid.
|
||||||
func isUploadIDExists(storage StorageAPI, bucket, object, uploadID string) (bool, error) {
|
func isUploadIDExists(storage StorageAPI, bucket, object, uploadID string) (bool, error) {
|
||||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
uploadIDPath := path.Join(bucket, object, uploadID)
|
||||||
|
@ -17,12 +17,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -291,125 +288,19 @@ func (xl xlObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMark
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMultipartUpload - initialize a new multipart upload, returns a unique id.
|
||||||
func (xl xlObjects) NewMultipartUpload(bucket, object string) (string, error) {
|
func (xl xlObjects) NewMultipartUpload(bucket, object string) (string, error) {
|
||||||
// Verify if bucket name is valid.
|
return newMultipartUploadCommon(xl.storage, bucket, object)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return "", (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
// Verify if object name is valid.
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
|
||||||
}
|
|
||||||
// Verify whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(xl.storage, bucket); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := xl.storage.StatVol(minioMetaBucket); err != nil {
|
|
||||||
if err == errVolumeNotFound {
|
|
||||||
err = xl.storage.MakeVol(minioMetaBucket)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
uuid, err := uuid.New()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
uploadID := uuid.String()
|
|
||||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
|
||||||
if _, err = xl.storage.StatFile(minioMetaBucket, uploadIDPath); err != nil {
|
|
||||||
if err != errFileNotFound {
|
|
||||||
return "", (toObjectErr(err, minioMetaBucket, uploadIDPath))
|
|
||||||
}
|
|
||||||
// uploadIDPath doesn't exist, so create empty file to reserve the name
|
|
||||||
var w io.WriteCloser
|
|
||||||
if w, err = xl.storage.CreateFile(minioMetaBucket, uploadIDPath); err == nil {
|
|
||||||
// Close the writer.
|
|
||||||
if err = w.Close(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "", toObjectErr(err, minioMetaBucket, uploadIDPath)
|
|
||||||
}
|
|
||||||
return uploadID, nil
|
|
||||||
}
|
|
||||||
// uploadIDPath already exists.
|
|
||||||
// loop again to try with different uuid generated.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectPart - writes the multipart upload chunks.
|
// PutObjectPart - writes the multipart upload chunks.
|
||||||
func (xl xlObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) {
|
func (xl xlObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) {
|
||||||
// Verify if bucket is valid.
|
newMD5Hex, err := putObjectPartCommon(xl.storage, bucket, object, uploadID, partID, size, data, md5Hex)
|
||||||
if !IsValidBucketName(bucket) {
|
if err != nil {
|
||||||
return "", BucketNameInvalid{Bucket: bucket}
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", ObjectNameInvalid{Bucket: bucket, Object: object}
|
|
||||||
}
|
|
||||||
// Verify whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(xl.storage, bucket); err != nil {
|
|
||||||
return "", err
|
return "", err
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if status, err := isUploadIDExists(xl.storage, bucket, object, uploadID); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !status {
|
|
||||||
return "", InvalidUploadID{UploadID: uploadID}
|
|
||||||
}
|
|
||||||
|
|
||||||
partSuffix := fmt.Sprintf("%s.%d", uploadID, partID)
|
partSuffix := fmt.Sprintf("%s.%d", uploadID, partID)
|
||||||
partSuffixPath := path.Join(bucket, object, partSuffix)
|
partSuffixPath := path.Join(bucket, object, partSuffix)
|
||||||
fileWriter, err := xl.storage.CreateFile(minioMetaBucket, partSuffixPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err, bucket, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize md5 writer.
|
|
||||||
md5Writer := md5.New()
|
|
||||||
|
|
||||||
// Instantiate a new multi writer.
|
|
||||||
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
|
||||||
|
|
||||||
// Instantiate checksum hashers and create a multiwriter.
|
|
||||||
if size > 0 {
|
|
||||||
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (toObjectErr(err))
|
|
||||||
}
|
|
||||||
// Reader shouldn't have more data what mentioned in size argument.
|
|
||||||
// reading one more byte from the reader to validate it.
|
|
||||||
// expected to fail, success validates existence of more data in the reader.
|
|
||||||
if _, err = io.CopyN(ioutil.Discard, data, 1); err == nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (UnExpectedDataSize{Size: int(size)})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(multiWriter, data); err != nil {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (toObjectErr(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
|
||||||
if md5Hex != "" {
|
|
||||||
if newMD5Hex != md5Hex {
|
|
||||||
safeCloseAndRemove(fileWriter)
|
|
||||||
return "", (BadDigest{md5Hex, newMD5Hex})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = fileWriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
partSuffixMD5 := fmt.Sprintf("%s.%.5d.%s", uploadID, partID, newMD5Hex)
|
partSuffixMD5 := fmt.Sprintf("%s.%.5d.%s", uploadID, partID, newMD5Hex)
|
||||||
partSuffixMD5Path := path.Join(bucket, object, partSuffixMD5)
|
partSuffixMD5Path := path.Join(bucket, object, partSuffixMD5)
|
||||||
err = xl.storage.RenameFile(minioMetaBucket, partSuffixPath, minioMetaBucket, partSuffixMD5Path)
|
err = xl.storage.RenameFile(minioMetaBucket, partSuffixPath, minioMetaBucket, partSuffixMD5Path)
|
||||||
@ -548,38 +439,7 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
|
|||||||
return s3MD5, nil
|
return s3MD5, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortMultipartUpload - abort multipart upload.
|
// AbortMultipartUpload - aborts a multipart upload.
|
||||||
func (xl xlObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
func (xl xlObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
||||||
// Verify if bucket is valid.
|
return abortMultipartUploadCommon(xl.storage, bucket, object, uploadID)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return (ObjectNameInvalid{Bucket: bucket, Object: object})
|
|
||||||
}
|
|
||||||
if status, err := isUploadIDExists(xl.storage, bucket, object, uploadID); err != nil {
|
|
||||||
return err
|
|
||||||
} else if !status {
|
|
||||||
return (InvalidUploadID{UploadID: uploadID})
|
|
||||||
}
|
|
||||||
|
|
||||||
markerPath := ""
|
|
||||||
for {
|
|
||||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
|
||||||
fileInfos, eof, err := xl.storage.ListFiles(minioMetaBucket, uploadIDPath, markerPath, false, 1000)
|
|
||||||
if err != nil {
|
|
||||||
if err == errFileNotFound {
|
|
||||||
return (InvalidUploadID{UploadID: uploadID})
|
|
||||||
}
|
|
||||||
return toObjectErr(err)
|
|
||||||
}
|
|
||||||
for _, fileInfo := range fileInfos {
|
|
||||||
xl.storage.DeleteFile(minioMetaBucket, fileInfo.Name)
|
|
||||||
markerPath = fileInfo.Name
|
|
||||||
}
|
|
||||||
if eof {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
@ -167,73 +165,9 @@ func (xl xlObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PutObject - create an object.
|
||||||
func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) {
|
func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) {
|
||||||
// Verify if bucket is valid.
|
return putObjectCommon(xl.storage, bucket, object, size, data, metadata)
|
||||||
if !IsValidBucketName(bucket) {
|
|
||||||
return "", (BucketNameInvalid{Bucket: bucket})
|
|
||||||
}
|
|
||||||
if !IsValidObjectName(object) {
|
|
||||||
return "", (ObjectNameInvalid{
|
|
||||||
Bucket: bucket,
|
|
||||||
Object: object,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Check whether the bucket exists.
|
|
||||||
if isExist, err := isBucketExist(xl.storage, bucket); err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if !isExist {
|
|
||||||
return "", BucketNotFound{Bucket: bucket}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileWriter, err := xl.storage.CreateFile(bucket, object)
|
|
||||||
if err != nil {
|
|
||||||
return "", toObjectErr(err, bucket, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize md5 writer.
|
|
||||||
md5Writer := md5.New()
|
|
||||||
|
|
||||||
// Instantiate a new multi writer.
|
|
||||||
multiWriter := io.MultiWriter(md5Writer, fileWriter)
|
|
||||||
|
|
||||||
// Instantiate checksum hashers and create a multiwriter.
|
|
||||||
if size > 0 {
|
|
||||||
if _, err = io.CopyN(multiWriter, data, size); err != nil {
|
|
||||||
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
|
||||||
return "", clErr
|
|
||||||
}
|
|
||||||
return "", toObjectErr(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(multiWriter, data); err != nil {
|
|
||||||
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
|
||||||
return "", clErr
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil))
|
|
||||||
// md5Hex representation.
|
|
||||||
var md5Hex string
|
|
||||||
if len(metadata) != 0 {
|
|
||||||
md5Hex = metadata["md5Sum"]
|
|
||||||
}
|
|
||||||
if md5Hex != "" {
|
|
||||||
if newMD5Hex != md5Hex {
|
|
||||||
if err = safeCloseAndRemove(fileWriter); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "", BadDigest{md5Hex, newMD5Hex}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = fileWriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return md5sum, successfully wrote object.
|
|
||||||
return newMD5Hex, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xl xlObjects) DeleteObject(bucket, object string) error {
|
func (xl xlObjects) DeleteObject(bucket, object string) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user