1
0
mirror of https://github.com/minio/minio.git synced 2025-04-01 10:13:42 -04:00

xl/multipart: Make sure to delete temp renamed object. ()

Existing objects before overwrites are renamed to
temp location in completeMultipart. We make sure
that we delete it even if subsequenty calls fail.

Additionally move verifying of parent dir is a
file earlier to fail the entire operation.

Ref 
This commit is contained in:
Harshavardhana 2017-02-21 19:43:44 -08:00 committed by GitHub
parent fe86319c56
commit cc28765025
3 changed files with 39 additions and 26 deletions

@ -483,7 +483,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata, sha256sum) objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata, sha256sum)
} }
if err != nil { if err != nil {
errorIf(err, "Unable to create an object.") errorIf(err, "Unable to create an object. %s", r.URL.Path)
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }

@ -499,15 +499,15 @@ func (xl xlObjects) newMultipartUpload(bucket string, object string, meta map[st
// success. // success.
defer xl.deleteObject(minioMetaTmpBucket, tempUploadIDPath) defer xl.deleteObject(minioMetaTmpBucket, tempUploadIDPath)
// Attempt to rename temp upload object to actual upload path // Attempt to rename temp upload object to actual upload path object
// object rErr := renameObject(xl.storageDisks, minioMetaTmpBucket, tempUploadIDPath, minioMetaMultipartBucket, uploadIDPath, xl.writeQuorum)
if rErr := renameObject(xl.storageDisks, minioMetaTmpBucket, tempUploadIDPath, minioMetaMultipartBucket, uploadIDPath, xl.writeQuorum); rErr != nil { if rErr != nil {
return "", toObjectErr(rErr, minioMetaMultipartBucket, uploadIDPath) return "", toObjectErr(rErr, minioMetaMultipartBucket, uploadIDPath)
} }
initiated := time.Now().UTC() initiated := time.Now().UTC()
// Create or update 'uploads.json' // Create or update 'uploads.json'
if err := xl.addUploadID(bucket, object, uploadID, initiated); err != nil { if err = xl.addUploadID(bucket, object, uploadID, initiated); err != nil {
return "", err return "", err
} }
// Return success. // Return success.
@ -885,6 +885,13 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
if !xl.isUploadIDExists(bucket, object, uploadID) { if !xl.isUploadIDExists(bucket, object, uploadID) {
return ObjectInfo{}, traceError(InvalidUploadID{UploadID: uploadID}) return ObjectInfo{}, traceError(InvalidUploadID{UploadID: uploadID})
} }
// Check if an object is present as one of the parent dir.
// -- FIXME. (needs a new kind of lock).
if xl.parentDirIsObject(bucket, path.Dir(object)) {
return ObjectInfo{}, toObjectErr(traceError(errFileAccessDenied), bucket, object)
}
// Calculate s3 compatible md5sum for complete multipart. // Calculate s3 compatible md5sum for complete multipart.
s3MD5, err := getCompleteMultipartMD5(parts) s3MD5, err := getCompleteMultipartMD5(parts)
if err != nil { if err != nil {
@ -964,11 +971,6 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
} }
} }
// Check if an object is present as one of the parent dir.
if xl.parentDirIsObject(bucket, path.Dir(object)) {
return ObjectInfo{}, toObjectErr(traceError(errFileAccessDenied), bucket, object)
}
// Save the final object size and modtime. // Save the final object size and modtime.
xlMeta.Stat.Size = objectSize xlMeta.Stat.Size = objectSize
xlMeta.Stat.ModTime = time.Now().UTC() xlMeta.Stat.ModTime = time.Now().UTC()
@ -990,6 +992,7 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
if err = writeUniqueXLMetadata(onlineDisks, minioMetaTmpBucket, tempUploadIDPath, partsMetadata, xl.writeQuorum); err != nil { if err = writeUniqueXLMetadata(onlineDisks, minioMetaTmpBucket, tempUploadIDPath, partsMetadata, xl.writeQuorum); err != nil {
return ObjectInfo{}, toObjectErr(err, minioMetaTmpBucket, tempUploadIDPath) return ObjectInfo{}, toObjectErr(err, minioMetaTmpBucket, tempUploadIDPath)
} }
rErr := commitXLMetadata(onlineDisks, minioMetaTmpBucket, tempUploadIDPath, minioMetaMultipartBucket, uploadIDPath, xl.writeQuorum) rErr := commitXLMetadata(onlineDisks, minioMetaTmpBucket, tempUploadIDPath, minioMetaMultipartBucket, uploadIDPath, xl.writeQuorum)
if rErr != nil { if rErr != nil {
return ObjectInfo{}, toObjectErr(rErr, minioMetaMultipartBucket, uploadIDPath) return ObjectInfo{}, toObjectErr(rErr, minioMetaMultipartBucket, uploadIDPath)
@ -1008,13 +1011,17 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
} }
}() }()
// Rename if an object already exists to temporary location.
uniqueID := mustGetUUID()
if xl.isObject(bucket, object) { if xl.isObject(bucket, object) {
// Rename if an object already exists to temporary location.
newUniqueID := mustGetUUID()
// Delete success renamed object.
defer xl.deleteObject(minioMetaTmpBucket, newUniqueID)
// NOTE: Do not use online disks slice here. // NOTE: Do not use online disks slice here.
// The reason is that existing object should be purged // The reason is that existing object should be purged
// regardless of `xl.json` status and rolled back in case of errors. // regardless of `xl.json` status and rolled back in case of errors.
err = renameObject(xl.storageDisks, bucket, object, minioMetaTmpBucket, uniqueID, xl.writeQuorum) err = renameObject(xl.storageDisks, bucket, object, minioMetaTmpBucket, newUniqueID, xl.writeQuorum)
if err != nil { if err != nil {
return ObjectInfo{}, toObjectErr(err, bucket, object) return ObjectInfo{}, toObjectErr(err, bucket, object)
} }
@ -1038,9 +1045,6 @@ func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, upload
return ObjectInfo{}, toObjectErr(err, bucket, object) return ObjectInfo{}, toObjectErr(err, bucket, object)
} }
// Delete the previously successfully renamed object.
xl.deleteObject(minioMetaTmpBucket, uniqueID)
// Hold the lock so that two parallel // Hold the lock so that two parallel
// complete-multipart-uploads do not leave a stale // complete-multipart-uploads do not leave a stale
// uploads.json behind. // uploads.json behind.

