mirror of
https://github.com/versity/versitygw.git
synced 2026-04-22 05:30:29 +00:00
It is better if we let the s3response module handle the xml formatting spec specifics, and let the backends not worry about how to format the time fields. This should help to prevent any future backend modifications or additions from accidental incorrect time formatting.
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
// Copyright 2023 Versity Software
|
|
// This file is 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 backend
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/versity/versitygw/s3err"
|
|
"github.com/versity/versitygw/s3response"
|
|
)
|
|
|
|
func IsValidBucketName(name string) bool { return true }
|
|
|
|
type ByBucketName []s3response.ListAllMyBucketsEntry
|
|
|
|
func (d ByBucketName) Len() int { return len(d) }
|
|
func (d ByBucketName) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
|
func (d ByBucketName) Less(i, j int) bool { return d[i].Name < d[j].Name }
|
|
|
|
type ByObjectName []types.Object
|
|
|
|
func (d ByObjectName) Len() int { return len(d) }
|
|
func (d ByObjectName) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
|
func (d ByObjectName) Less(i, j int) bool { return *d[i].Key < *d[j].Key }
|
|
|
|
func GetStringPtr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
func GetTimePtr(t time.Time) *time.Time {
|
|
return &t
|
|
}
|
|
|
|
var (
|
|
errInvalidRange = s3err.GetAPIError(s3err.ErrInvalidRange)
|
|
)
|
|
|
|
// ParseRange parses input range header and returns startoffset, length, and
|
|
// error. If no endoffset specified, then length is set to -1.
|
|
func ParseRange(size int64, acceptRange string) (int64, int64, error) {
|
|
if acceptRange == "" {
|
|
return 0, size, nil
|
|
}
|
|
|
|
rangeKv := strings.Split(acceptRange, "=")
|
|
|
|
if len(rangeKv) < 2 {
|
|
return 0, 0, errInvalidRange
|
|
}
|
|
|
|
bRange := strings.Split(rangeKv[1], "-")
|
|
if len(bRange) < 1 || len(bRange) > 2 {
|
|
return 0, 0, errInvalidRange
|
|
}
|
|
|
|
startOffset, err := strconv.ParseInt(bRange[0], 10, 64)
|
|
if err != nil {
|
|
return 0, 0, errInvalidRange
|
|
}
|
|
|
|
endOffset := int64(-1)
|
|
if len(bRange) == 1 || bRange[1] == "" {
|
|
return startOffset, endOffset, nil
|
|
}
|
|
|
|
endOffset, err = strconv.ParseInt(bRange[1], 10, 64)
|
|
if err != nil {
|
|
return 0, 0, errInvalidRange
|
|
}
|
|
|
|
if endOffset <= startOffset {
|
|
return 0, 0, errInvalidRange
|
|
}
|
|
|
|
return startOffset, endOffset - startOffset + 1, nil
|
|
}
|
|
|
|
func CreateExceedingRangeErr(objSize int64) s3err.APIError {
|
|
return s3err.APIError{
|
|
Code: "InvalidArgument",
|
|
Description: fmt.Sprintf("Range specified is not valid for source object of size: %d", objSize),
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
}
|
|
}
|
|
|
|
func GetMultipartMD5(parts []types.CompletedPart) string {
|
|
var partsEtagBytes []byte
|
|
for _, part := range parts {
|
|
partsEtagBytes = append(partsEtagBytes, getEtagBytes(*part.ETag)...)
|
|
}
|
|
s3MD5 := fmt.Sprintf("%s-%d", md5String(partsEtagBytes), len(parts))
|
|
return s3MD5
|
|
}
|
|
|
|
func getEtagBytes(etag string) []byte {
|
|
decode, err := hex.DecodeString(strings.ReplaceAll(etag, string('"'), ""))
|
|
if err != nil {
|
|
return []byte(etag)
|
|
}
|
|
return decode
|
|
}
|
|
|
|
func md5String(data []byte) string {
|
|
sum := md5.Sum(data)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
type FileSectionReadCloser struct {
|
|
R io.Reader
|
|
F *os.File
|
|
}
|
|
|
|
func (f *FileSectionReadCloser) Read(p []byte) (int, error) {
|
|
return f.R.Read(p)
|
|
}
|
|
|
|
func (f *FileSectionReadCloser) Close() error {
|
|
return f.F.Close()
|
|
}
|