diff --git a/pkg/local_object_storage/metabase/ec.go b/pkg/local_object_storage/metabase/ec.go index aa626d52cf..7da4cabffe 100644 --- a/pkg/local_object_storage/metabase/ec.go +++ b/pkg/local_object_storage/metabase/ec.go @@ -167,6 +167,10 @@ func (db *DB) resolveECPartInMetaBucket(crs *bbolt.Cursor, parent oid.ID, pi iec return oid.ID{}, object.NewSplitInfoError(sizeSplitInfo) } + if pi.Index == -1 { + return id, nil + } + if (sizeSplitInfo == nil || sizeSplitInfo.GetLastPart().IsZero()) && getObjAttribute(partCrs, id, object.FilterFirstSplitObject) != nil { if sizeSplitInfo == nil { sizeSplitInfo = new(object.SplitInfo) diff --git a/pkg/services/object/get/ec.go b/pkg/services/object/get/ec.go index 0b963e3270..63c1aed63e 100644 --- a/pkg/services/object/get/ec.go +++ b/pkg/services/object/get/ec.go @@ -21,6 +21,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" "github.com/nspcc-dev/neofs-sdk-go/container" + "github.com/nspcc-dev/neofs-sdk-go/container/acl" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" @@ -1653,46 +1654,72 @@ func checkECPartInfoRequest(xHdrs []string, cnr container.Container) (iec.PartIn } } - if ruleIdxStr == "" && partIdxStr == "" { + if ruleIdxStr == "" { + if partIdxStr != "" { + return res, fmt.Errorf("%s X-header must be set in a correct EC part GET request (%s X-header found: %s)", + iec.AttributeRuleIdx, iec.AttributePartIdx, partIdxStr) + } + res.RuleIndex = -1 return res, nil } - if (ruleIdxStr == "") != (partIdxStr == "") { - return res, fmt.Errorf("%s and %s X-headers must be set together", iec.AttributeRuleIdx, iec.AttributePartIdx) - } + var ( + bACL = cnr.BasicACL() + ecRules = cnr.PlacementPolicy().ECRules() + ) // TODO: state limits in https://github.com/nspcc-dev/neofs-api. Share consts for them. ruleIdx, err := strconv.ParseUint(ruleIdxStr, 10, 8) if err != nil { return res, fmt.Errorf("invalid %s X-header: %w", iec.AttributeRuleIdx, err) } + res.RuleIndex = int(ruleIdx) + err = checkECRuleIdx(bACL, ecRules, res.RuleIndex) + if err != nil { + return res, err + } + + if partIdxStr == "" { + res.Index = -1 + return res, nil + } partIdx, err := strconv.ParseUint(partIdxStr, 10, 8) if err != nil { return res, fmt.Errorf("invalid %s X-header: %w", iec.AttributePartIdx, err) } + res.Index = int(partIdx) - if cnr.BasicACL() != 0 { // Uninitialized in tests, safe to do anyway, invalid requests will fail. - var ecRules = cnr.PlacementPolicy().ECRules() + err = checkECPartIdx(bACL, ecRules, res.RuleIndex, res.Index) + if err != nil { + return res, err + } + return res, nil +} + +func checkECRuleIdx(bACL acl.Basic, ecRules []netmap.ECRule, ruleIdx int) error { + if bACL != 0 { // Uninitialized in tests, safe to do anyway, invalid requests will fail. if len(ecRules) == 0 { - return res, errors.New("EC part requested in container without EC policy") + return errors.New("EC part requested in container without EC policy") } - - if int(ruleIdx) >= len(ecRules) { - return res, fmt.Errorf("EC rule index overflows container policy: idx=%d,rules=%d", ruleIdx, len(ecRules)) + if ruleIdx >= len(ecRules) { + return fmt.Errorf("EC rule index overflows container policy: idx=%d,rules=%d", ruleIdx, len(ecRules)) } + } + + return nil +} - if total := ecRules[ruleIdx].DataPartNum() + ecRules[ruleIdx].ParityPartNum(); int(partIdx) >= int(total) { - return res, fmt.Errorf("EC part index overflows container policy: idx=%d,parts=%d", partIdx, total) +func checkECPartIdx(bACL acl.Basic, ecRules []netmap.ECRule, ruleIdx, partIdx int) error { + if bACL != 0 { // Uninitialized in tests, safe to do anyway, invalid requests will fail. + if total := ecRules[ruleIdx].DataPartNum() + ecRules[ruleIdx].ParityPartNum(); partIdx >= int(total) { + return fmt.Errorf("EC part index overflows container policy: idx=%d,parts=%d", partIdx, total) } } - res.RuleIndex = int(ruleIdx) - res.Index = int(partIdx) - - return res, nil + return nil } func checkECAttributesInReceivedObject(hdr object.Object, ruleIdx, partIdx string) error {