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 }