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 }