frostfs-s3-gw/api/layer/util.go

154 lines
4.1 KiB
Go

package layer
import (
"encoding/hex"
"fmt"
"os"
"strconv"
"strings"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
)
type (
// ListObjectsInfo contains common fields of data for ListObjectsV1 and ListObjectsV2.
ListObjectsInfo struct {
Prefixes []string
Objects []*data.ObjectInfo
IsTruncated bool
}
// ListObjectsInfoV1 holds data which ListObjectsV1 returns.
ListObjectsInfoV1 struct {
ListObjectsInfo
NextMarker string
}
// ListObjectsInfoV2 holds data which ListObjectsV2 returns.
ListObjectsInfoV2 struct {
ListObjectsInfo
NextContinuationToken string
}
// ListObjectVersionsInfo stores info and list of objects versions.
ListObjectVersionsInfo struct {
CommonPrefixes []string
IsTruncated bool
KeyMarker string
NextKeyMarker string
NextVersionIDMarker string
Version []*data.ExtendedObjectInfo
DeleteMarker []*data.ExtendedObjectInfo
VersionIDMarker string
}
)
// PathSeparator is a path components separator string.
const PathSeparator = string(os.PathSeparator)
func userHeaders(attrs []object.Attribute) map[string]string {
result := make(map[string]string, len(attrs))
for _, attr := range attrs {
result[attr.Key()] = attr.Value()
}
return result
}
func objectInfoFromMeta(bkt *data.BucketInfo, meta *object.Object) *data.ObjectInfo {
var (
mimeType string
creation time.Time
)
headers := userHeaders(meta.Attributes())
delete(headers, object.AttributeFilePath)
if contentType, ok := headers[object.AttributeContentType]; ok {
mimeType = contentType
delete(headers, object.AttributeContentType)
}
if val, ok := headers[object.AttributeTimestamp]; !ok { //nolint:revive
// ignore empty value
} else if dt, err := strconv.ParseInt(val, 10, 64); err == nil {
creation = time.Unix(dt, 0)
delete(headers, object.AttributeTimestamp)
}
objID, _ := meta.ID()
payloadChecksum, _ := meta.PayloadChecksum()
return &data.ObjectInfo{
ID: objID,
CID: bkt.CID,
IsDir: false,
Bucket: bkt.Name,
Name: filepathFromObject(meta),
Created: creation,
ContentType: mimeType,
Headers: headers,
Owner: *meta.OwnerID(),
Size: meta.PayloadSize(),
CreationEpoch: meta.CreationEpoch(),
HashSum: hex.EncodeToString(payloadChecksum.Value()),
}
}
func GetObjectSize(objInfo *data.ObjectInfo) (uint64, error) {
var err error
fullSize := objInfo.Size
if objInfo.Headers[AttributeDecryptedSize] != "" {
if fullSize, err = strconv.ParseUint(objInfo.Headers[AttributeDecryptedSize], 10, 64); err != nil {
return 0, fmt.Errorf("invalid decrypted size header: %w", err)
}
} else if objInfo.Headers[MultipartObjectSize] != "" {
if fullSize, err = strconv.ParseUint(objInfo.Headers[MultipartObjectSize], 10, 64); err != nil {
return 0, fmt.Errorf("invalid multipart size header: %w", err)
}
}
return fullSize, nil
}
func FormEncryptionInfo(headers map[string]string) encryption.ObjectEncryption {
algorithm := headers[AttributeEncryptionAlgorithm]
return encryption.ObjectEncryption{
Enabled: len(algorithm) > 0,
Algorithm: algorithm,
HMACKey: headers[AttributeHMACKey],
HMACSalt: headers[AttributeHMACSalt],
}
}
func addEncryptionHeaders(meta map[string]string, enc encryption.Params) error {
meta[AttributeEncryptionAlgorithm] = AESEncryptionAlgorithm
hmacKey, hmacSalt, err := enc.HMAC()
if err != nil {
return fmt.Errorf("get hmac: %w", err)
}
meta[AttributeHMACKey] = hex.EncodeToString(hmacKey)
meta[AttributeHMACSalt] = hex.EncodeToString(hmacSalt)
return nil
}
func filepathFromObject(o *object.Object) string {
for _, attr := range o.Attributes() {
if attr.Key() == object.AttributeFilePath {
return attr.Value()
}
}
objID, _ := o.ID()
return objID.EncodeToString()
}
// NameFromString splits name into a base file name and a directory path.
func NameFromString(name string) (string, string) {
ind := strings.LastIndex(name, PathSeparator)
return name[ind+1:], name[:ind+1]
}