From 1186f2f7035dd59a3f4b059492c95ee10d778ce1 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Tue, 12 Apr 2022 19:23:16 +0300 Subject: [PATCH] [#170] oid, cid: Add marshal format checks Also add checking presence of the `oid`, `cid` fields via `set` flag. Signed-off-by: Pavel Karpy --- audit/result.go | 55 ++++++++++++++++++--- audit/result_test.go | 7 ++- container/announcement.go | 29 ++++++++++- container/announcement_test.go | 11 +++-- container/id/id.go | 6 --- eacl/table.go | 55 ++++++++++++++++++--- eacl/table_test.go | 7 ++- object/address/address.go | 82 +++++++++++++++++++++++++------ object/address/address_test.go | 22 ++++++--- object/fmt.go | 21 ++++++-- object/id/id.go | 6 --- object/object.go | 90 ++++++++++++++++++++++++++++------ object/object_test.go | 4 +- object/raw_test.go | 21 ++++++-- object/splitinfo.go | 62 +++++++++++++++++++---- object/splitinfo_test.go | 14 ++++-- pool/pool.go | 58 +++++++++++++--------- 17 files changed, 432 insertions(+), 118 deletions(-) diff --git a/audit/result.go b/audit/result.go index c89dbfd..161c8c7 100644 --- a/audit/result.go +++ b/audit/result.go @@ -1,6 +1,9 @@ package audit import ( + "errors" + "fmt" + "github.com/nspcc-dev/neofs-api-go/v2/audit" "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" @@ -43,17 +46,54 @@ func (r *Result) Marshal() []byte { return data } +var errCIDNotSet = errors.New("container ID is not set") + // Unmarshal decodes Result from its canonical NeoFS binary format (Protocol Buffers // with direct field order). Returns an error describing a format violation. // // See also Marshal. func (r *Result) Unmarshal(data []byte) error { err := r.v2.Unmarshal(data) - if err == nil { - r.versionEncoded = true + if err != nil { + return err } - return err + r.versionEncoded = true + + // format checks + + var cID cid.ID + + cidV2 := r.v2.GetContainerID() + if cidV2 == nil { + return errCIDNotSet + } + + err = cID.ReadFromV2(*cidV2) + if err != nil { + return fmt.Errorf("could not convert V2 container ID: %w", err) + } + + var ( + oID oid.ID + oidV2 refs.ObjectID + ) + + for _, oidV2 = range r.v2.GetPassSG() { + err = oID.ReadFromV2(oidV2) + if err != nil { + return fmt.Errorf("invalid passed storage group ID: %w", err) + } + } + + for _, oidV2 = range r.v2.GetFailSG() { + err = oID.ReadFromV2(oidV2) + if err != nil { + return fmt.Errorf("invalid failed storage group ID: %w", err) + } + } + + return nil } // Epoch returns NeoFS epoch when the data associated with the Result was audited. @@ -73,20 +113,21 @@ func (r *Result) ForEpoch(epoch uint64) { } // Container returns identifier of the container with which the data audit Result -// is associated. +// is associated and a bool that indicates container ID field presence in the Result. // -// Returns zero ID if container is not specified. Zero Result has zero container. +// Zero Result does not have container ID. // // See also ForContainer. -func (r Result) Container() cid.ID { +func (r Result) Container() (cid.ID, bool) { var cID cid.ID cidV2 := r.v2.GetContainerID() if cidV2 != nil { _ = cID.ReadFromV2(*cidV2) + return cID, true } - return cID + return cID, false } // ForContainer sets identifier of the container with which the data audit Result diff --git a/audit/result_test.go b/audit/result_test.go index e41cb40..0f8c413 100644 --- a/audit/result_test.go +++ b/audit/result_test.go @@ -66,7 +66,8 @@ func TestResultData(t *testing.T) { countFailNodes := func(f func([]byte)) int { return countNodes(false, f) } require.Zero(t, r.Epoch()) - require.True(t, r.Container().Empty()) + _, set := r.Container() + require.False(t, set) require.Nil(t, r.AuditorKey()) require.False(t, r.Completed()) require.Zero(t, r.RequestsPoR()) @@ -82,7 +83,9 @@ func TestResultData(t *testing.T) { cnr := cidtest.ID() r.ForContainer(cnr) - require.Equal(t, cnr, r.Container()) + cID, set := r.Container() + require.True(t, set) + require.Equal(t, cnr, cID) key := []byte{1, 2, 3} r.SetAuditorKey(key) diff --git a/container/announcement.go b/container/announcement.go index 4fc3538..0378abc 100644 --- a/container/announcement.go +++ b/container/announcement.go @@ -1,6 +1,9 @@ package container import ( + "errors" + "fmt" + "github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" @@ -39,7 +42,7 @@ func (a *UsedSpaceAnnouncement) SetEpoch(epoch uint64) { } // ContainerID of the announcement. -func (a *UsedSpaceAnnouncement) ContainerID() (cID cid.ID) { +func (a *UsedSpaceAnnouncement) ContainerID() (cID cid.ID, isSet bool) { v2 := (*container.UsedSpaceAnnouncement)(a) cidV2 := v2.GetContainerID() @@ -48,6 +51,7 @@ func (a *UsedSpaceAnnouncement) ContainerID() (cID cid.ID) { } _ = cID.ReadFromV2(*cidV2) + isSet = true return } @@ -82,7 +86,28 @@ func (a *UsedSpaceAnnouncement) Marshal() ([]byte, error) { return a.ToV2().StableMarshal(nil) } +var errCIDNotSet = errors.New("container ID is not set") + // Unmarshal unmarshals protobuf binary representation of UsedSpaceAnnouncement. func (a *UsedSpaceAnnouncement) Unmarshal(data []byte) error { - return a.ToV2().Unmarshal(data) + err := a.ToV2().Unmarshal(data) + if err != nil { + return err + } + + // format checks + + var cID cid.ID + + cidV2 := a.ToV2().GetContainerID() + if cidV2 == nil { + return errCIDNotSet + } + + err = cID.ReadFromV2(*cidV2) + if err != nil { + return fmt.Errorf("could not convert V2 container ID: %w", err) + } + + return nil } diff --git a/container/announcement_test.go b/container/announcement_test.go index afc3bd3..40084d9 100644 --- a/container/announcement_test.go +++ b/container/announcement_test.go @@ -26,7 +26,9 @@ func TestAnnouncement(t *testing.T) { require.Equal(t, epoch, a.Epoch()) require.Equal(t, usedSpace, a.UsedSpace()) - require.Equal(t, id, a.ContainerID()) + cID, set := a.ContainerID() + require.True(t, set) + require.Equal(t, id, cID) t.Run("test v2", func(t *testing.T) { const newEpoch, newUsedSpace uint64 = 20, 200 @@ -51,7 +53,9 @@ func TestAnnouncement(t *testing.T) { require.Equal(t, newEpoch, newA.Epoch()) require.Equal(t, newUsedSpace, newA.UsedSpace()) - require.Equal(t, cID, newA.ContainerID()) + cIDNew, set := newA.ContainerID() + require.True(t, set) + require.Equal(t, cID, cIDNew) }) } @@ -82,7 +86,8 @@ func TestUsedSpaceAnnouncement_ToV2(t *testing.T) { // check initial values require.Zero(t, announcement.Epoch()) require.Zero(t, announcement.UsedSpace()) - require.True(t, announcement.ContainerID().Empty()) + _, set := announcement.ContainerID() + require.False(t, set) // convert to v2 message announcementV2 := announcement.ToV2() diff --git a/container/id/id.go b/container/id/id.go index e064cfc..cdbddce 100644 --- a/container/id/id.go +++ b/container/id/id.go @@ -112,9 +112,3 @@ func (id *ID) DecodeString(s string) error { func (id ID) String() string { return id.EncodeToString() } - -// Empty returns true if it is called on -// zero container ID. -func (id ID) Empty() bool { - return id == ID{} -} diff --git a/eacl/table.go b/eacl/table.go index ba090f9..4cdfe93 100644 --- a/eacl/table.go +++ b/eacl/table.go @@ -2,6 +2,8 @@ package eacl import ( "crypto/sha256" + "errors" + "fmt" v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl" "github.com/nspcc-dev/neofs-api-go/v2/refs" @@ -16,20 +18,25 @@ import ( // Table is compatible with v2 acl.EACLTable message. type Table struct { version version.Version - cid cid.ID + cid *cid.ID token *session.Token sig *signature.Signature records []Record } // CID returns identifier of the container that should use given access control rules. -func (t Table) CID() cid.ID { - return t.cid +func (t Table) CID() (cID cid.ID, isSet bool) { + if t.cid != nil { + cID = *t.cid + isSet = true + } + + return } // SetCID sets identifier of the container that should use given access control rules. func (t *Table) SetCID(cid cid.ID) { - t.cid = cid + t.cid = &cid } // Version returns version of eACL format. @@ -87,7 +94,7 @@ func (t *Table) ToV2() *v2acl.Table { v2 := new(v2acl.Table) var cidV2 refs.ContainerID - if !t.cid.Empty() { + if t.cid != nil { t.cid.WriteToV2(&cidV2) v2.SetContainerID(&cidV2) } @@ -150,8 +157,8 @@ func NewTableFromV2(table *v2acl.Table) *Table { // set container id if id := table.GetContainerID(); id != nil { - if t.cid.Empty() { - t.cid = cid.ID{} + if t.cid == nil { + t.cid = new(cid.ID) } var h [sha256.Size]byte @@ -176,6 +183,8 @@ func (t *Table) Marshal() ([]byte, error) { return t.ToV2().StableMarshal(nil) } +var errCIDNotSet = errors.New("container ID is not set") + // Unmarshal unmarshals protobuf binary representation of Table. func (t *Table) Unmarshal(data []byte) error { fV2 := new(v2acl.Table) @@ -183,6 +192,12 @@ func (t *Table) Unmarshal(data []byte) error { return err } + // format checks + err := checkFormat(fV2) + if err != nil { + return err + } + *t = *NewTableFromV2(fV2) return nil @@ -200,6 +215,11 @@ func (t *Table) UnmarshalJSON(data []byte) error { return err } + err := checkFormat(tV2) + if err != nil { + return err + } + *t = *NewTableFromV2(tV2) return nil @@ -207,7 +227,10 @@ func (t *Table) UnmarshalJSON(data []byte) error { // EqualTables compares Table with each other. func EqualTables(t1, t2 Table) bool { - if !t1.CID().Equals(t2.CID()) || + cID1, set1 := t1.CID() + cID2, set2 := t2.CID() + + if set1 != set2 || cID1 != cID2 || !t1.Version().Equal(t2.Version()) { return false } @@ -226,3 +249,19 @@ func EqualTables(t1, t2 Table) bool { return true } + +func checkFormat(v2 *v2acl.Table) error { + var cID cid.ID + + cidV2 := v2.GetContainerID() + if cidV2 == nil { + return errCIDNotSet + } + + err := cID.ReadFromV2(*cidV2) + if err != nil { + return fmt.Errorf("could not convert V2 container ID: %w", err) + } + + return nil +} diff --git a/eacl/table_test.go b/eacl/table_test.go index 6681761..800d3b0 100644 --- a/eacl/table_test.go +++ b/eacl/table_test.go @@ -46,7 +46,9 @@ func TestTable(t *testing.T) { id := cidtest.ID() table := eacl.CreateTable(id) - require.Equal(t, id, table.CID()) + cID, set := table.CID() + require.True(t, set) + require.Equal(t, id, cID) require.Equal(t, version.Current(), table.Version()) }) } @@ -124,7 +126,8 @@ func TestTable_ToV2(t *testing.T) { // check initial values require.Equal(t, version.Current(), table.Version()) require.Nil(t, table.Records()) - require.True(t, table.CID().Empty()) + _, set := table.CID() + require.False(t, set) require.Nil(t, table.SessionToken()) require.Nil(t, table.Signature()) diff --git a/object/address/address.go b/object/address/address.go index cb74a2a..736b034 100644 --- a/object/address/address.go +++ b/object/address/address.go @@ -2,6 +2,7 @@ package address import ( "errors" + "fmt" "strings" "github.com/nspcc-dev/neofs-api-go/v2/refs" @@ -45,16 +46,16 @@ func (a *Address) ToV2() *refs.Address { } // ContainerID returns container identifier. -func (a *Address) ContainerID() (v cid.ID) { - var cID cid.ID +func (a *Address) ContainerID() (v cid.ID, isSet bool) { v2 := (*refs.Address)(a) cidV2 := v2.GetContainerID() if cidV2 != nil { - _ = cID.ReadFromV2(*cidV2) + _ = v.ReadFromV2(*cidV2) + isSet = true } - return cID + return } // SetContainerID sets container identifier. @@ -66,16 +67,16 @@ func (a *Address) SetContainerID(id cid.ID) { } // ObjectID returns object identifier. -func (a *Address) ObjectID() (v oid.ID) { - var id oid.ID +func (a *Address) ObjectID() (v oid.ID, isSet bool) { v2 := (*refs.Address)(a) oidV2 := v2.GetObjectID() if oidV2 != nil { - _ = id.ReadFromV2(*oidV2) + _ = v.ReadFromV2(*oidV2) + isSet = true } - return id + return } // SetObjectID sets object identifier. @@ -111,10 +112,17 @@ func (a *Address) Parse(s string) error { // String returns string representation of Object.Address. func (a *Address) String() string { - return strings.Join([]string{ - a.ContainerID().String(), - a.ObjectID().String(), - }, addressSeparator) + var cidStr, oidStr string + + if cID, set := a.ContainerID(); set { + cidStr = cID.String() + } + + if oID, set := a.ObjectID(); set { + oidStr = oID.String() + } + + return strings.Join([]string{cidStr, oidStr}, addressSeparator) } // Marshal marshals Address into a protobuf binary form. @@ -122,9 +130,19 @@ func (a *Address) Marshal() ([]byte, error) { return (*refs.Address)(a).StableMarshal(nil) } +var errCIDNotSet = errors.New("container ID is not set") +var errOIDNotSet = errors.New("object ID is not set") + // Unmarshal unmarshals protobuf binary representation of Address. func (a *Address) Unmarshal(data []byte) error { - return (*refs.Address)(a).Unmarshal(data) + err := (*refs.Address)(a).Unmarshal(data) + if err != nil { + return err + } + + v2 := a.ToV2() + + return checkFormat(v2) } // MarshalJSON encodes Address to protobuf JSON format. @@ -134,5 +152,41 @@ func (a *Address) MarshalJSON() ([]byte, error) { // UnmarshalJSON decodes Address from protobuf JSON format. func (a *Address) UnmarshalJSON(data []byte) error { - return (*refs.Address)(a).UnmarshalJSON(data) + v2 := (*refs.Address)(a) + + err := v2.UnmarshalJSON(data) + if err != nil { + return err + } + + return checkFormat(v2) +} + +func checkFormat(v2 *refs.Address) error { + var ( + cID cid.ID + oID oid.ID + ) + + cidV2 := v2.GetContainerID() + if cidV2 == nil { + return errCIDNotSet + } + + err := cID.ReadFromV2(*cidV2) + if err != nil { + return fmt.Errorf("could not convert V2 container ID: %w", err) + } + + oidV2 := v2.GetObjectID() + if oidV2 == nil { + return errOIDNotSet + } + + err = oID.ReadFromV2(*oidV2) + if err != nil { + return fmt.Errorf("could not convert V2 object ID: %w", err) + } + + return nil } diff --git a/object/address/address_test.go b/object/address/address_test.go index 428a47a..4d4a47f 100644 --- a/object/address/address_test.go +++ b/object/address/address_test.go @@ -17,7 +17,9 @@ func TestAddress_SetContainerID(t *testing.T) { a.SetContainerID(id) - require.Equal(t, id, a.ContainerID()) + cID, set := a.ContainerID() + require.True(t, set) + require.Equal(t, id, cID) } func TestAddress_SetObjectID(t *testing.T) { @@ -27,7 +29,9 @@ func TestAddress_SetObjectID(t *testing.T) { a.SetObjectID(oid) - require.Equal(t, oid, a.ObjectID()) + oID, set := a.ObjectID() + require.True(t, set) + require.Equal(t, oid, oID) } func TestAddress_Parse(t *testing.T) { @@ -40,8 +44,12 @@ func TestAddress_Parse(t *testing.T) { a := NewAddress() require.NoError(t, a.Parse(s)) - require.Equal(t, oid, a.ObjectID()) - require.Equal(t, cid, a.ContainerID()) + oID, set := a.ObjectID() + require.True(t, set) + require.Equal(t, oid, oID) + cID, set := a.ContainerID() + require.True(t, set) + require.Equal(t, cid, cID) }) t.Run("should fail for bad address", func(t *testing.T) { @@ -107,8 +115,10 @@ func TestNewAddress(t *testing.T) { a := NewAddress() // check initial values - require.True(t, a.ContainerID().Empty()) - require.True(t, a.ObjectID().Empty()) + _, set := a.ContainerID() + require.False(t, set) + _, set = a.ObjectID() + require.False(t, set) // convert to v2 message aV2 := a.ToV2() diff --git a/object/fmt.go b/object/fmt.go index fe5040a..168d722 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -88,7 +88,12 @@ func VerifyID(obj *Object) error { return err } - if !id.Equals(obj.ID()) { + oID, set := obj.ID() + if !set { + return errOIDNotSet + } + + if !id.Equals(oID) { return errIncorrectID } @@ -98,7 +103,12 @@ func VerifyID(obj *Object) error { // CalculateAndSetSignature signs id with provided key and sets that signature to // the object. func CalculateAndSetSignature(key ecdsa.PrivateKey, obj *Object) error { - sig, err := obj.ID().CalculateIDSignature(key) + oID, set := obj.ID() + if !set { + return errOIDNotSet + } + + sig, err := oID.CalculateIDSignature(key) if err != nil { return err } @@ -110,8 +120,13 @@ func CalculateAndSetSignature(key ecdsa.PrivateKey, obj *Object) error { // VerifyIDSignature verifies object ID signature. func (o *Object) VerifyIDSignature() bool { + oID, set := o.ID() + if !set { + return false + } + var idV2 refs.ObjectID - o.ID().WriteToV2(&idV2) + oID.WriteToV2(&idV2) sig := o.Signature() diff --git a/object/id/id.go b/object/id/id.go index c0426c0..dc5990d 100644 --- a/object/id/id.go +++ b/object/id/id.go @@ -130,12 +130,6 @@ func (id ID) CalculateIDSignature(key ecdsa.PrivateKey) (signature.Signature, er return *sign, err } -// Empty returns true if it is called on -// zero object ID. -func (id ID) Empty() bool { - return id == ID{} -} - // Marshal marshals ID into a protobuf binary form. func (id ID) Marshal() ([]byte, error) { var v2 refs.ObjectID diff --git a/object/object.go b/object/object.go index c71239c..e203302 100644 --- a/object/object.go +++ b/object/object.go @@ -1,6 +1,9 @@ package object import ( + "errors" + "fmt" + "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-sdk-go/checksum" @@ -86,15 +89,14 @@ func (o *Object) setSplitFields(setter func(*object.SplitHeader)) { } // ID returns object identifier. -func (o *Object) ID() oid.ID { - var v oid.ID - +func (o *Object) ID() (v oid.ID, isSet bool) { v2 := (*object.Object)(o) if id := v2.GetObjectID(); id != nil { _ = v.ReadFromV2(*v2.GetObjectID()) + isSet = true } - return v + return } // SetID sets object identifier. @@ -161,16 +163,16 @@ func (o *Object) SetPayloadSize(v uint64) { } // ContainerID returns identifier of the related container. -func (o *Object) ContainerID() cid.ID { - var cID cid.ID +func (o *Object) ContainerID() (v cid.ID, isSet bool) { v2 := (*object.Object)(o) cidV2 := v2.GetHeader().GetContainerID() if cidV2 != nil { - _ = cID.ReadFromV2(*cidV2) + _ = v.ReadFromV2(*cidV2) + isSet = true } - return cID + return } // SetContainerID sets identifier of the related container. @@ -304,16 +306,16 @@ func (o *Object) SetAttributes(v ...Attribute) { } // PreviousID returns identifier of the previous sibling object. -func (o *Object) PreviousID() oid.ID { - var v oid.ID +func (o *Object) PreviousID() (v oid.ID, isSet bool) { v2 := (*object.Object)(o) v2Prev := v2.GetHeader().GetSplit().GetPrevious() if v2Prev != nil { _ = v.ReadFromV2(*v2Prev) + isSet = true } - return v + return } // SetPreviousID sets identifier of the previous sibling object. @@ -432,16 +434,16 @@ func (o *Object) SetSplitID(id *SplitID) { } // ParentID returns identifier of the parent object. -func (o *Object) ParentID() oid.ID { - var v oid.ID +func (o *Object) ParentID() (v oid.ID, isSet bool) { v2 := (*object.Object)(o) v2Par := v2.GetHeader().GetSplit().GetParent() if v2Par != nil { _ = v.ReadFromV2(*v2Par) + isSet = true } - return v + return } // SetParentID sets identifier of the parent object. @@ -564,7 +566,12 @@ func (o *Object) Marshal() ([]byte, error) { // Unmarshal unmarshals protobuf binary representation of object. func (o *Object) Unmarshal(data []byte) error { - return (*object.Object)(o).Unmarshal(data) + err := (*object.Object)(o).Unmarshal(data) + if err != nil { + return err + } + + return formatCheck((*object.Object)(o)) } // MarshalJSON encodes object to protobuf JSON format. @@ -574,5 +581,56 @@ func (o *Object) MarshalJSON() ([]byte, error) { // UnmarshalJSON decodes object from protobuf JSON format. func (o *Object) UnmarshalJSON(data []byte) error { - return (*object.Object)(o).UnmarshalJSON(data) + err := (*object.Object)(o).UnmarshalJSON(data) + if err != nil { + return err + } + + return formatCheck((*object.Object)(o)) +} + +var errOIDNotSet = errors.New("object ID is not set") +var errCIDNotSet = errors.New("container ID is not set") + +func formatCheck(v2 *object.Object) error { + var ( + oID oid.ID + cID cid.ID + ) + + oidV2 := v2.GetObjectID() + if oidV2 == nil { + return errOIDNotSet + } + + err := oID.ReadFromV2(*oidV2) + if err != nil { + return fmt.Errorf("could not convert V2 object ID: %w", err) + } + + cidV2 := v2.GetHeader().GetContainerID() + if cidV2 == nil { + return errCIDNotSet + } + + err = cID.ReadFromV2(*cidV2) + if err != nil { + return fmt.Errorf("could not convert V2 container ID: %w", err) + } + + if prev := v2.GetHeader().GetSplit().GetPrevious(); prev != nil { + err = oID.ReadFromV2(*prev) + if err != nil { + return fmt.Errorf("could not convert previous object ID: %w", err) + } + } + + if parent := v2.GetHeader().GetSplit().GetParent(); parent != nil { + err = oID.ReadFromV2(*parent) + if err != nil { + return fmt.Errorf("could not convert parent object ID: %w", err) + } + } + + return nil } diff --git a/object/object_test.go b/object/object_test.go index e489154..3e0dca9 100644 --- a/object/object_test.go +++ b/object/object_test.go @@ -19,6 +19,8 @@ func TestInitCreation(t *testing.T) { Owner: own, }) - require.Equal(t, cnr, o.ContainerID()) + cID, set := o.ContainerID() + require.True(t, set) + require.Equal(t, cnr, cID) require.Equal(t, &own, o.OwnerID()) } diff --git a/object/raw_test.go b/object/raw_test.go index b1675c4..0db51b3 100644 --- a/object/raw_test.go +++ b/object/raw_test.go @@ -44,7 +44,9 @@ func TestObject_SetID(t *testing.T) { obj.SetID(id) - require.Equal(t, id, obj.ID()) + oID, set := obj.ID() + require.True(t, set) + require.Equal(t, id, oID) } func TestObject_SetSignature(t *testing.T) { @@ -98,7 +100,9 @@ func TestObject_SetContainerID(t *testing.T) { obj.SetContainerID(cid) - require.Equal(t, cid, obj.ContainerID()) + cID, set := obj.ContainerID() + require.True(t, set) + require.Equal(t, cid, cID) } func TestObject_SetOwnerID(t *testing.T) { @@ -168,7 +172,10 @@ func TestObject_SetPreviousID(t *testing.T) { obj.SetPreviousID(prev) - require.Equal(t, prev, obj.PreviousID()) + oID, set := obj.PreviousID() + + require.True(t, set) + require.Equal(t, prev, oID) } func TestObject_SetChildren(t *testing.T) { @@ -270,7 +277,9 @@ func TestObject_SetParentID(t *testing.T) { id := randID(t) obj.SetParentID(id) - require.Equal(t, id, obj.ParentID()) + oID, set := obj.ParentID() + require.True(t, set) + require.Equal(t, id, oID) } func TestObject_ResetRelations(t *testing.T) { @@ -280,7 +289,8 @@ func TestObject_ResetRelations(t *testing.T) { obj.ResetRelations() - require.True(t, obj.PreviousID().Empty()) + _, set := obj.PreviousID() + require.False(t, set) } func TestObject_HasParent(t *testing.T) { @@ -298,6 +308,7 @@ func TestObject_HasParent(t *testing.T) { func TestObjectEncoding(t *testing.T) { o := New() o.SetID(randID(t)) + o.SetContainerID(cidtest.ID()) t.Run("binary", func(t *testing.T) { data, err := o.Marshal() diff --git a/object/splitinfo.go b/object/splitinfo.go index 2201e46..8e52a01 100644 --- a/object/splitinfo.go +++ b/object/splitinfo.go @@ -1,6 +1,9 @@ package object import ( + "errors" + "fmt" + "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-api-go/v2/refs" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" @@ -41,16 +44,16 @@ func (s *SplitInfo) SetSplitID(v *SplitID) { (*object.SplitInfo)(s).SetSplitID(v.ToV2()) } -func (s SplitInfo) LastPart() oid.ID { - var id oid.ID +func (s SplitInfo) LastPart() (v oid.ID, isSet bool) { v2 := (object.SplitInfo)(s) lpV2 := v2.GetLastPart() if lpV2 != nil { - _ = id.ReadFromV2(*lpV2) + _ = v.ReadFromV2(*lpV2) + isSet = true } - return id + return } func (s *SplitInfo) SetLastPart(v oid.ID) { @@ -60,16 +63,16 @@ func (s *SplitInfo) SetLastPart(v oid.ID) { (*object.SplitInfo)(s).SetLastPart(&idV2) } -func (s SplitInfo) Link() oid.ID { - var id oid.ID +func (s SplitInfo) Link() (v oid.ID, isSet bool) { v2 := (object.SplitInfo)(s) linkV2 := v2.GetLink() if linkV2 != nil { - _ = id.ReadFromV2(*linkV2) + _ = v.ReadFromV2(*linkV2) + isSet = true } - return id + return } func (s *SplitInfo) SetLink(v oid.ID) { @@ -84,7 +87,12 @@ func (s *SplitInfo) Marshal() ([]byte, error) { } func (s *SplitInfo) Unmarshal(data []byte) error { - return (*object.SplitInfo)(s).Unmarshal(data) + err := (*object.SplitInfo)(s).Unmarshal(data) + if err != nil { + return err + } + + return formatCheckSI((*object.SplitInfo)(s)) } // MarshalJSON implements json.Marshaler. @@ -94,5 +102,39 @@ func (s *SplitInfo) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements json.Unmarshaler. func (s *SplitInfo) UnmarshalJSON(data []byte) error { - return (*object.SplitInfo)(s).UnmarshalJSON(data) + err := (*object.SplitInfo)(s).UnmarshalJSON(data) + if err != nil { + return err + } + + return formatCheckSI((*object.SplitInfo)(s)) +} + +var errLinkNotSet = errors.New("link object ID is not set") +var errLastPartNotSet = errors.New("last part object ID is not set") + +func formatCheckSI(v2 *object.SplitInfo) error { + var oID oid.ID + + link := v2.GetLink() + if link == nil { + return errLinkNotSet + } + + err := oID.ReadFromV2(*link) + if err != nil { + return fmt.Errorf("could not convert link object ID: %w", err) + } + + lastPart := v2.GetLastPart() + if lastPart == nil { + return errLastPartNotSet + } + + err = oID.ReadFromV2(*lastPart) + if err != nil { + return fmt.Errorf("could not convert last part object ID: %w", err) + } + + return nil } diff --git a/object/splitinfo_test.go b/object/splitinfo_test.go index f89cb52..729f53c 100644 --- a/object/splitinfo_test.go +++ b/object/splitinfo_test.go @@ -21,10 +21,14 @@ func TestSplitInfo(t *testing.T) { require.Equal(t, splitID, s.SplitID()) s.SetLastPart(lastPart) - require.Equal(t, lastPart, s.LastPart()) + lp, set := s.LastPart() + require.True(t, set) + require.Equal(t, lastPart, lp) s.SetLink(link) - require.Equal(t, link, s.Link()) + l, set := s.Link() + require.True(t, set) + require.Equal(t, link, l) t.Run("to and from v2", func(t *testing.T) { v2 := s.ToV2() @@ -77,8 +81,10 @@ func TestNewSplitInfo(t *testing.T) { // check initial values require.Nil(t, si.SplitID()) - require.True(t, si.LastPart().Empty()) - require.True(t, si.Link().Empty()) + _, set := si.LastPart() + require.False(t, set) + _, set = si.Link() + require.False(t, set) // convert to v2 message siV2 := si.ToV2() diff --git a/pool/pool.go b/pool/pool.go index 54d9462..f8b864f 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -192,7 +192,12 @@ func (c *clientWrapper) containerSetEACL(ctx context.Context, prm PrmContainerSe prm.waitParams.setDefaults() } - return waitForEACLPresence(ctx, c, prm.table.CID(), &prm.table, &prm.waitParams) + var cIDp *cid.ID + if cID, set := prm.table.CID(); set { + cIDp = &cID + } + + return waitForEACLPresence(ctx, c, cIDp, &prm.table, &prm.waitParams) } func (c *clientWrapper) endpointInfo(ctx context.Context, _ prmEndpointInfo) (*netmap.NodeInfo, error) { @@ -288,12 +293,12 @@ func (c *clientWrapper) objectPut(ctx context.Context, prm PrmObjectPut) (*oid.I func (c *clientWrapper) objectDelete(ctx context.Context, prm PrmObjectDelete) error { var cliPrm sdkClient.PrmObjectDelete - if cnr := prm.addr.ContainerID(); cnr != nil { - cliPrm.FromContainer(*cnr) + if cnr, set := prm.addr.ContainerID(); set { + cliPrm.FromContainer(cnr) } - if obj := prm.addr.ObjectID(); obj != nil { - cliPrm.ByID(*obj) + if obj, set := prm.addr.ObjectID(); set { + cliPrm.ByID(obj) } if prm.stoken != nil { @@ -314,20 +319,20 @@ func (c *clientWrapper) objectDelete(ctx context.Context, prm PrmObjectDelete) e func (c *clientWrapper) objectGet(ctx context.Context, prm PrmObjectGet) (*ResGetObject, error) { var cliPrm sdkClient.PrmObjectGet - if cnr := prm.addr.ContainerID(); cnr != nil { - cliPrm.FromContainer(*cnr) + if cnr, set := prm.addr.ContainerID(); set { + cliPrm.FromContainer(cnr) } - if obj := prm.addr.ObjectID(); obj != nil { - cliPrm.ByID(*obj) + if obj, set := prm.addr.ObjectID(); set { + cliPrm.ByID(obj) } - if cnr := prm.addr.ContainerID(); cnr != nil { - cliPrm.FromContainer(*cnr) + if cnr, set := prm.addr.ContainerID(); set { + cliPrm.FromContainer(cnr) } - if obj := prm.addr.ObjectID(); obj != nil { - cliPrm.ByID(*obj) + if obj, set := prm.addr.ObjectID(); set { + cliPrm.ByID(obj) } if prm.stoken != nil { @@ -362,12 +367,12 @@ func (c *clientWrapper) objectGet(ctx context.Context, prm PrmObjectGet) (*ResGe func (c *clientWrapper) objectHead(ctx context.Context, prm PrmObjectHead) (*object.Object, error) { var cliPrm sdkClient.PrmObjectHead - if cnr := prm.addr.ContainerID(); cnr != nil { - cliPrm.FromContainer(*cnr) + if cnr, set := prm.addr.ContainerID(); set { + cliPrm.FromContainer(cnr) } - if obj := prm.addr.ObjectID(); obj != nil { - cliPrm.ByID(*obj) + if obj, set := prm.addr.ObjectID(); set { + cliPrm.ByID(obj) } if prm.stoken != nil { @@ -401,12 +406,12 @@ func (c *clientWrapper) objectRange(ctx context.Context, prm PrmObjectRange) (*R cliPrm.SetOffset(prm.off) cliPrm.SetLength(prm.ln) - if cnr := prm.addr.ContainerID(); cnr != nil { - cliPrm.FromContainer(*cnr) + if cnr, set := prm.addr.ContainerID(); set { + cliPrm.FromContainer(cnr) } - if obj := prm.addr.ObjectID(); obj != nil { - cliPrm.ByID(*obj) + if obj, set := prm.addr.ObjectID(); set { + cliPrm.ByID(obj) } if prm.stoken != nil { @@ -1354,10 +1359,15 @@ func (p *Pool) fillAppropriateKey(prm *prmCommon) { // PutObject writes an object through a remote server using NeoFS API protocol. func (p *Pool) PutObject(ctx context.Context, prm PrmObjectPut) (*oid.ID, error) { + var cIDp *cid.ID + if cID, set := prm.hdr.ContainerID(); set { + cIDp = &cID + } + var prmCtx prmContext prmCtx.useDefaultSession() prmCtx.useVerb(sessionv2.ObjectVerbPut) - prmCtx.useAddress(newAddressFromCnrID(prm.hdr.ContainerID())) + prmCtx.useAddress(newAddressFromCnrID(cIDp)) p.fillAppropriateKey(&prm.prmCommon) @@ -1786,7 +1796,9 @@ func sessionTokenForOwner(id *owner.ID, cliRes *resCreateSession, exp uint64) *s func newAddressFromCnrID(cnrID *cid.ID) *address.Address { addr := address.NewAddress() - addr.SetContainerID(cnrID) + if cnrID != nil { + addr.SetContainerID(*cnrID) + } return addr }