2020-09-29 12:38:35 +00:00
|
|
|
package putsvc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2023-04-03 11:23:53 +00:00
|
|
|
"context"
|
2020-09-29 12:38:35 +00:00
|
|
|
"crypto/sha256"
|
2021-06-07 09:29:38 +00:00
|
|
|
"errors"
|
2021-05-18 08:12:51 +00:00
|
|
|
"fmt"
|
2020-09-29 12:38:35 +00:00
|
|
|
"hash"
|
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/transformer"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
|
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
2020-09-29 12:38:35 +00:00
|
|
|
)
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
// validatingTarget validates object format and content.
|
2020-09-29 12:38:35 +00:00
|
|
|
type validatingTarget struct {
|
|
|
|
nextTarget transformer.ObjectTarget
|
|
|
|
|
|
|
|
fmt *object.FormatValidator
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
unpreparedObject bool
|
|
|
|
|
2020-09-29 12:38:35 +00:00
|
|
|
hash hash.Hash
|
|
|
|
|
|
|
|
checksum []byte
|
2021-06-07 09:29:38 +00:00
|
|
|
|
|
|
|
maxPayloadSz uint64 // network config
|
|
|
|
|
|
|
|
payloadSz uint64 // payload size of the streaming object from header
|
|
|
|
|
|
|
|
writtenPayload uint64 // number of already written payload bytes
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 09:29:38 +00:00
|
|
|
var (
|
2022-11-01 12:53:57 +00:00
|
|
|
// ErrExceedingMaxSize is returned when payload size is greater than the limit.
|
2022-02-08 11:14:21 +00:00
|
|
|
ErrExceedingMaxSize = errors.New("payload size is greater than the limit")
|
2022-11-01 12:53:57 +00:00
|
|
|
// ErrWrongPayloadSize is returned when chunk payload size is greater than the length declared in header.
|
2022-02-08 11:14:21 +00:00
|
|
|
ErrWrongPayloadSize = errors.New("wrong payload size")
|
2021-06-07 09:29:38 +00:00
|
|
|
)
|
|
|
|
|
2023-04-12 14:01:29 +00:00
|
|
|
func (t *validatingTarget) WriteHeader(ctx context.Context, obj *objectSDK.Object) error {
|
2021-06-07 09:29:38 +00:00
|
|
|
t.payloadSz = obj.PayloadSize()
|
|
|
|
chunkLn := uint64(len(obj.Payload()))
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
if !t.unpreparedObject {
|
|
|
|
// check chunk size
|
|
|
|
if chunkLn > t.payloadSz {
|
|
|
|
return ErrWrongPayloadSize
|
|
|
|
}
|
|
|
|
|
|
|
|
// check payload size limit
|
|
|
|
if t.payloadSz > t.maxPayloadSz {
|
|
|
|
return ErrExceedingMaxSize
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:35:01 +00:00
|
|
|
cs, csSet := obj.PayloadChecksum()
|
|
|
|
if !csSet {
|
|
|
|
return errors.New("missing payload checksum")
|
|
|
|
}
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
switch typ := cs.Type(); typ {
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("(%T) unsupported payload checksum type %v", t, typ)
|
|
|
|
case checksum.SHA256:
|
|
|
|
t.hash = sha256.New()
|
|
|
|
case checksum.TZ:
|
|
|
|
t.hash = tz.New()
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:35:01 +00:00
|
|
|
t.checksum = cs.Value()
|
2021-06-07 09:29:38 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 14:01:29 +00:00
|
|
|
if err := t.fmt.Validate(ctx, obj, t.unpreparedObject); err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return fmt.Errorf("(%T) coult not validate object format: %w", t, err)
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 14:01:29 +00:00
|
|
|
err := t.nextTarget.WriteHeader(ctx, obj)
|
2021-06-07 09:29:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
if !t.unpreparedObject {
|
|
|
|
// update written bytes
|
|
|
|
//
|
|
|
|
// Note: we MUST NOT add obj.PayloadSize() since obj
|
|
|
|
// can carry only the chunk of the full payload
|
|
|
|
t.writtenPayload += chunkLn
|
|
|
|
}
|
2021-06-07 09:29:38 +00:00
|
|
|
|
|
|
|
return nil
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (t *validatingTarget) Write(ctx context.Context, p []byte) (n int, err error) {
|
2021-06-07 09:29:38 +00:00
|
|
|
chunkLn := uint64(len(p))
|
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
if !t.unpreparedObject {
|
|
|
|
// check if new chunk will overflow payload size
|
|
|
|
if t.writtenPayload+chunkLn > t.payloadSz {
|
|
|
|
return 0, ErrWrongPayloadSize
|
|
|
|
}
|
2021-06-07 09:29:38 +00:00
|
|
|
|
2022-04-04 13:04:18 +00:00
|
|
|
_, err = t.hash.Write(p)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-01-11 15:14:16 +00:00
|
|
|
}
|
2020-09-29 12:38:35 +00:00
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
n, err = t.nextTarget.Write(ctx, p)
|
2021-06-07 09:29:38 +00:00
|
|
|
if err == nil {
|
|
|
|
t.writtenPayload += uint64(n)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (t *validatingTarget) Close(ctx context.Context) (*transformer.AccessIdentifiers, error) {
|
2022-04-04 13:04:18 +00:00
|
|
|
if !t.unpreparedObject {
|
|
|
|
// check payload size correctness
|
|
|
|
if t.payloadSz != t.writtenPayload {
|
|
|
|
return nil, ErrWrongPayloadSize
|
|
|
|
}
|
|
|
|
|
|
|
|
if !bytes.Equal(t.hash.Sum(nil), t.checksum) {
|
|
|
|
return nil, fmt.Errorf("(%T) incorrect payload checksum", t)
|
|
|
|
}
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
return t.nextTarget.Close(ctx)
|
2020-09-29 12:38:35 +00:00
|
|
|
}
|