[#399] object/fmt: Check expiration epoch in tombstone body and header

According to nspcc-dev/neofs-api#136 tombstone body should store the same
attribute as in object header. If they are different, then check is failed
with `errTombstoneExpiration`.

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2021-02-19 12:29:42 +03:00 committed by Alex Vanin
parent 3ed0065455
commit e6cdf3fbf5

View file

@ -38,6 +38,10 @@ var errNilID = errors.New("missing identifier")
var errNilCID = errors.New("missing container identifier") var errNilCID = errors.New("missing container identifier")
var errNoExpirationEpoch = errors.New("missing expiration epoch attribute")
var errTombstoneExpiration = errors.New("tombstone body and header contain different expiration values")
func defaultCfg() *cfg { func defaultCfg() *cfg {
return new(cfg) return new(cfg)
} }
@ -132,6 +136,17 @@ func (v *FormatValidator) ValidateContent(o *Object) error {
return errors.Wrapf(err, "(%T) could not unmarshal tombstone content", v) return errors.Wrapf(err, "(%T) could not unmarshal tombstone content", v)
} }
// check if tombstone has the same expiration in body and header
exp, err := expirationEpochAttribute(o)
if err != nil {
return err
}
if exp != tombstone.ExpirationEpoch() {
return errTombstoneExpiration
}
// mark all objects from tombstone body as removed in storage engine
cid := o.ContainerID() cid := o.ContainerID()
idList := tombstone.Members() idList := tombstone.Members()
addrList := make([]*object.Address, 0, len(idList)) addrList := make([]*object.Address, 0, len(idList))
@ -177,24 +192,32 @@ func (v *FormatValidator) ValidateContent(o *Object) error {
var errExpired = errors.New("object has expired") var errExpired = errors.New("object has expired")
func (v *FormatValidator) checkExpiration(obj *Object) error { func (v *FormatValidator) checkExpiration(obj *Object) error {
exp, err := expirationEpochAttribute(obj)
if err != nil {
if errors.Is(err, errNoExpirationEpoch) {
return nil // objects without expiration attribute are valid
}
return err
}
if exp < v.netState.CurrentEpoch() {
return errExpired
}
return nil
}
func expirationEpochAttribute(obj *Object) (uint64, error) {
for _, a := range obj.Attributes() { for _, a := range obj.Attributes() {
if a.Key() != objectV2.SysAttributeExpEpoch { if a.Key() != objectV2.SysAttributeExpEpoch {
continue continue
} }
exp, err := strconv.ParseUint(a.Value(), 10, 64) return strconv.ParseUint(a.Value(), 10, 64)
if err != nil {
return err
}
if exp < v.netState.CurrentEpoch() {
return errExpired
}
break
} }
return nil return 0, errNoExpirationEpoch
} }
// WithNetState returns options to set network state interface. // WithNetState returns options to set network state interface.