diff --git a/cmd/api-errors.go b/cmd/api-errors.go index e4d3989e8..cafa75bbd 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -87,6 +87,7 @@ const ( ErrNoSuchBucketPolicy ErrNoSuchKey ErrNoSuchUpload + ErrNoSuchVersion ErrNotImplemented ErrPreconditionFailed ErrRequestTimeTooSkewed @@ -447,6 +448,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{ 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, }, + ErrNoSuchVersion: { + Code: "NoSuchVersion", + Description: "Indicates that the version ID specified in the request does not match an existing version.", + HTTPStatusCode: http.StatusNotFound, + }, ErrNotImplemented: { Code: "NotImplemented", Description: "A header you provided implies functionality that is not implemented", diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index fd17bca31..52e0926c0 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -323,6 +323,11 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req bucket := vars["bucket"] object := vars["object"] + if vid := r.URL.Query().Get("versionId"); vid != "" && vid != "null" { + writeErrorResponse(w, ErrNoSuchVersion, r.URL, guessIsBrowserReq(r)) + return + } + var ( opts ObjectOptions err error @@ -483,7 +488,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re return } if crypto.S3.IsRequested(r.Header) || crypto.S3KMS.IsRequested(r.Header) { // If SSE-S3 or SSE-KMS present -> AWS fails with undefined error - writeErrorResponse(w, ErrBadRequest, r.URL, guessIsBrowserReq(r)) + writeErrorResponseHeadersOnly(w, ErrBadRequest) return } @@ -491,6 +496,11 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re bucket := vars["bucket"] object := vars["object"] + if vid := r.URL.Query().Get("versionId"); vid != "" && vid != "null" { + writeErrorResponseHeadersOnly(w, ErrNoSuchVersion) + return + } + getObjectInfo := objectAPI.GetObjectInfo if api.CacheAPI() != nil { getObjectInfo = api.CacheAPI().GetObjectInfo @@ -697,6 +707,22 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re cpSrcPath = r.Header.Get("X-Amz-Copy-Source") } + // Check https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectVersioning.html + // Regardless of whether you have enabled versioning, each object in your bucket + // has a version ID. If you have not enabled versioning, Amazon S3 sets the value + // of the version ID to null. If you have enabled versioning, Amazon S3 assigns a + // unique version ID value for the object. + if u, err := url.Parse(cpSrcPath); err == nil { + // Check if versionId query param was added, if yes then check if + // its non "null" value, we should error out since we do not support + // any versions other than "null". + if vid := u.Query().Get("versionId"); vid != "" && vid != "null" { + writeErrorResponse(w, ErrNoSuchVersion, r.URL, guessIsBrowserReq(r)) + return + } + cpSrcPath = u.Path + } + srcBucket, srcObject := path2BucketAndObject(cpSrcPath) // If source object is empty or bucket is empty, reply back invalid copy source. if srcObject == "" || srcBucket == "" {