forked from TrueCloudLab/frostfs-sdk-go
105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
|
package erasurecode
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||
|
)
|
||
|
|
||
|
// Verify verifies that parts are well formed.
|
||
|
// All parts are expected to be non-nil.
|
||
|
// The number of parts must be equal to `total` field of the EC header
|
||
|
// and parts must be sorted by index.
|
||
|
func (c *Constructor) Verify(parts []*objectSDK.Object) error {
|
||
|
c.clear()
|
||
|
|
||
|
var headerLength int
|
||
|
for i := range parts {
|
||
|
if parts[i] == nil {
|
||
|
return ErrMalformedSlice
|
||
|
}
|
||
|
|
||
|
var err error
|
||
|
headerLength, err = validatePart(parts, i, headerLength)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p0 := parts[0]
|
||
|
for i := 1; i < len(parts); i++ {
|
||
|
// This part must be kept in sync with copyRequiredFields().
|
||
|
pi := parts[i]
|
||
|
if p0.OwnerID().Equals(pi.OwnerID()) {
|
||
|
return fmt.Errorf("%w: owner id mismatch: %s != %s", ErrMalformedSlice, p0.OwnerID(), pi.OwnerID())
|
||
|
}
|
||
|
if p0.Version() == nil && pi.Version() != nil || !p0.Version().Equal(*pi.Version()) {
|
||
|
return fmt.Errorf("%w: version mismatch: %s != %s", ErrMalformedSlice, p0.Version(), pi.Version())
|
||
|
}
|
||
|
|
||
|
cnr0, _ := p0.ContainerID()
|
||
|
cnri, _ := pi.ContainerID()
|
||
|
if !cnr0.Equals(cnri) {
|
||
|
return fmt.Errorf("%w: container id mismatch: %s != %s", ErrMalformedSlice, cnr0, cnri)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := c.fillHeader(parts); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
c.fillPayload(parts)
|
||
|
|
||
|
ok, err := c.enc.Verify(c.headerShards)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if !ok {
|
||
|
return ErrMalformedSlice
|
||
|
}
|
||
|
|
||
|
ok, err = c.enc.Verify(c.payloadShards)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if !ok {
|
||
|
return ErrMalformedSlice
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// copyRequiredFields sets all fields in dst which are copied from src and shared among all chunks.
|
||
|
// src can be either another chunk of full object.
|
||
|
// dst must be a chunk.
|
||
|
func copyRequiredFields(dst *objectSDK.Object, src *objectSDK.Object) {
|
||
|
dst.SetVersion(src.Version())
|
||
|
dst.SetOwnerID(src.OwnerID())
|
||
|
dst.SetCreationEpoch(src.CreationEpoch())
|
||
|
dst.SetSessionToken(src.SessionToken())
|
||
|
|
||
|
cnr, _ := src.ContainerID()
|
||
|
dst.SetContainerID(cnr)
|
||
|
}
|
||
|
|
||
|
// validatePart makes i-th part is consistent with the rest.
|
||
|
// If headerLength is not zero it is asserted to be equal in the ec header.
|
||
|
// Otherwise, new headerLength is returned.
|
||
|
func validatePart(parts []*objectSDK.Object, i int, headerLength int) (int, error) {
|
||
|
ec := parts[i].GetECHeader()
|
||
|
if ec == nil {
|
||
|
return headerLength, fmt.Errorf("%w: missing EC header", ErrMalformedSlice)
|
||
|
}
|
||
|
if ec.Index() != uint32(i) {
|
||
|
return headerLength, fmt.Errorf("%w: index=%d, ec.index=%d", ErrMalformedSlice, i, ec.Index())
|
||
|
}
|
||
|
if ec.Total() != uint32(len(parts)) {
|
||
|
return headerLength, fmt.Errorf("%w: len(parts)=%d, total=%d", ErrMalformedSlice, len(parts), ec.Total())
|
||
|
}
|
||
|
if headerLength == 0 {
|
||
|
return int(ec.HeaderLength()), nil
|
||
|
}
|
||
|
if ec.HeaderLength() != uint32(headerLength) {
|
||
|
return headerLength, fmt.Errorf("%w: header length mismatch %d != %d", ErrMalformedSlice, headerLength, ec.HeaderLength())
|
||
|
}
|
||
|
return headerLength, nil
|
||
|
}
|