diff --git a/cmd/erasure-healing.go b/cmd/erasure-healing.go index 654df6cb0..7ff773c74 100644 --- a/cmd/erasure-healing.go +++ b/cmd/erasure-healing.go @@ -505,6 +505,11 @@ func (er erasureObjects) healObject(ctx context.Context, bucket string, object s // record the index of the updated disks partsMetadata[i].Erasure.Index = i + 1 + // dataDir should be empty when + // - transitionStatus is complete and not in restored state + if partsMetadata[i].TransitionStatus == lifecycle.TransitionComplete && !isRestoredObjectOnDisk(partsMetadata[i].Metadata) { + partsMetadata[i].DataDir = "" + } // Attempt a rename now from healed data to final location. if err = disk.RenameData(ctx, minioMetaTmpBucket, tmpID, partsMetadata[i], bucket, object); err != nil { logger.LogIf(ctx, err) diff --git a/cmd/erasure-multipart.go b/cmd/erasure-multipart.go index 36b837965..eebaaa7fe 100644 --- a/cmd/erasure-multipart.go +++ b/cmd/erasure-multipart.go @@ -575,7 +575,7 @@ func (er erasureObjects) PutObjectPart(ctx context.Context, bucket, object, uplo PartNumber: partID, ETag: md5hex, LastModified: fi.ModTime, - Size: fi.Size, + Size: n, ActualSize: data.ActualSize(), }, nil } diff --git a/cmd/erasure-object.go b/cmd/erasure-object.go index eab7a2e00..f90e54f9b 100644 --- a/cmd/erasure-object.go +++ b/cmd/erasure-object.go @@ -1475,37 +1475,15 @@ func (er erasureObjects) restoreTransitionedObject(ctx context.Context, bucket s if err != nil { return setRestoreHeaderFn(oi, err) } + if pInfo.Size != partInfo.Size { + return setRestoreHeaderFn(oi, InvalidObjectState{Bucket: bucket, Object: object}) + } uploadedParts = append(uploadedParts, CompletePart{ PartNumber: pInfo.PartNumber, ETag: pInfo.ETag, }) } - - partsMatch := true - // validate parts created via multipart - if len(oi.Parts) == len(uploadedParts) { - for i, pi := range oi.Parts { - if uploadedParts[i].ETag != pi.ETag { - partsMatch = false - break - } - } - } else { - partsMatch = false - } - - if !partsMatch { - return setRestoreHeaderFn(oi, InvalidObjectState{Bucket: bucket, Object: object}) - } - _, err = er.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, ObjectOptions{ - MTime: oi.ModTime, - Versioned: globalBucketVersioningSys.Enabled(bucket), - VersionSuspended: globalBucketVersioningSys.Suspended(bucket), - }) - if err != nil { - uploadIDPath := er.getUploadIDDir(bucket, object, uploadID) - return setRestoreHeaderFn(oi, toObjectErr(err, minioMetaMultipartBucket, uploadIDPath)) - } - return setRestoreHeaderFn(oi, nil) + MTime: oi.ModTime}) + return setRestoreHeaderFn(oi, err) } diff --git a/cmd/object-api-errors.go b/cmd/object-api-errors.go index b97a90b70..f18dc861f 100644 --- a/cmd/object-api-errors.go +++ b/cmd/object-api-errors.go @@ -495,7 +495,7 @@ func (e TransitionStorageClassNotFound) Error() string { type InvalidObjectState GenericError func (e InvalidObjectState) Error() string { - return "The operation is not valid for the current state of the object" + e.Bucket + "/" + e.Object + return "The operation is not valid for the current state of the object " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")" } /// Bucket related errors. diff --git a/cmd/xl-storage.go b/cmd/xl-storage.go index 39564c89b..5e4a2dc12 100644 --- a/cmd/xl-storage.go +++ b/cmd/xl-storage.go @@ -1858,8 +1858,7 @@ func (s *xlStorage) RenameData(ctx context.Context, srcVolume, srcPath string, f var srcDataPath string var dstDataPath string dataDir := retainSlash(fi.DataDir) - // no need to rename dataDir paths for objects that are in transitionComplete state. - if dataDir != "" && fi.TransitionStatus != lifecycle.TransitionComplete { + if dataDir != "" { srcDataPath = retainSlash(pathJoin(srcVolumeDir, srcPath, dataDir)) // make sure to always use path.Join here, do not use pathJoin as // it would additionally add `/` at the end and it comes in the