github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/api/manifest.go (about) 1 // Copyright 2016 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Spectrum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "net/http" 26 "strings" 27 "sync" 28 "time" 29 30 "github.com/SmartMeshFoundation/Spectrum/common" 31 "github.com/SmartMeshFoundation/Spectrum/log" 32 "github.com/SmartMeshFoundation/Spectrum/swarm/storage" 33 ) 34 35 const ( 36 ManifestType = "application/bzz-manifest+json" 37 ) 38 39 // Manifest represents a swarm manifest 40 type Manifest struct { 41 Entries []ManifestEntry `json:"entries,omitempty"` 42 } 43 44 // ManifestEntry represents an entry in a swarm manifest 45 type ManifestEntry struct { 46 Hash string `json:"hash,omitempty"` 47 Path string `json:"path,omitempty"` 48 ContentType string `json:"contentType,omitempty"` 49 Mode int64 `json:"mode,omitempty"` 50 Size int64 `json:"size,omitempty"` 51 ModTime time.Time `json:"mod_time,omitempty"` 52 Status int `json:"status,omitempty"` 53 } 54 55 // ManifestList represents the result of listing files in a manifest 56 type ManifestList struct { 57 CommonPrefixes []string `json:"common_prefixes,omitempty"` 58 Entries []*ManifestEntry `json:"entries,omitempty"` 59 } 60 61 // NewManifest creates and stores a new, empty manifest 62 func (a *Api) NewManifest() (storage.Key, error) { 63 var manifest Manifest 64 data, err := json.Marshal(&manifest) 65 if err != nil { 66 return nil, err 67 } 68 return a.Store(bytes.NewReader(data), int64(len(data)), &sync.WaitGroup{}) 69 } 70 71 // ManifestWriter is used to add and remove entries from an underlying manifest 72 type ManifestWriter struct { 73 api *Api 74 trie *manifestTrie 75 quitC chan bool 76 } 77 78 func (a *Api) NewManifestWriter(key storage.Key, quitC chan bool) (*ManifestWriter, error) { 79 trie, err := loadManifest(a.dpa, key, quitC) 80 if err != nil { 81 return nil, fmt.Errorf("error loading manifest %s: %s", key, err) 82 } 83 return &ManifestWriter{a, trie, quitC}, nil 84 } 85 86 // AddEntry stores the given data and adds the resulting key to the manifest 87 func (m *ManifestWriter) AddEntry(data io.Reader, e *ManifestEntry) (storage.Key, error) { 88 key, err := m.api.Store(data, e.Size, nil) 89 if err != nil { 90 return nil, err 91 } 92 entry := newManifestTrieEntry(e, nil) 93 entry.Hash = key.String() 94 m.trie.addEntry(entry, m.quitC) 95 return key, nil 96 } 97 98 // RemoveEntry removes the given path from the manifest 99 func (m *ManifestWriter) RemoveEntry(path string) error { 100 m.trie.deleteEntry(path, m.quitC) 101 return nil 102 } 103 104 // Store stores the manifest, returning the resulting storage key 105 func (m *ManifestWriter) Store() (storage.Key, error) { 106 return m.trie.hash, m.trie.recalcAndStore() 107 } 108 109 // ManifestWalker is used to recursively walk the entries in the manifest and 110 // all of its submanifests 111 type ManifestWalker struct { 112 api *Api 113 trie *manifestTrie 114 quitC chan bool 115 } 116 117 func (a *Api) NewManifestWalker(key storage.Key, quitC chan bool) (*ManifestWalker, error) { 118 trie, err := loadManifest(a.dpa, key, quitC) 119 if err != nil { 120 return nil, fmt.Errorf("error loading manifest %s: %s", key, err) 121 } 122 return &ManifestWalker{a, trie, quitC}, nil 123 } 124 125 // SkipManifest is used as a return value from WalkFn to indicate that the 126 // manifest should be skipped 127 var SkipManifest = errors.New("skip this manifest") 128 129 // WalkFn is the type of function called for each entry visited by a recursive 130 // manifest walk 131 type WalkFn func(entry *ManifestEntry) error 132 133 // Walk recursively walks the manifest calling walkFn for each entry in the 134 // manifest, including submanifests 135 func (m *ManifestWalker) Walk(walkFn WalkFn) error { 136 return m.walk(m.trie, "", walkFn) 137 } 138 139 func (m *ManifestWalker) walk(trie *manifestTrie, prefix string, walkFn WalkFn) error { 140 for _, entry := range trie.entries { 141 if entry == nil { 142 continue 143 } 144 entry.Path = prefix + entry.Path 145 err := walkFn(&entry.ManifestEntry) 146 if err != nil { 147 if entry.ContentType == ManifestType && err == SkipManifest { 148 continue 149 } 150 return err 151 } 152 if entry.ContentType != ManifestType { 153 continue 154 } 155 if err := trie.loadSubTrie(entry, nil); err != nil { 156 return err 157 } 158 if err := m.walk(entry.subtrie, entry.Path, walkFn); err != nil { 159 return err 160 } 161 } 162 return nil 163 } 164 165 type manifestTrie struct { 166 dpa *storage.DPA 167 entries [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry 168 hash storage.Key // if hash != nil, it is stored 169 } 170 171 func newManifestTrieEntry(entry *ManifestEntry, subtrie *manifestTrie) *manifestTrieEntry { 172 return &manifestTrieEntry{ 173 ManifestEntry: *entry, 174 subtrie: subtrie, 175 } 176 } 177 178 type manifestTrieEntry struct { 179 ManifestEntry 180 181 subtrie *manifestTrie 182 } 183 184 func loadManifest(dpa *storage.DPA, hash storage.Key, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand 185 186 log.Trace(fmt.Sprintf("manifest lookup key: '%v'.", hash.Log())) 187 // retrieve manifest via DPA 188 manifestReader := dpa.Retrieve(hash) 189 return readManifest(manifestReader, hash, dpa, quitC) 190 } 191 192 func readManifest(manifestReader storage.LazySectionReader, hash storage.Key, dpa *storage.DPA, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand 193 194 // TODO check size for oversized manifests 195 size, err := manifestReader.Size(quitC) 196 if err != nil { // size == 0 197 // can't determine size means we don't have the root chunk 198 err = fmt.Errorf("Manifest not Found") 199 return 200 } 201 manifestData := make([]byte, size) 202 read, err := manifestReader.Read(manifestData) 203 if int64(read) < size { 204 log.Trace(fmt.Sprintf("Manifest %v not found.", hash.Log())) 205 if err == nil { 206 err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) 207 } 208 return 209 } 210 211 log.Trace(fmt.Sprintf("Manifest %v retrieved", hash.Log())) 212 var man struct { 213 Entries []*manifestTrieEntry `json:"entries"` 214 } 215 err = json.Unmarshal(manifestData, &man) 216 if err != nil { 217 err = fmt.Errorf("Manifest %v is malformed: %v", hash.Log(), err) 218 log.Trace(fmt.Sprintf("%v", err)) 219 return 220 } 221 222 log.Trace(fmt.Sprintf("Manifest %v has %d entries.", hash.Log(), len(man.Entries))) 223 224 trie = &manifestTrie{ 225 dpa: dpa, 226 } 227 for _, entry := range man.Entries { 228 trie.addEntry(entry, quitC) 229 } 230 return 231 } 232 233 func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { 234 self.hash = nil // trie modified, hash needs to be re-calculated on demand 235 236 if len(entry.Path) == 0 { 237 self.entries[256] = entry 238 return 239 } 240 241 b := entry.Path[0] 242 oldentry := self.entries[b] 243 if (oldentry == nil) || (oldentry.Path == entry.Path && oldentry.ContentType != ManifestType) { 244 self.entries[b] = entry 245 return 246 } 247 248 cpl := 0 249 for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { 250 cpl++ 251 } 252 253 if (oldentry.ContentType == ManifestType) && (cpl == len(oldentry.Path)) { 254 if self.loadSubTrie(oldentry, quitC) != nil { 255 return 256 } 257 entry.Path = entry.Path[cpl:] 258 oldentry.subtrie.addEntry(entry, quitC) 259 oldentry.Hash = "" 260 return 261 } 262 263 commonPrefix := entry.Path[:cpl] 264 265 subtrie := &manifestTrie{ 266 dpa: self.dpa, 267 } 268 entry.Path = entry.Path[cpl:] 269 oldentry.Path = oldentry.Path[cpl:] 270 subtrie.addEntry(entry, quitC) 271 subtrie.addEntry(oldentry, quitC) 272 273 self.entries[b] = newManifestTrieEntry(&ManifestEntry{ 274 Path: commonPrefix, 275 ContentType: ManifestType, 276 }, subtrie) 277 } 278 279 func (self *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { 280 for _, e := range self.entries { 281 if e != nil { 282 cnt++ 283 entry = e 284 } 285 } 286 return 287 } 288 289 func (self *manifestTrie) deleteEntry(path string, quitC chan bool) { 290 self.hash = nil // trie modified, hash needs to be re-calculated on demand 291 292 if len(path) == 0 { 293 self.entries[256] = nil 294 return 295 } 296 297 b := path[0] 298 entry := self.entries[b] 299 if entry == nil { 300 return 301 } 302 if entry.Path == path { 303 self.entries[b] = nil 304 return 305 } 306 307 epl := len(entry.Path) 308 if (entry.ContentType == ManifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { 309 if self.loadSubTrie(entry, quitC) != nil { 310 return 311 } 312 entry.subtrie.deleteEntry(path[epl:], quitC) 313 entry.Hash = "" 314 // remove subtree if it has less than 2 elements 315 cnt, lastentry := entry.subtrie.getCountLast() 316 if cnt < 2 { 317 if lastentry != nil { 318 lastentry.Path = entry.Path + lastentry.Path 319 } 320 self.entries[b] = lastentry 321 } 322 } 323 } 324 325 func (self *manifestTrie) recalcAndStore() error { 326 if self.hash != nil { 327 return nil 328 } 329 330 var buffer bytes.Buffer 331 buffer.WriteString(`{"entries":[`) 332 333 list := &Manifest{} 334 for _, entry := range self.entries { 335 if entry != nil { 336 if entry.Hash == "" { // TODO: paralellize 337 err := entry.subtrie.recalcAndStore() 338 if err != nil { 339 return err 340 } 341 entry.Hash = entry.subtrie.hash.String() 342 } 343 list.Entries = append(list.Entries, entry.ManifestEntry) 344 } 345 346 } 347 348 manifest, err := json.Marshal(list) 349 if err != nil { 350 return err 351 } 352 353 sr := bytes.NewReader(manifest) 354 wg := &sync.WaitGroup{} 355 key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg, nil) 356 wg.Wait() 357 self.hash = key 358 return err2 359 } 360 361 func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { 362 if entry.subtrie == nil { 363 hash := common.Hex2Bytes(entry.Hash) 364 entry.subtrie, err = loadManifest(self.dpa, hash, quitC) 365 entry.Hash = "" // might not match, should be recalculated 366 } 367 return 368 } 369 370 func (self *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { 371 plen := len(prefix) 372 var start, stop int 373 if plen == 0 { 374 start = 0 375 stop = 256 376 } else { 377 start = int(prefix[0]) 378 stop = start 379 } 380 381 for i := start; i <= stop; i++ { 382 select { 383 case <-quitC: 384 return fmt.Errorf("aborted") 385 default: 386 } 387 entry := self.entries[i] 388 if entry != nil { 389 epl := len(entry.Path) 390 if entry.ContentType == ManifestType { 391 l := plen 392 if epl < l { 393 l = epl 394 } 395 if prefix[:l] == entry.Path[:l] { 396 err := self.loadSubTrie(entry, quitC) 397 if err != nil { 398 return err 399 } 400 err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) 401 if err != nil { 402 return err 403 } 404 } 405 } else { 406 if (epl >= plen) && (prefix == entry.Path[:plen]) { 407 cb(entry, rp+entry.Path[plen:]) 408 } 409 } 410 } 411 } 412 return nil 413 } 414 415 func (self *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { 416 return self.listWithPrefixInt(prefix, "", quitC, cb) 417 } 418 419 func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { 420 421 log.Trace(fmt.Sprintf("findPrefixOf(%s)", path)) 422 423 if len(path) == 0 { 424 return self.entries[256], 0 425 } 426 427 //see if first char is in manifest entries 428 b := path[0] 429 entry = self.entries[b] 430 if entry == nil { 431 return self.entries[256], 0 432 } 433 434 epl := len(entry.Path) 435 log.Trace(fmt.Sprintf("path = %v entry.Path = %v epl = %v", path, entry.Path, epl)) 436 if len(path) <= epl { 437 if entry.Path[:len(path)] == path { 438 if entry.ContentType == ManifestType { 439 err := self.loadSubTrie(entry, quitC) 440 if err == nil && entry.subtrie != nil { 441 subentries := entry.subtrie.entries 442 for i := 0; i < len(subentries); i++ { 443 sub := subentries[i] 444 if sub != nil && sub.Path == "" { 445 return sub, len(path) 446 } 447 } 448 } 449 entry.Status = http.StatusMultipleChoices 450 } 451 pos = len(path) 452 return 453 } 454 return nil, 0 455 } 456 if path[:epl] == entry.Path { 457 log.Trace(fmt.Sprintf("entry.ContentType = %v", entry.ContentType)) 458 //the subentry is a manifest, load subtrie 459 if entry.ContentType == ManifestType && (strings.Contains(entry.Path, path) || strings.Contains(path, entry.Path)) { 460 err := self.loadSubTrie(entry, quitC) 461 if err != nil { 462 return nil, 0 463 } 464 sub, pos := entry.subtrie.findPrefixOf(path[epl:], quitC) 465 if sub != nil { 466 entry = sub 467 pos += epl 468 return sub, pos 469 } else if path == entry.Path { 470 entry.Status = http.StatusMultipleChoices 471 } 472 473 } else { 474 //entry is not a manifest, return it 475 if path != entry.Path { 476 return nil, 0 477 } 478 pos = epl 479 } 480 } 481 return 482 } 483 484 // file system manifest always contains regularized paths 485 // no leading or trailing slashes, only single slashes inside 486 func RegularSlashes(path string) (res string) { 487 for i := 0; i < len(path); i++ { 488 if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { 489 res = res + path[i:i+1] 490 } 491 } 492 if (len(res) > 0) && (res[len(res)-1] == '/') { 493 res = res[:len(res)-1] 494 } 495 return 496 } 497 498 func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { 499 path := RegularSlashes(spath) 500 var pos int 501 quitC := make(chan bool) 502 entry, pos = self.findPrefixOf(path, quitC) 503 return entry, path[:pos] 504 }