You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1025 lines
36 KiB
1025 lines
36 KiB
/*
|
|
* Minio Cloud Storage, (C) 2015, 2016, 2017, 2018 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 cmd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/coreos/etcd/client"
|
|
"github.com/minio/minio/pkg/auth"
|
|
"github.com/minio/minio/pkg/event"
|
|
"github.com/minio/minio/pkg/hash"
|
|
)
|
|
|
|
// APIError structure
|
|
type APIError struct {
|
|
Code string
|
|
Description string
|
|
HTTPStatusCode int
|
|
}
|
|
|
|
// APIErrorResponse - error response format
|
|
type APIErrorResponse struct {
|
|
XMLName xml.Name `xml:"Error" json:"-"`
|
|
Code string
|
|
Message string
|
|
Key string
|
|
BucketName string
|
|
Resource string
|
|
RequestID string `xml:"RequestId" json:"RequestId"`
|
|
HostID string `xml:"HostId" json:"HostId"`
|
|
}
|
|
|
|
// APIErrorCode type of error status.
|
|
type APIErrorCode int
|
|
|
|
// Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
|
const (
|
|
ErrNone APIErrorCode = iota
|
|
ErrAccessDenied
|
|
ErrBadDigest
|
|
ErrEntityTooSmall
|
|
ErrEntityTooLarge
|
|
ErrIncompleteBody
|
|
ErrInternalError
|
|
ErrInvalidAccessKeyID
|
|
ErrInvalidBucketName
|
|
ErrInvalidDigest
|
|
ErrInvalidRange
|
|
ErrInvalidCopyPartRange
|
|
ErrInvalidCopyPartRangeSource
|
|
ErrInvalidMaxKeys
|
|
ErrInvalidMaxUploads
|
|
ErrInvalidMaxParts
|
|
ErrInvalidPartNumberMarker
|
|
ErrInvalidRequestBody
|
|
ErrInvalidCopySource
|
|
ErrInvalidMetadataDirective
|
|
ErrInvalidCopyDest
|
|
ErrInvalidPolicyDocument
|
|
ErrInvalidObjectState
|
|
ErrMalformedXML
|
|
ErrMissingContentLength
|
|
ErrMissingContentMD5
|
|
ErrMissingRequestBodyError
|
|
ErrNoSuchBucket
|
|
ErrNoSuchBucketPolicy
|
|
ErrNoSuchKey
|
|
ErrNoSuchUpload
|
|
ErrNotImplemented
|
|
ErrPreconditionFailed
|
|
ErrRequestTimeTooSkewed
|
|
ErrSignatureDoesNotMatch
|
|
ErrMethodNotAllowed
|
|
ErrInvalidPart
|
|
ErrInvalidPartOrder
|
|
ErrAuthorizationHeaderMalformed
|
|
ErrMalformedPOSTRequest
|
|
ErrPOSTFileRequired
|
|
ErrSignatureVersionNotSupported
|
|
ErrBucketNotEmpty
|
|
ErrAllAccessDisabled
|
|
ErrMalformedPolicy
|
|
ErrMissingFields
|
|
ErrMissingCredTag
|
|
ErrCredMalformed
|
|
ErrInvalidRegion
|
|
ErrInvalidService
|
|
ErrInvalidRequestVersion
|
|
ErrMissingSignTag
|
|
ErrMissingSignHeadersTag
|
|
ErrPolicyAlreadyExpired
|
|
ErrMalformedDate
|
|
ErrMalformedPresignedDate
|
|
ErrMalformedCredentialDate
|
|
ErrMalformedCredentialRegion
|
|
ErrMalformedExpires
|
|
ErrNegativeExpires
|
|
ErrAuthHeaderEmpty
|
|
ErrExpiredPresignRequest
|
|
ErrRequestNotReadyYet
|
|
ErrUnsignedHeaders
|
|
ErrMissingDateHeader
|
|
ErrInvalidQuerySignatureAlgo
|
|
ErrInvalidQueryParams
|
|
ErrBucketAlreadyOwnedByYou
|
|
ErrInvalidDuration
|
|
ErrBucketAlreadyExists
|
|
ErrMetadataTooLarge
|
|
ErrUnsupportedMetadata
|
|
ErrMaximumExpires
|
|
ErrSlowDown
|
|
ErrInvalidPrefixMarker
|
|
// Add new error codes here.
|
|
|
|
// Server-Side-Encryption (with Customer provided key) related API errors.
|
|
ErrInsecureSSECustomerRequest
|
|
ErrSSEMultipartEncrypted
|
|
ErrSSEEncryptedObject
|
|
ErrInvalidEncryptionParameters
|
|
ErrInvalidSSECustomerAlgorithm
|
|
ErrInvalidSSECustomerKey
|
|
ErrMissingSSECustomerKey
|
|
ErrMissingSSECustomerKeyMD5
|
|
ErrSSECustomerKeyMD5Mismatch
|
|
ErrInvalidSSECustomerParameters
|
|
|
|
// Bucket notification related errors.
|
|
ErrEventNotification
|
|
ErrARNNotification
|
|
ErrRegionNotification
|
|
ErrOverlappingFilterNotification
|
|
ErrFilterNameInvalid
|
|
ErrFilterNamePrefix
|
|
ErrFilterNameSuffix
|
|
ErrFilterValueInvalid
|
|
ErrOverlappingConfigs
|
|
ErrUnsupportedNotification
|
|
|
|
// S3 extended errors.
|
|
ErrContentSHA256Mismatch
|
|
|
|
// Add new extended error codes here.
|
|
|
|
// Minio extended errors.
|
|
ErrReadQuorum
|
|
ErrWriteQuorum
|
|
ErrStorageFull
|
|
ErrRequestBodyParse
|
|
ErrObjectExistsAsDirectory
|
|
ErrPolicyNesting
|
|
ErrInvalidObjectName
|
|
ErrInvalidResourceName
|
|
ErrServerNotInitialized
|
|
ErrOperationTimedOut
|
|
ErrPartsSizeUnequal
|
|
ErrInvalidRequest
|
|
// Minio storage class error codes
|
|
ErrInvalidStorageClass
|
|
ErrBackendDown
|
|
// Add new extended error codes here.
|
|
// Please open a https://github.com/minio/minio/issues before adding
|
|
// new error codes here.
|
|
|
|
ErrMalformedJSON
|
|
ErrAdminInvalidAccessKey
|
|
ErrAdminInvalidSecretKey
|
|
ErrAdminConfigNoQuorum
|
|
ErrAdminConfigTooLarge
|
|
ErrAdminConfigBadJSON
|
|
ErrAdminCredentialsMismatch
|
|
ErrInsecureClientRequest
|
|
ErrObjectTampered
|
|
ErrHealNotImplemented
|
|
ErrHealNoSuchProcess
|
|
ErrHealInvalidClientToken
|
|
ErrHealMissingBucket
|
|
ErrHealAlreadyRunning
|
|
ErrHealOverlappingPaths
|
|
ErrIncorrectContinuationToken
|
|
)
|
|
|
|
// error code to APIError structure, these fields carry respective
|
|
// descriptions for all the error responses.
|
|
var errorCodeResponse = map[APIErrorCode]APIError{
|
|
ErrInvalidCopyDest: {
|
|
Code: "InvalidRequest",
|
|
Description: "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidCopySource: {
|
|
Code: "InvalidArgument",
|
|
Description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMetadataDirective: {
|
|
Code: "InvalidArgument",
|
|
Description: "Unknown metadata directive.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidStorageClass: {
|
|
Code: "InvalidStorageClass",
|
|
Description: "Invalid storage class.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRequestBody: {
|
|
Code: "InvalidArgument",
|
|
Description: "Body shouldn't be set for this request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxUploads: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument max-uploads must be an integer between 0 and 2147483647",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxKeys: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument maxKeys must be an integer between 0 and 2147483647",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxParts: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument max-parts must be an integer between 0 and 2147483647",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPartNumberMarker: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument partNumberMarker must be an integer.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPolicyDocument: {
|
|
Code: "InvalidPolicyDocument",
|
|
Description: "The content of the form does not meet the conditions specified in the policy document.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAccessDenied: {
|
|
Code: "AccessDenied",
|
|
Description: "Access Denied.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrBadDigest: {
|
|
Code: "BadDigest",
|
|
Description: "The Content-Md5 you specified did not match what we received.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrEntityTooSmall: {
|
|
Code: "EntityTooSmall",
|
|
Description: "Your proposed upload is smaller than the minimum allowed object size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrEntityTooLarge: {
|
|
Code: "EntityTooLarge",
|
|
Description: "Your proposed upload exceeds the maximum allowed object size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrIncompleteBody: {
|
|
Code: "IncompleteBody",
|
|
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInternalError: {
|
|
Code: "InternalError",
|
|
Description: "We encountered an internal error, please try again.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
ErrInvalidAccessKeyID: {
|
|
Code: "InvalidAccessKeyId",
|
|
Description: "The access key ID you provided does not exist in our records.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrInvalidBucketName: {
|
|
Code: "InvalidBucketName",
|
|
Description: "The specified bucket is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidDigest: {
|
|
Code: "InvalidDigest",
|
|
Description: "The Content-Md5 you specified is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRange: {
|
|
Code: "InvalidRange",
|
|
Description: "The requested range is not satisfiable",
|
|
HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
|
},
|
|
ErrMalformedXML: {
|
|
Code: "MalformedXML",
|
|
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingContentLength: {
|
|
Code: "MissingContentLength",
|
|
Description: "You must provide the Content-Length HTTP header.",
|
|
HTTPStatusCode: http.StatusLengthRequired,
|
|
},
|
|
ErrMissingContentMD5: {
|
|
Code: "MissingContentMD5",
|
|
Description: "Missing required header for this request: Content-Md5.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingRequestBodyError: {
|
|
Code: "MissingRequestBodyError",
|
|
Description: "Request body is empty.",
|
|
HTTPStatusCode: http.StatusLengthRequired,
|
|
},
|
|
ErrNoSuchBucket: {
|
|
Code: "NoSuchBucket",
|
|
Description: "The specified bucket does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchBucketPolicy: {
|
|
Code: "NoSuchBucketPolicy",
|
|
Description: "The bucket policy does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchKey: {
|
|
Code: "NoSuchKey",
|
|
Description: "The specified key does not exist.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchUpload: {
|
|
Code: "NoSuchUpload",
|
|
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,
|
|
},
|
|
ErrNotImplemented: {
|
|
Code: "NotImplemented",
|
|
Description: "A header you provided implies functionality that is not implemented",
|
|
HTTPStatusCode: http.StatusNotImplemented,
|
|
},
|
|
ErrPreconditionFailed: {
|
|
Code: "PreconditionFailed",
|
|
Description: "At least one of the pre-conditions you specified did not hold",
|
|
HTTPStatusCode: http.StatusPreconditionFailed,
|
|
},
|
|
ErrRequestTimeTooSkewed: {
|
|
Code: "RequestTimeTooSkewed",
|
|
Description: "The difference between the request time and the server's time is too large.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrSignatureDoesNotMatch: {
|
|
Code: "SignatureDoesNotMatch",
|
|
Description: "The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrMethodNotAllowed: {
|
|
Code: "MethodNotAllowed",
|
|
Description: "The specified method is not allowed against this resource.",
|
|
HTTPStatusCode: http.StatusMethodNotAllowed,
|
|
},
|
|
ErrInvalidPart: {
|
|
Code: "InvalidPart",
|
|
Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPartOrder: {
|
|
Code: "InvalidPartOrder",
|
|
Description: "The list of parts was not in ascending order. The parts list must be specified in order by part number.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidObjectState: {
|
|
Code: "InvalidObjectState",
|
|
Description: "The operation is not valid for the current state of the object.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrAuthorizationHeaderMalformed: {
|
|
Code: "AuthorizationHeaderMalformed",
|
|
Description: "The authorization header is malformed; the region is wrong; expecting 'us-east-1'.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPOSTRequest: {
|
|
Code: "MalformedPOSTRequest",
|
|
Description: "The body of your POST request is not well-formed multipart/form-data.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPOSTFileRequired: {
|
|
Code: "InvalidArgument",
|
|
Description: "POST requires exactly one file upload per request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSignatureVersionNotSupported: {
|
|
Code: "InvalidRequest",
|
|
Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBucketNotEmpty: {
|
|
Code: "BucketNotEmpty",
|
|
Description: "The bucket you tried to delete is not empty",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrBucketAlreadyExists: {
|
|
Code: "BucketAlreadyExists",
|
|
Description: "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrAllAccessDisabled: {
|
|
Code: "AllAccessDisabled",
|
|
Description: "All access to this bucket has been disabled.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrMalformedPolicy: {
|
|
Code: "MalformedPolicy",
|
|
Description: "Policy has invalid resource.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingFields: {
|
|
Code: "MissingFields",
|
|
Description: "Missing fields in request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingCredTag: {
|
|
Code: "InvalidRequest",
|
|
Description: "Missing Credential field for this request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrCredMalformed: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedDate: {
|
|
Code: "MalformedDate",
|
|
Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPresignedDate: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Date must be in the ISO8601 Long Format \"yyyyMMdd'T'HHmmss'Z'\"",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385.
|
|
// right Description: "Error parsing the X-Amz-Credential parameter; incorrect date format \"%s\". This date in the credential must be in the format \"yyyyMMdd\".",
|
|
// Need changes to make sure variable messages can be constructed.
|
|
ErrMalformedCredentialDate: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; incorrect date format \"%s\". This date in the credential must be in the format \"yyyyMMdd\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385.
|
|
// right Description: "Error parsing the X-Amz-Credential parameter; the region 'us-east-' is wrong; expecting 'us-east-1'".
|
|
// Need changes to make sure variable messages can be constructed.
|
|
ErrMalformedCredentialRegion: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; the region is wrong;",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRegion: {
|
|
Code: "InvalidRegion",
|
|
Description: "Region does not match.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385.
|
|
// right Description: "Error parsing the X-Amz-Credential parameter; incorrect service \"s4\". This endpoint belongs to \"s3\".".
|
|
// Need changes to make sure variable messages can be constructed.
|
|
ErrInvalidService: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; incorrect service. This endpoint belongs to \"s3\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// FIXME: Should contain the invalid param set as seen in https://github.com/minio/minio/issues/2385.
|
|
// Description: "Error parsing the X-Amz-Credential parameter; incorrect terminal "aws4_reque". This endpoint uses "aws4_request".
|
|
// Need changes to make sure variable messages can be constructed.
|
|
ErrInvalidRequestVersion: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; incorrect terminal. This endpoint uses \"aws4_request\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignTag: {
|
|
Code: "AccessDenied",
|
|
Description: "Signature header missing Signature field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignHeadersTag: {
|
|
Code: "InvalidArgument",
|
|
Description: "Signature header missing SignedHeaders field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPolicyAlreadyExpired: {
|
|
Code: "AccessDenied",
|
|
Description: "Invalid according to Policy: Policy expired.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires should be a number",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrNegativeExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires must be non-negative",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAuthHeaderEmpty: {
|
|
Code: "InvalidArgument",
|
|
Description: "Authorization header is invalid -- one and only one ' ' (space) required.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingDateHeader: {
|
|
Code: "AccessDenied",
|
|
Description: "AWS authentication requires a valid Date or x-amz-date header",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidQuerySignatureAlgo: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrExpiredPresignRequest: {
|
|
Code: "AccessDenied",
|
|
Description: "Request has expired",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrRequestNotReadyYet: {
|
|
Code: "AccessDenied",
|
|
Description: "Request is not valid yet",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrSlowDown: {
|
|
Code: "SlowDown",
|
|
Description: "Please reduce your request",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrInvalidPrefixMarker: {
|
|
Code: "InvalidPrefixMarker",
|
|
Description: "Invalid marker prefix combination",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
// FIXME: Actual XML error response also contains the header which missed in list of signed header parameters.
|
|
ErrUnsignedHeaders: {
|
|
Code: "AccessDenied",
|
|
Description: "There were headers present in the request which were not signed",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidQueryParams: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBucketAlreadyOwnedByYou: {
|
|
Code: "BucketAlreadyOwnedByYou",
|
|
Description: "Your previous request to create the named bucket succeeded and you already own it.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrInvalidDuration: {
|
|
Code: "InvalidDuration",
|
|
Description: "Duration provided in the request is invalid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
/// Bucket notification related errors.
|
|
ErrEventNotification: {
|
|
Code: "InvalidArgument",
|
|
Description: "A specified event is not supported for notifications.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrARNNotification: {
|
|
Code: "InvalidArgument",
|
|
Description: "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrRegionNotification: {
|
|
Code: "InvalidArgument",
|
|
Description: "A specified destination is in a different region than the bucket. You must use a destination that resides in the same region as the bucket.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrOverlappingFilterNotification: {
|
|
Code: "InvalidArgument",
|
|
Description: "An object key name filtering rule defined with overlapping prefixes, overlapping suffixes, or overlapping combinations of prefixes and suffixes for the same event types.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrFilterNameInvalid: {
|
|
Code: "InvalidArgument",
|
|
Description: "filter rule name must be either prefix or suffix",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrFilterNamePrefix: {
|
|
Code: "InvalidArgument",
|
|
Description: "Cannot specify more than one prefix rule in a filter.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrFilterNameSuffix: {
|
|
Code: "InvalidArgument",
|
|
Description: "Cannot specify more than one suffix rule in a filter.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrFilterValueInvalid: {
|
|
Code: "InvalidArgument",
|
|
Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrOverlappingConfigs: {
|
|
Code: "InvalidArgument",
|
|
Description: "Configurations overlap. Configurations on the same bucket cannot share a common event type.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrUnsupportedNotification: {
|
|
Code: "UnsupportedNotification",
|
|
Description: "Minio server does not support Topic or Cloud Function based notifications.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidCopyPartRange: {
|
|
Code: "InvalidArgument",
|
|
Description: "The x-amz-copy-source-range value must be of the form bytes=first-last where first and last are the zero-based offsets of the first and last bytes to copy",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidCopyPartRangeSource: {
|
|
Code: "InvalidArgument",
|
|
Description: "Range specified is not valid for source object",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMetadataTooLarge: {
|
|
Code: "InvalidArgument",
|
|
Description: "Your metadata headers exceed the maximum allowed metadata size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInsecureSSECustomerRequest: {
|
|
Code: "InvalidRequest",
|
|
Description: "Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSEMultipartEncrypted: {
|
|
Code: "InvalidRequest",
|
|
Description: "The multipart upload initiate requested encryption. Subsequent part requests must include the appropriate encryption parameters.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSEEncryptedObject: {
|
|
Code: "InvalidRequest",
|
|
Description: "The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidEncryptionParameters: {
|
|
Code: "InvalidRequest",
|
|
Description: "The encryption parameters are not applicable to this object.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidSSECustomerAlgorithm: {
|
|
Code: "InvalidArgument",
|
|
Description: "Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidSSECustomerKey: {
|
|
Code: "InvalidArgument",
|
|
Description: "The secret key was invalid for the specified algorithm.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSSECustomerKey: {
|
|
Code: "InvalidArgument",
|
|
Description: "Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSSECustomerKeyMD5: {
|
|
Code: "InvalidArgument",
|
|
Description: "Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSECustomerKeyMD5Mismatch: {
|
|
Code: "InvalidArgument",
|
|
Description: "The calculated MD5 hash of the key did not match the hash that was provided.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidSSECustomerParameters: {
|
|
Code: "InvalidArgument",
|
|
Description: "The provided encryption parameters did not match the ones used originally.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
/// S3 extensions.
|
|
ErrContentSHA256Mismatch: {
|
|
Code: "XAmzContentSHA256Mismatch",
|
|
Description: "The provided 'x-amz-content-sha256' header does not match what was computed.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
/// Minio extensions.
|
|
ErrStorageFull: {
|
|
Code: "XMinioStorageFull",
|
|
Description: "Storage backend has reached its minimum free disk threshold. Please delete a few objects to proceed.",
|
|
HTTPStatusCode: http.StatusInsufficientStorage,
|
|
},
|
|
ErrRequestBodyParse: {
|
|
Code: "XMinioRequestBodyParse",
|
|
Description: "The request body failed to parse.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrObjectExistsAsDirectory: {
|
|
Code: "XMinioObjectExistsAsDirectory",
|
|
Description: "Object name already exists as a directory.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrReadQuorum: {
|
|
Code: "XMinioReadQuorum",
|
|
Description: "Multiple disk failures, unable to reconstruct data.",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrWriteQuorum: {
|
|
Code: "XMinioWriteQuorum",
|
|
Description: "Multiple disks failures, unable to write data.",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrPolicyNesting: {
|
|
Code: "XMinioPolicyNesting",
|
|
Description: "New bucket policy conflicts with an existing policy. Please try again with new prefix.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrInvalidObjectName: {
|
|
Code: "XMinioInvalidObjectName",
|
|
Description: "Object name contains unsupported characters.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidResourceName: {
|
|
Code: "XMinioInvalidResourceName",
|
|
Description: "Resource name contains bad components such as \"..\" or \".\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrServerNotInitialized: {
|
|
Code: "XMinioServerNotInitialized",
|
|
Description: "Server not initialized, please try again.",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrMalformedJSON: {
|
|
Code: "XMinioMalformedJSON",
|
|
Description: "The JSON you provided was not well-formed or did not validate against our published format.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAdminInvalidAccessKey: {
|
|
Code: "XMinioAdminInvalidAccessKey",
|
|
Description: "The access key is invalid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAdminInvalidSecretKey: {
|
|
Code: "XMinioAdminInvalidSecretKey",
|
|
Description: "The secret key is invalid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAdminConfigNoQuorum: {
|
|
Code: "XMinioAdminConfigNoQuorum",
|
|
Description: "Configuration update failed because server quorum was not met",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrAdminConfigTooLarge: {
|
|
Code: "XMinioAdminConfigTooLarge",
|
|
Description: fmt.Sprintf("Configuration data provided exceeds the allowed maximum of %d bytes",
|
|
maxConfigJSONSize),
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAdminConfigBadJSON: {
|
|
Code: "XMinioAdminConfigBadJSON",
|
|
Description: "JSON configuration provided has objects with duplicate keys",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAdminCredentialsMismatch: {
|
|
Code: "XMinioAdminCredentialsMismatch",
|
|
Description: "Credentials in config mismatch with server environment variables",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrInsecureClientRequest: {
|
|
Code: "XMinioInsecureClientRequest",
|
|
Description: "Cannot respond to plain-text request from TLS-encrypted server",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrOperationTimedOut: {
|
|
Code: "XMinioServerTimedOut",
|
|
Description: "A timeout occurred while trying to lock a resource",
|
|
HTTPStatusCode: http.StatusRequestTimeout,
|
|
},
|
|
ErrUnsupportedMetadata: {
|
|
Code: "InvalidArgument",
|
|
Description: "Your metadata headers are not supported.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPartsSizeUnequal: {
|
|
Code: "XMinioPartsSizeUnequal",
|
|
Description: "All parts except the last part should be of the same size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrObjectTampered: {
|
|
Code: "XMinioObjectTampered",
|
|
Description: errObjectTampered.Error(),
|
|
HTTPStatusCode: http.StatusPartialContent,
|
|
},
|
|
ErrMaximumExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// Generic Invalid-Request error. Should be used for response errors only for unlikely
|
|
// corner case errors for which introducing new APIErrorCode is not worth it. LogIf()
|
|
// should be used to log the error at the source of the error for debugging purposes.
|
|
ErrInvalidRequest: {
|
|
Code: "InvalidRequest",
|
|
Description: "Invalid Request",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealNotImplemented: {
|
|
Code: "XMinioHealNotImplemented",
|
|
Description: "This server does not implement heal functionality.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealNoSuchProcess: {
|
|
Code: "XMinioHealNoSuchProcess",
|
|
Description: "No such heal process is running on the server",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealInvalidClientToken: {
|
|
Code: "XMinioHealInvalidClientToken",
|
|
Description: "Client token mismatch",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealMissingBucket: {
|
|
Code: "XMinioHealMissingBucket",
|
|
Description: "A heal start request with a non-empty object-prefix parameter requires a bucket to be specified.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealAlreadyRunning: {
|
|
Code: "XMinioHealAlreadyRunning",
|
|
Description: "",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrHealOverlappingPaths: {
|
|
Code: "XMinioHealOverlappingPaths",
|
|
Description: "",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBackendDown: {
|
|
Code: "XMinioBackendDown",
|
|
Description: "Object storage backend is unreachable",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrIncorrectContinuationToken: {
|
|
Code: "InvalidArgument",
|
|
Description: "The continuation token provided is incorrect",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
// Add your error structure here.
|
|
}
|
|
|
|
// toAPIErrorCode - Converts embedded errors. Convenience
|
|
// function written to handle all cases where we have known types of
|
|
// errors returned by underlying layers.
|
|
func toAPIErrorCode(err error) (apiErr APIErrorCode) {
|
|
if err == nil {
|
|
return ErrNone
|
|
}
|
|
|
|
// Verify if the underlying error is signature mismatch.
|
|
switch err {
|
|
case errSignatureMismatch:
|
|
apiErr = ErrSignatureDoesNotMatch
|
|
case errDataTooLarge:
|
|
apiErr = ErrEntityTooLarge
|
|
case errDataTooSmall:
|
|
apiErr = ErrEntityTooSmall
|
|
case auth.ErrInvalidAccessKeyLength:
|
|
apiErr = ErrAdminInvalidAccessKey
|
|
case auth.ErrInvalidSecretKeyLength:
|
|
apiErr = ErrAdminInvalidSecretKey
|
|
// SSE errors
|
|
case errInsecureSSERequest:
|
|
apiErr = ErrInsecureSSECustomerRequest
|
|
case errInvalidSSEAlgorithm:
|
|
apiErr = ErrInvalidSSECustomerAlgorithm
|
|
case errInvalidSSEKey:
|
|
apiErr = ErrInvalidSSECustomerKey
|
|
case errMissingSSEKey:
|
|
apiErr = ErrMissingSSECustomerKey
|
|
case errMissingSSEKeyMD5:
|
|
apiErr = ErrMissingSSECustomerKeyMD5
|
|
case errSSEKeyMD5Mismatch:
|
|
apiErr = ErrSSECustomerKeyMD5Mismatch
|
|
case errObjectTampered:
|
|
apiErr = ErrObjectTampered
|
|
case errEncryptedObject:
|
|
apiErr = ErrSSEEncryptedObject
|
|
case errInvalidSSEParameters:
|
|
apiErr = ErrInvalidSSECustomerParameters
|
|
case errSSEKeyMismatch:
|
|
apiErr = ErrAccessDenied // no access without correct key
|
|
case context.Canceled, context.DeadlineExceeded:
|
|
apiErr = ErrOperationTimedOut
|
|
}
|
|
|
|
if apiErr != ErrNone {
|
|
// If there was a match in the above switch case.
|
|
return apiErr
|
|
}
|
|
|
|
// etcd specific errors, a key is always a bucket for us return
|
|
// ErrNoSuchBucket in such a case.
|
|
if e, ok := err.(*client.Error); ok {
|
|
if e.Code == client.ErrorCodeKeyNotFound {
|
|
return ErrNoSuchBucket
|
|
}
|
|
}
|
|
|
|
switch err.(type) {
|
|
case StorageFull:
|
|
apiErr = ErrStorageFull
|
|
case hash.BadDigest:
|
|
apiErr = ErrBadDigest
|
|
case AllAccessDisabled:
|
|
apiErr = ErrAllAccessDisabled
|
|
case IncompleteBody:
|
|
apiErr = ErrIncompleteBody
|
|
case ObjectExistsAsDirectory:
|
|
apiErr = ErrObjectExistsAsDirectory
|
|
case PrefixAccessDenied:
|
|
apiErr = ErrAccessDenied
|
|
case BucketNameInvalid:
|
|
apiErr = ErrInvalidBucketName
|
|
case BucketNotFound:
|
|
apiErr = ErrNoSuchBucket
|
|
case BucketAlreadyOwnedByYou:
|
|
apiErr = ErrBucketAlreadyOwnedByYou
|
|
case BucketNotEmpty:
|
|
apiErr = ErrBucketNotEmpty
|
|
case BucketAlreadyExists:
|
|
apiErr = ErrBucketAlreadyExists
|
|
case BucketExists:
|
|
apiErr = ErrBucketAlreadyOwnedByYou
|
|
case ObjectNotFound:
|
|
apiErr = ErrNoSuchKey
|
|
case ObjectAlreadyExists:
|
|
apiErr = ErrMethodNotAllowed
|
|
case ObjectNameInvalid:
|
|
apiErr = ErrInvalidObjectName
|
|
case InvalidUploadID:
|
|
apiErr = ErrNoSuchUpload
|
|
case InvalidPart:
|
|
apiErr = ErrInvalidPart
|
|
case InsufficientWriteQuorum:
|
|
apiErr = ErrWriteQuorum
|
|
case InsufficientReadQuorum:
|
|
apiErr = ErrReadQuorum
|
|
case UnsupportedDelimiter:
|
|
apiErr = ErrNotImplemented
|
|
case InvalidMarkerPrefixCombination:
|
|
apiErr = ErrNotImplemented
|
|
case InvalidUploadIDKeyCombination:
|
|
apiErr = ErrNotImplemented
|
|
case MalformedUploadID:
|
|
apiErr = ErrNoSuchUpload
|
|
case PartTooSmall:
|
|
apiErr = ErrEntityTooSmall
|
|
case SignatureDoesNotMatch:
|
|
apiErr = ErrSignatureDoesNotMatch
|
|
case hash.SHA256Mismatch:
|
|
apiErr = ErrContentSHA256Mismatch
|
|
case ObjectTooLarge:
|
|
apiErr = ErrEntityTooLarge
|
|
case ObjectTooSmall:
|
|
apiErr = ErrEntityTooSmall
|
|
case NotImplemented:
|
|
apiErr = ErrNotImplemented
|
|
case PartTooBig:
|
|
apiErr = ErrEntityTooLarge
|
|
case UnsupportedMetadata:
|
|
apiErr = ErrUnsupportedMetadata
|
|
case PartsSizeUnequal:
|
|
apiErr = ErrPartsSizeUnequal
|
|
case BucketPolicyNotFound:
|
|
apiErr = ErrNoSuchBucketPolicy
|
|
case *event.ErrInvalidEventName:
|
|
apiErr = ErrEventNotification
|
|
case *event.ErrInvalidARN:
|
|
apiErr = ErrARNNotification
|
|
case *event.ErrARNNotFound:
|
|
apiErr = ErrARNNotification
|
|
case *event.ErrUnknownRegion:
|
|
apiErr = ErrRegionNotification
|
|
case *event.ErrInvalidFilterName:
|
|
apiErr = ErrFilterNameInvalid
|
|
case *event.ErrFilterNamePrefix:
|
|
apiErr = ErrFilterNamePrefix
|
|
case *event.ErrFilterNameSuffix:
|
|
apiErr = ErrFilterNameSuffix
|
|
case *event.ErrInvalidFilterValue:
|
|
apiErr = ErrFilterValueInvalid
|
|
case *event.ErrDuplicateEventName:
|
|
apiErr = ErrOverlappingConfigs
|
|
case *event.ErrDuplicateQueueConfiguration:
|
|
apiErr = ErrOverlappingFilterNotification
|
|
case *event.ErrUnsupportedConfiguration:
|
|
apiErr = ErrUnsupportedNotification
|
|
case BackendDown:
|
|
apiErr = ErrBackendDown
|
|
default:
|
|
apiErr = ErrInternalError
|
|
}
|
|
|
|
return apiErr
|
|
}
|
|
|
|
// getAPIError provides API Error for input API error code.
|
|
func getAPIError(code APIErrorCode) APIError {
|
|
return errorCodeResponse[code]
|
|
}
|
|
|
|
// getErrorResponse gets in standard error and resource value and
|
|
// provides a encodable populated response values
|
|
func getAPIErrorResponse(err APIError, resource string) APIErrorResponse {
|
|
return APIErrorResponse{
|
|
Code: err.Code,
|
|
Message: err.Description,
|
|
Resource: resource,
|
|
RequestID: "3L137",
|
|
HostID: "3L137",
|
|
}
|
|
}
|
|
|