@ -439,11 +439,25 @@ func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.
// a slash separator, we treat it like a valid operation and // a slash separator, we treat it like a valid operation and
// return success. // return success.
if isObjectDir(object, size) { if isObjectDir(object, size) {
// Check if an object is present as one of the parent dir.
// -- FIXME. (needs a new kind of lock).
if xl.parentDirIsObject(bucket, path.Dir(object)) {
return ObjectInfo{}, toObjectErr(traceError(errFileAccessDenied), bucket, object)
}
return dirObjectInfo(bucket, object, size, metadata), nil return dirObjectInfo(bucket, object, size, metadata), nil
} }
// Validate put object input args.
if err = checkPutObjectArgs(bucket, object, xl); err != nil { if err = checkPutObjectArgs(bucket, object, xl); err != nil {
return ObjectInfo{}, err return ObjectInfo{}, err
} }
// Check if an object is present as one of the parent dir.
// -- FIXME. (needs a new kind of lock).
if xl.parentDirIsObject(bucket, path.Dir(object)) {
return ObjectInfo{}, toObjectErr(traceError(errFileAccessDenied), bucket, object)
}
// No metadata is set, allocate a new one. // No metadata is set, allocate a new one.
if metadata == nil { if metadata == nil {
metadata = make(map[string]string) metadata = make(map[string]string)
@ -526,7 +540,7 @@ func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.
// Compute part name // Compute part name
partName := "part." + strconv.Itoa(partIdx) partName := "part." + strconv.Itoa(partIdx)
// Compute the path of current part // Compute the path of current part
tempErasureObj := path.Join(uniqueID, partName) tempErasureObj := pathJoin(uniqueID, partName)
// Calculate the size of the current part, if size is unknown, curPartSize wil be unknown too. // Calculate the size of the current part, if size is unknown, curPartSize wil be unknown too.
// allowEmptyPart will always be true if this is the first part and false otherwise. // allowEmptyPart will always be true if this is the first part and false otherwise.
@ -645,16 +659,11 @@ func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.
} }
} }
// Check if an object is present as one of the parent dir. if xl.isObject(bucket, object) {
// -- FIXME. (needs a new kind of lock).
if xl.parentDirIsObject(bucket, path.Dir(object)) {
return ObjectInfo{}, toObjectErr(traceError(errFileAccessDenied), bucket, object)
}
// Rename if an object already exists to temporary location. // Rename if an object already exists to temporary location.
newUniqueID := mustGetUUID() newUniqueID := mustGetUUID()
if xl.isObject(bucket, object) {
// Delete the temporary copy of the object that existed before this PutObject request. // Delete successfully renamed object.
defer xl.deleteObject(minioMetaTmpBucket, newUniqueID) defer xl.deleteObject(minioMetaTmpBucket, newUniqueID)
// NOTE: Do not use online disks slice here. // NOTE: Do not use online disks slice here.