Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Changelog for NeoFS Node
- Shard evacuation replicate for EC parts (#3854)
- Suboptimal shard selection for EC parts during data evacuation (#3870)
- No redistribution of objects when adding a new `REP 3` node (#3873)
- Split of a split is allowed (#3867)

### Changed
- SN returns unsigned responses to requests with API >= `v2.22` (#3785)
Expand Down
23 changes: 13 additions & 10 deletions pkg/core/object/fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ func NewFormatValidator(fsChain FSChain, netmapContract NetmapContract, containe
//
// Returns nil error if the object has valid structure.
func (v *FormatValidator) Validate(obj *object.Object, unprepared bool) error {
return v.validate(obj, unprepared, false)
return v.validate(obj, unprepared, 0)
}

func (v *FormatValidator) validate(obj *object.Object, unprepared, isParent bool) error {
const maxObjectNestingLevel = 2

func (v *FormatValidator) validate(obj *object.Object, unprepared bool, nestingLevel int) error {
if obj == nil {
return errNilObject
}
Expand Down Expand Up @@ -194,7 +196,7 @@ func (v *FormatValidator) validate(obj *object.Object, unprepared, isParent bool
return fmt.Errorf("read container by ID=%s: %w", cnrID, err)
}

isEC, err := checkEC(*obj, cnr.PlacementPolicy().ECRules(), unprepared, isParent)
isEC, err := checkEC(*obj, cnr.PlacementPolicy().ECRules(), unprepared, nestingLevel > 0)
if err != nil {
return err
}
Expand All @@ -208,10 +210,6 @@ func (v *FormatValidator) validate(obj *object.Object, unprepared, isParent bool
par := obj.Parent()

if !isEC && obj.HasParent() {
if par != nil && par.HasParent() {
return errors.New("parent object has a parent itself")
}

if splitID != nil {
// V1 split
if firstSet {
Expand Down Expand Up @@ -257,9 +255,14 @@ func (v *FormatValidator) validate(obj *object.Object, unprepared, isParent bool
}
}

if par != nil && (firstSet || splitID != nil || isEC) {
// Parent object already exists.
return v.validate(par, false, true)
if par != nil {
if nestingLevel == maxObjectNestingLevel {
return fmt.Errorf("max object nesting level %d overflow", maxObjectNestingLevel)
}

// it is possible to have a split of a split
prepared := firstSet || splitID != nil || isEC
return v.validate(par, !prepared, nestingLevel+1)
}

return nil
Expand Down
60 changes: 60 additions & 0 deletions pkg/core/object/fmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test"
"github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
"github.com/nspcc-dev/neofs-sdk-go/user"
Expand Down Expand Up @@ -406,6 +407,27 @@ func TestFormatValidator_Validate(t *testing.T) {
})
})
})

t.Run("split", func(t *testing.T) {
t.Run("nesting limit exceeded", func(t *testing.T) {
chain := getParentChain(t, maxObjectNestingLevel+2) // one exceeds and one is a child
child := chain[len(chain)-1]

registerContainer(child.GetContainerID())

require.EqualError(t, v.Validate(&child, true), "max object nesting level 2 overflow")
})

t.Run("nesting is ok", func(t *testing.T) {
chain := getParentChain(t, maxObjectNestingLevel+1) // one exceeds and one is a child
child := chain[len(chain)-1]

registerContainer(child.GetContainerID())

require.NoError(t, v.Validate(&child, true))
})
})

for _, tc := range []struct {
scheme neofscrypto.Scheme
object object.Object
Expand All @@ -420,6 +442,44 @@ func TestFormatValidator_Validate(t *testing.T) {
}
}

func getParentChain(t *testing.T, nesting int) []object.Object {
var (
signer neofscrypto.Signer
parChain []object.Object
cID = cidtest.ID()
)
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
signer = neofsecdsa.SignerRFC6979(pk.PrivateKey)
owner := user.NewFromECDSAPublicKey(pk.PrivateKey.PublicKey)

for i := range nesting {
o := objecttest.Object()

o.SetOwner(owner)
o.SetSessionToken(nil)
o.SetContainerID(cID)
o.ResetRelations()
o.SetType(object.TypeRegular)
if i != 0 {
o.SetParent(&parChain[i-1])
}
err = o.SetIDWithSignature(signer)
require.NoError(t, err)

err := o.CalculateAndSetID()
require.NoError(t, err)

parChain = append(parChain, o)
}

child := &parChain[len(parChain)-1]
child.SetFirstID(oidtest.ID())
child.SetPreviousID(oidtest.ID())

return parChain
}

type testSplitVerifier struct {
}

Expand Down
1 change: 0 additions & 1 deletion pkg/services/object/split/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ func (v *Verifier) verifySinglePart(ctx context.Context, cnr cid.ID, firstID *oi
var prm getsvc.HeadPrm
prm.SetHeaderWriter(&hw)
prm.WithAddress(childAddr)
prm.WithRawFlag(true)

err := v.get.Head(ctx, prm)
if err != nil {
Expand Down
Loading