mirror of
https://github.com/minio/minio.git
synced 2025-05-21 17:43:48 -04:00
fix: avoid sending errors on missing objects on locked buckets (#10994)
make sure multi-object delete returned errors that are AWS S3 compatible
This commit is contained in:
parent
e6fa410778
commit
bdd094bc39
@ -32,13 +32,27 @@ type DeletedObject struct {
|
|||||||
// Replication status of DeleteMarker
|
// Replication status of DeleteMarker
|
||||||
DeleteMarkerReplicationStatus string `xml:"DeleteMarkerReplicationStatus,omitempty"`
|
DeleteMarkerReplicationStatus string `xml:"DeleteMarkerReplicationStatus,omitempty"`
|
||||||
// MTime of DeleteMarker on source that needs to be propagated to replica
|
// MTime of DeleteMarker on source that needs to be propagated to replica
|
||||||
DeleteMarkerMTime time.Time `xml:"DeleteMarkerMTime,omitempty"`
|
DeleteMarkerMTime DeleteMarkerMTime `xml:"DeleteMarkerMTime,omitempty"`
|
||||||
// Status of versioned delete (of object or DeleteMarker)
|
// Status of versioned delete (of object or DeleteMarker)
|
||||||
VersionPurgeStatus VersionPurgeStatusType `xml:"VersionPurgeStatus,omitempty"`
|
VersionPurgeStatus VersionPurgeStatusType `xml:"VersionPurgeStatus,omitempty"`
|
||||||
// PurgeTransitioned is nonempty if object is in transition tier
|
// PurgeTransitioned is nonempty if object is in transition tier
|
||||||
PurgeTransitioned string `xml:"PurgeTransitioned,omitempty"`
|
PurgeTransitioned string `xml:"PurgeTransitioned,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMarkerMTime is an embedded type containing time.Time for XML marshal
|
||||||
|
type DeleteMarkerMTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalXML encodes expiration date if it is non-zero and encodes
|
||||||
|
// empty string otherwise
|
||||||
|
func (t DeleteMarkerMTime) MarshalXML(e *xml.Encoder, startElement xml.StartElement) error {
|
||||||
|
if t.Time.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.EncodeElement(t.Time.Format(time.RFC3339), startElement)
|
||||||
|
}
|
||||||
|
|
||||||
// ObjectToDelete carries key name for the object to delete.
|
// ObjectToDelete carries key name for the object to delete.
|
||||||
type ObjectToDelete struct {
|
type ObjectToDelete struct {
|
||||||
ObjectName string `xml:"Key"`
|
ObjectName string `xml:"Key"`
|
||||||
|
@ -124,6 +124,7 @@ const (
|
|||||||
ErrObjectRestoreAlreadyInProgress
|
ErrObjectRestoreAlreadyInProgress
|
||||||
ErrNoSuchKey
|
ErrNoSuchKey
|
||||||
ErrNoSuchUpload
|
ErrNoSuchUpload
|
||||||
|
ErrInvalidVersionID
|
||||||
ErrNoSuchVersion
|
ErrNoSuchVersion
|
||||||
ErrNotImplemented
|
ErrNotImplemented
|
||||||
ErrPreconditionFailed
|
ErrPreconditionFailed
|
||||||
@ -558,11 +559,16 @@ var errorCodes = errorCodeMap{
|
|||||||
Description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
Description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
||||||
HTTPStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
ErrNoSuchVersion: {
|
ErrInvalidVersionID: {
|
||||||
Code: "InvalidArgument",
|
Code: "InvalidArgument",
|
||||||
Description: "Invalid version id specified",
|
Description: "Invalid version id specified",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
ErrNoSuchVersion: {
|
||||||
|
Code: "NoSuchVersion",
|
||||||
|
Description: "The specified version does not exist.",
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
},
|
||||||
ErrNotImplemented: {
|
ErrNotImplemented: {
|
||||||
Code: "NotImplemented",
|
Code: "NotImplemented",
|
||||||
Description: "A header you provided implies functionality that is not implemented",
|
Description: "A header you provided implies functionality that is not implemented",
|
||||||
@ -1886,6 +1892,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
|
|||||||
apiErr = ErrNoSuchKey
|
apiErr = ErrNoSuchKey
|
||||||
case MethodNotAllowed:
|
case MethodNotAllowed:
|
||||||
apiErr = ErrMethodNotAllowed
|
apiErr = ErrMethodNotAllowed
|
||||||
|
case InvalidVersionID:
|
||||||
|
apiErr = ErrInvalidVersionID
|
||||||
case VersionNotFound:
|
case VersionNotFound:
|
||||||
apiErr = ErrNoSuchVersion
|
apiErr = ErrNoSuchVersion
|
||||||
case ObjectAlreadyExists:
|
case ObjectAlreadyExists:
|
||||||
|
@ -495,7 +495,6 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
|
|||||||
})
|
})
|
||||||
deletedObjects := make([]DeletedObject, len(deleteObjects.Objects))
|
deletedObjects := make([]DeletedObject, len(deleteObjects.Objects))
|
||||||
for i := range errs {
|
for i := range errs {
|
||||||
|
|
||||||
dindex := objectsToDelete[ObjectToDelete{
|
dindex := objectsToDelete[ObjectToDelete{
|
||||||
ObjectName: dObjects[i].ObjectName,
|
ObjectName: dObjects[i].ObjectName,
|
||||||
VersionID: dObjects[i].VersionID,
|
VersionID: dObjects[i].VersionID,
|
||||||
@ -504,7 +503,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
|
|||||||
DeleteMarkerReplicationStatus: dObjects[i].DeleteMarkerReplicationStatus,
|
DeleteMarkerReplicationStatus: dObjects[i].DeleteMarkerReplicationStatus,
|
||||||
PurgeTransitioned: dObjects[i].PurgeTransitioned,
|
PurgeTransitioned: dObjects[i].PurgeTransitioned,
|
||||||
}]
|
}]
|
||||||
if errs[i] == nil || isErrObjectNotFound(errs[i]) || isErrVersionNotFound(errs[i]) {
|
if errs[i] == nil || isErrObjectNotFound(errs[i]) {
|
||||||
if replicateDeletes {
|
if replicateDeletes {
|
||||||
dObjects[i].DeleteMarkerReplicationStatus = deleteList[i].DeleteMarkerReplicationStatus
|
dObjects[i].DeleteMarkerReplicationStatus = deleteList[i].DeleteMarkerReplicationStatus
|
||||||
dObjects[i].VersionPurgeStatus = deleteList[i].VersionPurgeStatus
|
dObjects[i].VersionPurgeStatus = deleteList[i].VersionPurgeStatus
|
||||||
|
@ -89,16 +89,15 @@ func enforceRetentionBypassForDelete(ctx context.Context, r *http.Request, bucke
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts.VersionID = object.VersionID
|
opts.VersionID = object.VersionID
|
||||||
|
|
||||||
if gerr != nil { // error from GetObjectInfo
|
if gerr != nil { // error from GetObjectInfo
|
||||||
switch err.(type) {
|
switch gerr.(type) {
|
||||||
case MethodNotAllowed: // This happens usually for a delete marker
|
case MethodNotAllowed: // This happens usually for a delete marker
|
||||||
if oi.DeleteMarker {
|
if oi.DeleteMarker {
|
||||||
// Delete marker should be present and valid.
|
// Delete marker should be present and valid.
|
||||||
return ErrNone
|
return ErrNone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toAPIErrorCode(ctx, err)
|
return toAPIErrorCode(ctx, gerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhold := objectlock.GetObjectLegalHoldMeta(oi.UserDefined)
|
lhold := objectlock.GetObjectLegalHoldMeta(oi.UserDefined)
|
||||||
|
@ -200,7 +200,7 @@ func replicateDelete(ctx context.Context, dobj DeletedObjectVersionInfo, objectA
|
|||||||
VersionID: versionID,
|
VersionID: versionID,
|
||||||
Internal: miniogo.AdvancedRemoveOptions{
|
Internal: miniogo.AdvancedRemoveOptions{
|
||||||
ReplicationDeleteMarker: dobj.DeleteMarkerVersionID != "",
|
ReplicationDeleteMarker: dobj.DeleteMarkerVersionID != "",
|
||||||
ReplicationMTime: dobj.DeleteMarkerMTime,
|
ReplicationMTime: dobj.DeleteMarkerMTime.Time,
|
||||||
ReplicationStatus: miniogo.ReplicationStatusReplica,
|
ReplicationStatus: miniogo.ReplicationStatusReplica,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -854,7 +854,7 @@ func (i *crawlItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, m
|
|||||||
DeleteMarkerVersionID: dmVersionID,
|
DeleteMarkerVersionID: dmVersionID,
|
||||||
VersionID: versionID,
|
VersionID: versionID,
|
||||||
DeleteMarkerReplicationStatus: string(meta.oi.ReplicationStatus),
|
DeleteMarkerReplicationStatus: string(meta.oi.ReplicationStatus),
|
||||||
DeleteMarkerMTime: meta.oi.ModTime,
|
DeleteMarkerMTime: DeleteMarkerMTime{meta.oi.ModTime},
|
||||||
DeleteMarker: meta.oi.DeleteMarker,
|
DeleteMarker: meta.oi.DeleteMarker,
|
||||||
VersionPurgeStatus: meta.oi.VersionPurgeStatus,
|
VersionPurgeStatus: meta.oi.VersionPurgeStatus,
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -857,19 +856,21 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize list of errors.
|
// Initialize list of errors.
|
||||||
var opErrs = make([]error, len(storageDisks))
|
|
||||||
var delObjErrs = make([][]error, len(storageDisks))
|
var delObjErrs = make([][]error, len(storageDisks))
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
// Remove versions in bulk for each disk
|
// Remove versions in bulk for each disk
|
||||||
for index, disk := range storageDisks {
|
for index, disk := range storageDisks {
|
||||||
if disk == nil {
|
|
||||||
opErrs[index] = errDiskNotFound
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(index int, disk StorageAPI) {
|
go func(index int, disk StorageAPI) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
if disk == nil {
|
||||||
|
delObjErrs[index] = make([]error, len(versions))
|
||||||
|
for i := range versions {
|
||||||
|
delObjErrs[index][i] = errDiskNotFound
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
delObjErrs[index] = disk.DeleteVersions(ctx, bucket, versions)
|
delObjErrs[index] = disk.DeleteVersions(ctx, bucket, versions)
|
||||||
}(index, disk)
|
}(index, disk)
|
||||||
}
|
}
|
||||||
@ -878,30 +879,31 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
|||||||
|
|
||||||
// Reduce errors for each object
|
// Reduce errors for each object
|
||||||
for objIndex := range objects {
|
for objIndex := range objects {
|
||||||
if errs[objIndex] != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
diskErrs := make([]error, len(storageDisks))
|
diskErrs := make([]error, len(storageDisks))
|
||||||
// Iterate over disks to fetch the error
|
// Iterate over disks to fetch the error
|
||||||
// of deleting of the current object
|
// of deleting of the current object
|
||||||
for i := range delObjErrs {
|
for i := range delObjErrs {
|
||||||
// delObjErrs[i] is not nil when disks[i] is also not nil
|
// delObjErrs[i] is not nil when disks[i] is also not nil
|
||||||
if delObjErrs[i] != nil {
|
if delObjErrs[i] != nil {
|
||||||
if errors.Is(delObjErrs[i][objIndex], errFileNotFound) ||
|
|
||||||
errors.Is(delObjErrs[i][objIndex], errFileVersionNotFound) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
diskErrs[i] = delObjErrs[i][objIndex]
|
diskErrs[i] = delObjErrs[i][objIndex]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errs[objIndex] = reduceWriteQuorumErrs(ctx, diskErrs, objectOpIgnoredErrs, writeQuorums[objIndex])
|
err := reduceWriteQuorumErrs(ctx, diskErrs, objectOpIgnoredErrs, writeQuorums[objIndex])
|
||||||
|
if objects[objIndex].VersionID != "" {
|
||||||
|
errs[objIndex] = toObjectErr(err, bucket, objects[objIndex].ObjectName, objects[objIndex].VersionID)
|
||||||
|
} else {
|
||||||
|
errs[objIndex] = toObjectErr(err, bucket, objects[objIndex].ObjectName)
|
||||||
|
}
|
||||||
|
|
||||||
if errs[objIndex] == nil {
|
if errs[objIndex] == nil {
|
||||||
ObjectPathUpdated(pathJoin(bucket, objects[objIndex].ObjectName))
|
ObjectPathUpdated(pathJoin(bucket, objects[objIndex].ObjectName))
|
||||||
|
}
|
||||||
|
|
||||||
if versions[objIndex].Deleted {
|
if versions[objIndex].Deleted {
|
||||||
dobjects[objIndex] = DeletedObject{
|
dobjects[objIndex] = DeletedObject{
|
||||||
DeleteMarker: versions[objIndex].Deleted,
|
DeleteMarker: versions[objIndex].Deleted,
|
||||||
DeleteMarkerVersionID: versions[objIndex].VersionID,
|
DeleteMarkerVersionID: versions[objIndex].VersionID,
|
||||||
DeleteMarkerMTime: versions[objIndex].ModTime,
|
DeleteMarkerMTime: DeleteMarkerMTime{versions[objIndex].ModTime},
|
||||||
DeleteMarkerReplicationStatus: versions[objIndex].DeleteMarkerReplicationStatus,
|
DeleteMarkerReplicationStatus: versions[objIndex].DeleteMarkerReplicationStatus,
|
||||||
ObjectName: versions[objIndex].Name,
|
ObjectName: versions[objIndex].Name,
|
||||||
VersionPurgeStatus: versions[objIndex].VersionPurgeStatus,
|
VersionPurgeStatus: versions[objIndex].VersionPurgeStatus,
|
||||||
@ -917,7 +919,6 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check failed deletes across multiple objects
|
// Check failed deletes across multiple objects
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
|
@ -585,19 +585,19 @@ func (z *erasureServerSets) DeleteObjects(ctx context.Context, bucket string, ob
|
|||||||
}
|
}
|
||||||
defer multiDeleteLock.Unlock()
|
defer multiDeleteLock.Unlock()
|
||||||
|
|
||||||
|
if z.SingleZone() {
|
||||||
|
return z.serverSets[0].DeleteObjects(ctx, bucket, objects, opts)
|
||||||
|
}
|
||||||
|
|
||||||
for _, zone := range z.serverSets {
|
for _, zone := range z.serverSets {
|
||||||
deletedObjects, errs := zone.DeleteObjects(ctx, bucket, objects, opts)
|
deletedObjects, errs := zone.DeleteObjects(ctx, bucket, objects, opts)
|
||||||
for i, derr := range errs {
|
for i, derr := range errs {
|
||||||
if derrs[i] == nil {
|
if derr != nil {
|
||||||
if derr != nil && !isErrObjectNotFound(derr) && !isErrVersionNotFound(derr) {
|
|
||||||
derrs[i] = derr
|
derrs[i] = derr
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if derrs[i] == nil {
|
|
||||||
dobjects[i] = deletedObjects[i]
|
dobjects[i] = deletedObjects[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return dobjects, derrs
|
return dobjects, derrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,11 +759,9 @@ func (s *erasureSets) DeleteObjects(ctx context.Context, bucket string, objects
|
|||||||
dobjects, errs := s.getHashedSet(objsGroup[0].object.ObjectName).DeleteObjects(ctx, bucket, toNames(objsGroup), opts)
|
dobjects, errs := s.getHashedSet(objsGroup[0].object.ObjectName).DeleteObjects(ctx, bucket, toNames(objsGroup), opts)
|
||||||
for i, obj := range objsGroup {
|
for i, obj := range objsGroup {
|
||||||
delErrs[obj.origIndex] = errs[i]
|
delErrs[obj.origIndex] = errs[i]
|
||||||
if delErrs[obj.origIndex] == nil {
|
|
||||||
delObjects[obj.origIndex] = dobjects[i]
|
delObjects[obj.origIndex] = dobjects[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return delObjects, delErrs
|
return delObjects, delErrs
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,14 @@ func (e BucketNotEmpty) Error() string {
|
|||||||
return "Bucket not empty: " + e.Bucket
|
return "Bucket not empty: " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionNotFound object does not exist.
|
// InvalidVersionID invalid version id
|
||||||
|
type InvalidVersionID GenericError
|
||||||
|
|
||||||
|
func (e InvalidVersionID) Error() string {
|
||||||
|
return "Invalid version id: " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionNotFound version does not exist.
|
||||||
type VersionNotFound GenericError
|
type VersionNotFound GenericError
|
||||||
|
|
||||||
func (e VersionNotFound) Error() string {
|
func (e VersionNotFound) Error() string {
|
||||||
|
@ -93,7 +93,7 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
|
|||||||
_, err := uuid.Parse(vid)
|
_, err := uuid.Parse(vid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
return opts, VersionNotFound{
|
return opts, InvalidVersionID{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Object: object,
|
Object: object,
|
||||||
VersionID: vid,
|
VersionID: vid,
|
||||||
@ -209,7 +209,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada
|
|||||||
_, err := uuid.Parse(vid)
|
_, err := uuid.Parse(vid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
return opts, VersionNotFound{
|
return opts, InvalidVersionID{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Object: object,
|
Object: object,
|
||||||
VersionID: vid,
|
VersionID: vid,
|
||||||
|
@ -2798,13 +2798,14 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
|
|||||||
VersionID: versionID,
|
VersionID: versionID,
|
||||||
DeleteMarkerVersionID: dmVersionID,
|
DeleteMarkerVersionID: dmVersionID,
|
||||||
DeleteMarkerReplicationStatus: string(objInfo.ReplicationStatus),
|
DeleteMarkerReplicationStatus: string(objInfo.ReplicationStatus),
|
||||||
DeleteMarkerMTime: objInfo.ModTime,
|
DeleteMarkerMTime: DeleteMarkerMTime{objInfo.ModTime},
|
||||||
DeleteMarker: objInfo.DeleteMarker,
|
DeleteMarker: objInfo.DeleteMarker,
|
||||||
VersionPurgeStatus: objInfo.VersionPurgeStatus,
|
VersionPurgeStatus: objInfo.VersionPurgeStatus,
|
||||||
},
|
},
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if goi.TransitionStatus == lifecycle.TransitionComplete { // clean up transitioned tier
|
if goi.TransitionStatus == lifecycle.TransitionComplete { // clean up transitioned tier
|
||||||
action := lifecycle.DeleteAction
|
action := lifecycle.DeleteAction
|
||||||
if goi.VersionID != "" {
|
if goi.VersionID != "" {
|
||||||
@ -2819,6 +2820,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
|
|||||||
IsLatest: goi.IsLatest,
|
IsLatest: goi.IsLatest,
|
||||||
}, action, true)
|
}, action, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
setPutObjHeaders(w, objInfo, true)
|
setPutObjHeaders(w, objInfo, true)
|
||||||
writeSuccessNoContent(w)
|
writeSuccessNoContent(w)
|
||||||
}
|
}
|
||||||
|
@ -1765,7 +1765,7 @@ func testAPICopyObjectPartHandler(obj ObjectLayer, instanceType, bucketName stri
|
|||||||
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=17",
|
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=17",
|
||||||
accessKey: credentials.AccessKey,
|
accessKey: credentials.AccessKey,
|
||||||
secretKey: credentials.SecretKey,
|
secretKey: credentials.SecretKey,
|
||||||
expectedRespStatus: http.StatusBadRequest,
|
expectedRespStatus: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2135,7 +2135,7 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=17",
|
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=17",
|
||||||
accessKey: credentials.AccessKey,
|
accessKey: credentials.AccessKey,
|
||||||
secretKey: credentials.SecretKey,
|
secretKey: credentials.SecretKey,
|
||||||
expectedRespStatus: http.StatusBadRequest,
|
expectedRespStatus: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,7 +761,7 @@ next:
|
|||||||
ObjectName: objectName,
|
ObjectName: objectName,
|
||||||
DeleteMarkerVersionID: oi.VersionID,
|
DeleteMarkerVersionID: oi.VersionID,
|
||||||
DeleteMarkerReplicationStatus: string(oi.ReplicationStatus),
|
DeleteMarkerReplicationStatus: string(oi.ReplicationStatus),
|
||||||
DeleteMarkerMTime: oi.ModTime,
|
DeleteMarkerMTime: DeleteMarkerMTime{oi.ModTime},
|
||||||
DeleteMarker: oi.DeleteMarker,
|
DeleteMarker: oi.DeleteMarker,
|
||||||
VersionPurgeStatus: oi.VersionPurgeStatus,
|
VersionPurgeStatus: oi.VersionPurgeStatus,
|
||||||
},
|
},
|
||||||
|
@ -445,8 +445,12 @@ func (z *xlMetaV2) DeleteVersion(fi FileInfo) (string, bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var uv uuid.UUID
|
var uv uuid.UUID
|
||||||
|
var err error
|
||||||
if fi.VersionID != "" {
|
if fi.VersionID != "" {
|
||||||
uv, _ = uuid.Parse(fi.VersionID)
|
uv, err = uuid.Parse(fi.VersionID)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, errFileVersionNotFound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ventry xlMetaV2Version
|
var ventry xlMetaV2Version
|
||||||
@ -645,8 +649,11 @@ func (z xlMetaV2) ListVersions(volume, path string) (versions []FileInfo, modTim
|
|||||||
// for consumption across callers.
|
// for consumption across callers.
|
||||||
func (z xlMetaV2) ToFileInfo(volume, path, versionID string) (fi FileInfo, err error) {
|
func (z xlMetaV2) ToFileInfo(volume, path, versionID string) (fi FileInfo, err error) {
|
||||||
var uv uuid.UUID
|
var uv uuid.UUID
|
||||||
if versionID != "" {
|
if versionID != "" && versionID != nullVersionID {
|
||||||
uv, _ = uuid.Parse(versionID)
|
uv, err = uuid.Parse(versionID)
|
||||||
|
if err != nil {
|
||||||
|
return FileInfo{}, errFileVersionNotFound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestModTime time.Time
|
var latestModTime time.Time
|
||||||
|
@ -971,10 +971,16 @@ func (s *xlStorage) DeleteVersion(ctx context.Context, volume, path string, fi F
|
|||||||
|
|
||||||
buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
|
buf, err := s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == errFileNotFound && fi.VersionID != "" {
|
||||||
|
err = errFileVersionNotFound
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
|
if fi.VersionID != "" {
|
||||||
|
return errFileVersionNotFound
|
||||||
|
}
|
||||||
return errFileNotFound
|
return errFileNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1146,10 +1152,22 @@ func (s *xlStorage) ReadVersion(ctx context.Context, volume, path, versionID str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if err == errFileNotFound {
|
if err == errFileNotFound {
|
||||||
if err = s.renameLegacyMetadata(volume, path); err != nil {
|
if err = s.renameLegacyMetadata(volume, path); err != nil {
|
||||||
|
if err == errFileNotFound {
|
||||||
|
if versionID != "" {
|
||||||
|
return fi, errFileVersionNotFound
|
||||||
|
}
|
||||||
|
return fi, errFileNotFound
|
||||||
|
}
|
||||||
return fi, err
|
return fi, err
|
||||||
}
|
}
|
||||||
buf, err = s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
|
buf, err = s.ReadAll(ctx, volume, pathJoin(path, xlStorageFormatFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == errFileNotFound {
|
||||||
|
if versionID != "" {
|
||||||
|
return fi, errFileVersionNotFound
|
||||||
|
}
|
||||||
|
return fi, errFileNotFound
|
||||||
|
}
|
||||||
return fi, err
|
return fi, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user