github.com/ethersphere/bee/v2@v2.2.0/pkg/manifest/mantaray.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 manifest 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 12 "github.com/ethersphere/bee/v2/pkg/file" 13 "github.com/ethersphere/bee/v2/pkg/manifest/mantaray" 14 "github.com/ethersphere/bee/v2/pkg/swarm" 15 ) 16 17 const ( 18 // ManifestMantarayContentType represents content type used for noting that 19 // specific file should be processed as mantaray manifest. 20 ManifestMantarayContentType = "application/bzz-manifest-mantaray+octet-stream" 21 ) 22 23 type mantarayManifest struct { 24 trie *mantaray.Node 25 26 ls file.LoadSaver 27 } 28 29 // NewMantarayManifest creates a new mantaray-based manifest. 30 func NewMantarayManifest( 31 ls file.LoadSaver, 32 encrypted bool, 33 ) (Interface, error) { 34 mm := &mantarayManifest{ 35 trie: mantaray.New(), 36 ls: ls, 37 } 38 // use empty obfuscation key if not encrypting 39 if !encrypted { 40 // NOTE: it will be copied to all trie nodes 41 mm.trie.SetObfuscationKey(mantaray.ZeroObfuscationKey) 42 } 43 return mm, nil 44 } 45 46 // NewMantarayManifestReference loads existing mantaray-based manifest. 47 func NewMantarayManifestReference( 48 reference swarm.Address, 49 ls file.LoadSaver, 50 ) (Interface, error) { 51 return &mantarayManifest{ 52 trie: mantaray.NewNodeRef(reference.Bytes()), 53 ls: ls, 54 }, nil 55 } 56 57 func (m *mantarayManifest) Root() *mantaray.Node { 58 return m.trie 59 } 60 61 func (m *mantarayManifest) Type() string { 62 return ManifestMantarayContentType 63 } 64 65 func (m *mantarayManifest) Add(ctx context.Context, path string, entry Entry) error { 66 p := []byte(path) 67 e := entry.Reference().Bytes() 68 69 return m.trie.Add(ctx, p, e, entry.Metadata(), m.ls) 70 } 71 72 func (m *mantarayManifest) Remove(ctx context.Context, path string) error { 73 p := []byte(path) 74 75 err := m.trie.Remove(ctx, p, m.ls) 76 if err != nil { 77 if errors.Is(err, mantaray.ErrNotFound) { 78 return ErrNotFound 79 } 80 return err 81 } 82 83 return nil 84 } 85 86 func (m *mantarayManifest) Lookup(ctx context.Context, path string) (Entry, error) { 87 p := []byte(path) 88 89 node, err := m.trie.LookupNode(ctx, p, m.ls) 90 if err != nil { 91 if errors.Is(err, mantaray.ErrNotFound) { 92 return nil, ErrNotFound 93 } 94 return nil, err 95 } 96 97 if !node.IsValueType() { 98 return nil, ErrNotFound 99 } 100 101 address := swarm.NewAddress(node.Entry()) 102 entry := NewEntry(address, node.Metadata()) 103 104 return entry, nil 105 } 106 107 func (m *mantarayManifest) HasPrefix(ctx context.Context, prefix string) (bool, error) { 108 p := []byte(prefix) 109 110 return m.trie.HasPrefix(ctx, p, m.ls) 111 } 112 113 func (m *mantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFunc) (swarm.Address, error) { 114 var ls mantaray.LoadSaver 115 if len(storeSizeFn) > 0 { 116 ls = &mantarayLoadSaver{ 117 ls: m.ls, 118 storeSizeFn: storeSizeFn, 119 } 120 } else { 121 ls = m.ls 122 } 123 124 err := m.trie.Save(ctx, ls) 125 if err != nil { 126 return swarm.ZeroAddress, fmt.Errorf("manifest save error: %w", err) 127 } 128 129 address := swarm.NewAddress(m.trie.Reference()) 130 131 return address, nil 132 } 133 134 func (m *mantarayManifest) IterateAddresses(ctx context.Context, fn swarm.AddressIterFunc) error { 135 reference := swarm.NewAddress(m.trie.Reference()) 136 137 if swarm.ZeroAddress.Equal(reference) { 138 return ErrMissingReference 139 } 140 141 emptyAddr := swarm.NewAddress([]byte{31: 0}) 142 walker := func(path []byte, node *mantaray.Node, err error) error { 143 if err != nil { 144 return err 145 } 146 147 if node != nil { 148 if node.Reference() != nil { 149 ref := swarm.NewAddress(node.Reference()) 150 151 err = fn(ref) 152 if err != nil { 153 return err 154 } 155 } 156 157 if node.IsValueType() && len(node.Entry()) > 0 { 158 entry := swarm.NewAddress(node.Entry()) 159 // The following comparison to the emptyAddr is 160 // a dirty hack which prevents the walker to 161 // fail when it encounters an empty address 162 // (e.g.: during the unpin traversal operation 163 // for manifest). This workaround should be 164 // removed after the manifest serialization bug 165 // is fixed. 166 if entry.Equal(emptyAddr) { 167 return nil 168 } 169 if err := fn(entry); err != nil { 170 return err 171 } 172 } 173 } 174 175 return nil 176 } 177 178 err := m.trie.WalkNode(ctx, []byte{}, m.ls, walker) 179 if err != nil { 180 return fmt.Errorf("manifest iterate addresses: %w", err) 181 } 182 183 return nil 184 } 185 186 type mantarayLoadSaver struct { 187 ls file.LoadSaver 188 storeSizeFn []StoreSizeFunc 189 } 190 191 func (ls *mantarayLoadSaver) Load(ctx context.Context, ref []byte) ([]byte, error) { 192 return ls.ls.Load(ctx, ref) 193 } 194 195 func (ls *mantarayLoadSaver) Save(ctx context.Context, data []byte) ([]byte, error) { 196 dataLen := int64(len(data)) 197 for i := range ls.storeSizeFn { 198 err := ls.storeSizeFn[i](dataLen) 199 if err != nil { 200 return nil, fmt.Errorf("manifest store size func: %w", err) 201 } 202 } 203 204 return ls.ls.Save(ctx, data) 205 }