github.com/ethersphere/bee/v2@v2.2.0/pkg/traversal/traversal.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package traversal provides abstraction and implementation
     6  // needed to traverse all chunks below a given root hash.
     7  // It tries to parse all manifests and collections in its
     8  // attempt to log all chunk addresses on the way.
     9  package traversal
    10  
    11  import (
    12  	"context"
    13  	"errors"
    14  	"fmt"
    15  
    16  	"github.com/ethersphere/bee/v2/pkg/file/joiner"
    17  	"github.com/ethersphere/bee/v2/pkg/file/loadsave"
    18  	"github.com/ethersphere/bee/v2/pkg/manifest"
    19  	"github.com/ethersphere/bee/v2/pkg/manifest/mantaray"
    20  	"github.com/ethersphere/bee/v2/pkg/soc"
    21  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    22  	"github.com/ethersphere/bee/v2/pkg/swarm"
    23  )
    24  
    25  // Traverser represents service which traverse through address dependent chunks.
    26  type Traverser interface {
    27  	// Traverse iterates through each address related to the supplied one, if possible.
    28  	Traverse(context.Context, swarm.Address, swarm.AddressIterFunc) error
    29  }
    30  
    31  // New constructs for a new Traverser.
    32  func New(getter storage.Getter, putter storage.Putter) Traverser {
    33  	return &service{getter: getter, putter: putter}
    34  }
    35  
    36  // service is implementation of Traverser using storage.Storer as its storage.
    37  type service struct {
    38  	getter storage.Getter
    39  	putter storage.Putter
    40  }
    41  
    42  // Traverse implements Traverser.Traverse method.
    43  func (s *service) Traverse(ctx context.Context, addr swarm.Address, iterFn swarm.AddressIterFunc) error {
    44  	processBytes := func(ref swarm.Address) error {
    45  		j, _, err := joiner.New(ctx, s.getter, s.putter, ref)
    46  		if err != nil {
    47  			return fmt.Errorf("traversal: joiner error on %q: %w", ref, err)
    48  		}
    49  		err = j.IterateChunkAddresses(iterFn)
    50  		if err != nil {
    51  			return fmt.Errorf("traversal: iterate chunk address error for %q: %w", ref, err)
    52  		}
    53  		return nil
    54  	}
    55  
    56  	// skip SOC check for encrypted references
    57  	if addr.IsValidLength() {
    58  		ch, err := s.getter.Get(ctx, addr)
    59  		if err != nil {
    60  			return fmt.Errorf("traversal: failed to get root chunk %s: %w", addr.String(), err)
    61  		}
    62  		if soc.Valid(ch) {
    63  			// if this is a SOC, the traversal will be just be the single chunk
    64  			return iterFn(addr)
    65  		}
    66  	}
    67  
    68  	ls := loadsave.NewReadonly(s.getter)
    69  	switch mf, err := manifest.NewDefaultManifestReference(addr, ls); {
    70  	case errors.Is(err, manifest.ErrInvalidManifestType):
    71  		break
    72  	case err != nil:
    73  		return fmt.Errorf("traversal: unable to create manifest reference for %q: %w", addr, err)
    74  	default:
    75  		err := mf.IterateAddresses(ctx, processBytes)
    76  		if errors.Is(err, mantaray.ErrTooShort) || errors.Is(err, mantaray.ErrInvalidVersionHash) {
    77  			// Based on the returned errors we conclude that it might
    78  			// not be a manifest, so we try non-manifest processing.
    79  			break
    80  		}
    81  		if err != nil {
    82  			return fmt.Errorf("traversal: unable to process bytes for %q: %w", addr, err)
    83  		}
    84  		return nil
    85  	}
    86  
    87  	// Non-manifest processing.
    88  	if err := processBytes(addr); err != nil {
    89  		return fmt.Errorf("traversal: unable to process bytes for %q: %w", addr, err)
    90  	}
    91  	return nil
    92  }