| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- package s3api
- import (
- "net/http"
- "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
- )
- // CopySizeCalculator handles size calculations for different copy scenarios
- type CopySizeCalculator struct {
- srcSize int64
- srcEncrypted bool
- dstEncrypted bool
- srcType EncryptionType
- dstType EncryptionType
- isCompressed bool
- }
- // EncryptionType represents different encryption types
- type EncryptionType int
- const (
- EncryptionTypeNone EncryptionType = iota
- EncryptionTypeSSEC
- EncryptionTypeSSEKMS
- EncryptionTypeSSES3
- )
- // NewCopySizeCalculator creates a new size calculator for copy operations
- func NewCopySizeCalculator(entry *filer_pb.Entry, r *http.Request) *CopySizeCalculator {
- calc := &CopySizeCalculator{
- srcSize: int64(entry.Attributes.FileSize),
- isCompressed: isCompressedEntry(entry),
- }
- // Determine source encryption type
- calc.srcType, calc.srcEncrypted = getSourceEncryptionType(entry.Extended)
- // Determine destination encryption type
- calc.dstType, calc.dstEncrypted = getDestinationEncryptionType(r)
- return calc
- }
- // CalculateTargetSize calculates the expected size of the target object
- func (calc *CopySizeCalculator) CalculateTargetSize() int64 {
- // For compressed objects, size calculation is complex
- if calc.isCompressed {
- return -1 // Indicates unknown size
- }
- switch {
- case !calc.srcEncrypted && !calc.dstEncrypted:
- // Plain → Plain: no size change
- return calc.srcSize
- case !calc.srcEncrypted && calc.dstEncrypted:
- // Plain → Encrypted: no overhead since IV is in metadata
- return calc.srcSize
- case calc.srcEncrypted && !calc.dstEncrypted:
- // Encrypted → Plain: no overhead since IV is in metadata
- return calc.srcSize
- case calc.srcEncrypted && calc.dstEncrypted:
- // Encrypted → Encrypted: no overhead since IV is in metadata
- return calc.srcSize
- default:
- return calc.srcSize
- }
- }
- // CalculateActualSize calculates the actual unencrypted size of the content
- func (calc *CopySizeCalculator) CalculateActualSize() int64 {
- // With IV in metadata, encrypted and unencrypted sizes are the same
- return calc.srcSize
- }
- // CalculateEncryptedSize calculates the encrypted size for the given encryption type
- func (calc *CopySizeCalculator) CalculateEncryptedSize(encType EncryptionType) int64 {
- // With IV in metadata, encrypted size equals actual size
- return calc.CalculateActualSize()
- }
- // getSourceEncryptionType determines the encryption type of the source object
- func getSourceEncryptionType(metadata map[string][]byte) (EncryptionType, bool) {
- if IsSSECEncrypted(metadata) {
- return EncryptionTypeSSEC, true
- }
- if IsSSEKMSEncrypted(metadata) {
- return EncryptionTypeSSEKMS, true
- }
- if IsSSES3EncryptedInternal(metadata) {
- return EncryptionTypeSSES3, true
- }
- return EncryptionTypeNone, false
- }
- // getDestinationEncryptionType determines the encryption type for the destination
- func getDestinationEncryptionType(r *http.Request) (EncryptionType, bool) {
- if IsSSECRequest(r) {
- return EncryptionTypeSSEC, true
- }
- if IsSSEKMSRequest(r) {
- return EncryptionTypeSSEKMS, true
- }
- if IsSSES3RequestInternal(r) {
- return EncryptionTypeSSES3, true
- }
- return EncryptionTypeNone, false
- }
- // isCompressedEntry checks if the entry represents a compressed object
- func isCompressedEntry(entry *filer_pb.Entry) bool {
- // Check for compression indicators in metadata
- if compressionType, exists := entry.Extended["compression"]; exists {
- return string(compressionType) != ""
- }
- // Check MIME type for compressed formats
- mimeType := entry.Attributes.Mime
- compressedMimeTypes := []string{
- "application/gzip",
- "application/x-gzip",
- "application/zip",
- "application/x-compress",
- "application/x-compressed",
- }
- for _, compressedType := range compressedMimeTypes {
- if mimeType == compressedType {
- return true
- }
- }
- return false
- }
- // SizeTransitionInfo provides detailed information about size changes during copy
- type SizeTransitionInfo struct {
- SourceSize int64
- TargetSize int64
- ActualSize int64
- SizeChange int64
- SourceType EncryptionType
- TargetType EncryptionType
- IsCompressed bool
- RequiresResize bool
- }
- // GetSizeTransitionInfo returns detailed size transition information
- func (calc *CopySizeCalculator) GetSizeTransitionInfo() *SizeTransitionInfo {
- targetSize := calc.CalculateTargetSize()
- actualSize := calc.CalculateActualSize()
- info := &SizeTransitionInfo{
- SourceSize: calc.srcSize,
- TargetSize: targetSize,
- ActualSize: actualSize,
- SizeChange: targetSize - calc.srcSize,
- SourceType: calc.srcType,
- TargetType: calc.dstType,
- IsCompressed: calc.isCompressed,
- RequiresResize: targetSize != calc.srcSize,
- }
- return info
- }
- // String returns a string representation of the encryption type
- func (e EncryptionType) String() string {
- switch e {
- case EncryptionTypeNone:
- return "None"
- case EncryptionTypeSSEC:
- return s3_constants.SSETypeC
- case EncryptionTypeSSEKMS:
- return s3_constants.SSETypeKMS
- case EncryptionTypeSSES3:
- return s3_constants.SSETypeS3
- default:
- return "Unknown"
- }
- }
- // OptimizedSizeCalculation provides size calculations optimized for different scenarios
- type OptimizedSizeCalculation struct {
- Strategy UnifiedCopyStrategy
- SourceSize int64
- TargetSize int64
- ActualContentSize int64
- EncryptionOverhead int64
- CanPreallocate bool
- RequiresStreaming bool
- }
- // CalculateOptimizedSizes calculates sizes optimized for the copy strategy
- func CalculateOptimizedSizes(entry *filer_pb.Entry, r *http.Request, strategy UnifiedCopyStrategy) *OptimizedSizeCalculation {
- calc := NewCopySizeCalculator(entry, r)
- info := calc.GetSizeTransitionInfo()
- result := &OptimizedSizeCalculation{
- Strategy: strategy,
- SourceSize: info.SourceSize,
- TargetSize: info.TargetSize,
- ActualContentSize: info.ActualSize,
- CanPreallocate: !info.IsCompressed && info.TargetSize > 0,
- RequiresStreaming: info.IsCompressed || info.TargetSize < 0,
- }
- // Calculate encryption overhead for the target
- // With IV in metadata, all encryption overhead is 0
- result.EncryptionOverhead = 0
- // Adjust based on strategy
- switch strategy {
- case CopyStrategyDirect:
- // Direct copy: no size change
- result.TargetSize = result.SourceSize
- result.CanPreallocate = true
- case CopyStrategyKeyRotation:
- // Key rotation: size might change slightly due to different IVs
- if info.SourceType == EncryptionTypeSSEC && info.TargetType == EncryptionTypeSSEC {
- // SSE-C key rotation: same overhead
- result.TargetSize = result.SourceSize
- }
- result.CanPreallocate = true
- case CopyStrategyEncrypt, CopyStrategyDecrypt, CopyStrategyReencrypt:
- // Size changes based on encryption transition
- result.TargetSize = info.TargetSize
- result.CanPreallocate = !info.IsCompressed
- }
- return result
- }
|