github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/api/manifest.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "net/http" 27 "strings" 28 "time" 29 30 "github.com/FusionFoundation/efsn/common" 31 "github.com/FusionFoundation/efsn/swarm/log" 32 "github.com/FusionFoundation/efsn/swarm/storage" 33 ) 34 35 const ( 36 ManifestType = "application/bzz-manifest+json" 37 ResourceContentType = "application/bzz-resource" 38 39 manifestSizeLimit = 5 * 1024 * 1024 40 ) 41 42 // Manifest represents a swarm manifest 43 type Manifest struct { 44 Entries []ManifestEntry `json:"entries,omitempty"` 45 } 46 47 // ManifestEntry represents an entry in a swarm manifest 48 type ManifestEntry struct { 49 Hash string `json:"hash,omitempty"` 50 Path string `json:"path,omitempty"` 51 ContentType string `json:"contentType,omitempty"` 52 Mode int64 `json:"mode,omitempty"` 53 Size int64 `json:"size,omitempty"` 54 ModTime time.Time `json:"mod_time,omitempty"` 55 Status int `json:"status,omitempty"` 56 Access *AccessEntry `json:"access,omitempty"` 57 } 58 59 // ManifestList represents the result of listing files in a manifest 60 type ManifestList struct { 61 CommonPrefixes []string `json:"common_prefixes,omitempty"` 62 Entries []*ManifestEntry `json:"entries,omitempty"` 63 } 64 65 // NewManifest creates and stores a new, empty manifest 66 func (a *API) NewManifest(ctx context.Context, toEncrypt bool) (storage.Address, error) { 67 var manifest Manifest 68 data, err := json.Marshal(&manifest) 69 if err != nil { 70 return nil, err 71 } 72 addr, wait, err := a.Store(ctx, bytes.NewReader(data), int64(len(data)), toEncrypt) 73 if err != nil { 74 return nil, err 75 } 76 err = wait(ctx) 77 return addr, err 78 } 79 80 // Manifest hack for supporting Mutable Resource Updates from the bzz: scheme 81 // see swarm/api/api.go:API.Get() for more information 82 func (a *API) NewResourceManifest(ctx context.Context, resourceAddr string) (storage.Address, error) { 83 var manifest Manifest 84 entry := ManifestEntry{ 85 Hash: resourceAddr, 86 ContentType: ResourceContentType, 87 } 88 manifest.Entries = append(manifest.Entries, entry) 89 data, err := json.Marshal(&manifest) 90 if err != nil { 91 return nil, err 92 } 93 addr, wait, err := a.Store(ctx, bytes.NewReader(data), int64(len(data)), false) 94 if err != nil { 95 return nil, err 96 } 97 err = wait(ctx) 98 return addr, err 99 } 100 101 // ManifestWriter is used to add and remove entries from an underlying manifest 102 type ManifestWriter struct { 103 api *API 104 trie *manifestTrie 105 quitC chan bool 106 } 107 108 func (a *API) NewManifestWriter(ctx context.Context, addr storage.Address, quitC chan bool) (*ManifestWriter, error) { 109 trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt) 110 if err != nil { 111 return nil, fmt.Errorf("error loading manifest %s: %s", addr, err) 112 } 113 return &ManifestWriter{a, trie, quitC}, nil 114 } 115 116 // AddEntry stores the given data and adds the resulting address to the manifest 117 func (m *ManifestWriter) AddEntry(ctx context.Context, data io.Reader, e *ManifestEntry) (addr storage.Address, err error) { 118 entry := newManifestTrieEntry(e, nil) 119 if data != nil { 120 var wait func(context.Context) error 121 addr, wait, err = m.api.Store(ctx, data, e.Size, m.trie.encrypted) 122 if err != nil { 123 return nil, err 124 } 125 err = wait(ctx) 126 if err != nil { 127 return nil, err 128 } 129 entry.Hash = addr.Hex() 130 } 131 if entry.Hash == "" { 132 return addr, errors.New("missing entry hash") 133 } 134 m.trie.addEntry(entry, m.quitC) 135 return addr, nil 136 } 137 138 // RemoveEntry removes the given path from the manifest 139 func (m *ManifestWriter) RemoveEntry(path string) error { 140 m.trie.deleteEntry(path, m.quitC) 141 return nil 142 } 143 144 // Store stores the manifest, returning the resulting storage address 145 func (m *ManifestWriter) Store() (storage.Address, error) { 146 return m.trie.ref, m.trie.recalcAndStore() 147 } 148 149 // ManifestWalker is used to recursively walk the entries in the manifest and 150 // all of its submanifests 151 type ManifestWalker struct { 152 api *API 153 trie *manifestTrie 154 quitC chan bool 155 } 156 157 func (a *API) NewManifestWalker(ctx context.Context, addr storage.Address, decrypt DecryptFunc, quitC chan bool) (*ManifestWalker, error) { 158 trie, err := loadManifest(ctx, a.fileStore, addr, quitC, decrypt) 159 if err != nil { 160 return nil, fmt.Errorf("error loading manifest %s: %s", addr, err) 161 } 162 return &ManifestWalker{a, trie, quitC}, nil 163 } 164 165 // ErrSkipManifest is used as a return value from WalkFn to indicate that the 166 // manifest should be skipped 167 var ErrSkipManifest = errors.New("skip this manifest") 168 169 // WalkFn is the type of function called for each entry visited by a recursive 170 // manifest walk 171 type WalkFn func(entry *ManifestEntry) error 172 173 // Walk recursively walks the manifest calling walkFn for each entry in the 174 // manifest, including submanifests 175 func (m *ManifestWalker) Walk(walkFn WalkFn) error { 176 return m.walk(m.trie, "", walkFn) 177 } 178 179 func (m *ManifestWalker) walk(trie *manifestTrie, prefix string, walkFn WalkFn) error { 180 for _, entry := range &trie.entries { 181 if entry == nil { 182 continue 183 } 184 entry.Path = prefix + entry.Path 185 err := walkFn(&entry.ManifestEntry) 186 if err != nil { 187 if entry.ContentType == ManifestType && err == ErrSkipManifest { 188 continue 189 } 190 return err 191 } 192 if entry.ContentType != ManifestType { 193 continue 194 } 195 if err := trie.loadSubTrie(entry, nil); err != nil { 196 return err 197 } 198 if err := m.walk(entry.subtrie, entry.Path, walkFn); err != nil { 199 return err 200 } 201 } 202 return nil 203 } 204 205 type manifestTrie struct { 206 fileStore *storage.FileStore 207 entries [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry 208 ref storage.Address // if ref != nil, it is stored 209 encrypted bool 210 decrypt DecryptFunc 211 } 212 213 func newManifestTrieEntry(entry *ManifestEntry, subtrie *manifestTrie) *manifestTrieEntry { 214 return &manifestTrieEntry{ 215 ManifestEntry: *entry, 216 subtrie: subtrie, 217 } 218 } 219 220 type manifestTrieEntry struct { 221 ManifestEntry 222 223 subtrie *manifestTrie 224 } 225 226 func loadManifest(ctx context.Context, fileStore *storage.FileStore, addr storage.Address, quitC chan bool, decrypt DecryptFunc) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand 227 log.Trace("manifest lookup", "addr", addr) 228 // retrieve manifest via FileStore 229 manifestReader, isEncrypted := fileStore.Retrieve(ctx, addr) 230 log.Trace("reader retrieved", "addr", addr) 231 return readManifest(manifestReader, addr, fileStore, isEncrypted, quitC, decrypt) 232 } 233 234 func readManifest(mr storage.LazySectionReader, addr storage.Address, fileStore *storage.FileStore, isEncrypted bool, quitC chan bool, decrypt DecryptFunc) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand 235 236 // TODO check size for oversized manifests 237 size, err := mr.Size(mr.Context(), quitC) 238 if err != nil { // size == 0 239 // can't determine size means we don't have the root chunk 240 log.Trace("manifest not found", "addr", addr) 241 err = fmt.Errorf("Manifest not Found") 242 return 243 } 244 if size > manifestSizeLimit { 245 log.Warn("manifest exceeds size limit", "addr", addr, "size", size, "limit", manifestSizeLimit) 246 err = fmt.Errorf("Manifest size of %v bytes exceeds the %v byte limit", size, manifestSizeLimit) 247 return 248 } 249 manifestData := make([]byte, size) 250 read, err := mr.Read(manifestData) 251 if int64(read) < size { 252 log.Trace("manifest not found", "addr", addr) 253 if err == nil { 254 err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) 255 } 256 return 257 } 258 259 log.Debug("manifest retrieved", "addr", addr) 260 var man struct { 261 Entries []*manifestTrieEntry `json:"entries"` 262 } 263 err = json.Unmarshal(manifestData, &man) 264 if err != nil { 265 err = fmt.Errorf("Manifest %v is malformed: %v", addr.Log(), err) 266 log.Trace("malformed manifest", "addr", addr) 267 return 268 } 269 270 log.Trace("manifest entries", "addr", addr, "len", len(man.Entries)) 271 272 trie = &manifestTrie{ 273 fileStore: fileStore, 274 encrypted: isEncrypted, 275 decrypt: decrypt, 276 } 277 for _, entry := range man.Entries { 278 err = trie.addEntry(entry, quitC) 279 if err != nil { 280 return 281 } 282 } 283 return 284 } 285 286 func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) error { 287 mt.ref = nil // trie modified, hash needs to be re-calculated on demand 288 289 if entry.ManifestEntry.Access != nil { 290 if mt.decrypt == nil { 291 return errors.New("dont have decryptor") 292 } 293 294 err := mt.decrypt(&entry.ManifestEntry) 295 if err != nil { 296 return err 297 } 298 } 299 300 if len(entry.Path) == 0 { 301 mt.entries[256] = entry 302 return nil 303 } 304 305 b := entry.Path[0] 306 oldentry := mt.entries[b] 307 if (oldentry == nil) || (oldentry.Path == entry.Path && oldentry.ContentType != ManifestType) { 308 mt.entries[b] = entry 309 return nil 310 } 311 312 cpl := 0 313 for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { 314 cpl++ 315 } 316 317 if (oldentry.ContentType == ManifestType) && (cpl == len(oldentry.Path)) { 318 if mt.loadSubTrie(oldentry, quitC) != nil { 319 return nil 320 } 321 entry.Path = entry.Path[cpl:] 322 oldentry.subtrie.addEntry(entry, quitC) 323 oldentry.Hash = "" 324 return nil 325 } 326 327 commonPrefix := entry.Path[:cpl] 328 329 subtrie := &manifestTrie{ 330 fileStore: mt.fileStore, 331 encrypted: mt.encrypted, 332 } 333 entry.Path = entry.Path[cpl:] 334 oldentry.Path = oldentry.Path[cpl:] 335 subtrie.addEntry(entry, quitC) 336 subtrie.addEntry(oldentry, quitC) 337 338 mt.entries[b] = newManifestTrieEntry(&ManifestEntry{ 339 Path: commonPrefix, 340 ContentType: ManifestType, 341 }, subtrie) 342 return nil 343 } 344 345 func (mt *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { 346 for _, e := range &mt.entries { 347 if e != nil { 348 cnt++ 349 entry = e 350 } 351 } 352 return 353 } 354 355 func (mt *manifestTrie) deleteEntry(path string, quitC chan bool) { 356 mt.ref = nil // trie modified, hash needs to be re-calculated on demand 357 358 if len(path) == 0 { 359 mt.entries[256] = nil 360 return 361 } 362 363 b := path[0] 364 entry := mt.entries[b] 365 if entry == nil { 366 return 367 } 368 if entry.Path == path { 369 mt.entries[b] = nil 370 return 371 } 372 373 epl := len(entry.Path) 374 if (entry.ContentType == ManifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { 375 if mt.loadSubTrie(entry, quitC) != nil { 376 return 377 } 378 entry.subtrie.deleteEntry(path[epl:], quitC) 379 entry.Hash = "" 380 // remove subtree if it has less than 2 elements 381 cnt, lastentry := entry.subtrie.getCountLast() 382 if cnt < 2 { 383 if lastentry != nil { 384 lastentry.Path = entry.Path + lastentry.Path 385 } 386 mt.entries[b] = lastentry 387 } 388 } 389 } 390 391 func (mt *manifestTrie) recalcAndStore() error { 392 if mt.ref != nil { 393 return nil 394 } 395 396 var buffer bytes.Buffer 397 buffer.WriteString(`{"entries":[`) 398 399 list := &Manifest{} 400 for _, entry := range &mt.entries { 401 if entry != nil { 402 if entry.Hash == "" { // TODO: paralellize 403 err := entry.subtrie.recalcAndStore() 404 if err != nil { 405 return err 406 } 407 entry.Hash = entry.subtrie.ref.Hex() 408 } 409 list.Entries = append(list.Entries, entry.ManifestEntry) 410 } 411 412 } 413 414 manifest, err := json.Marshal(list) 415 if err != nil { 416 return err 417 } 418 419 sr := bytes.NewReader(manifest) 420 ctx := context.TODO() 421 addr, wait, err2 := mt.fileStore.Store(ctx, sr, int64(len(manifest)), mt.encrypted) 422 if err2 != nil { 423 return err2 424 } 425 err2 = wait(ctx) 426 mt.ref = addr 427 return err2 428 } 429 430 func (mt *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { 431 if entry.ManifestEntry.Access != nil { 432 if mt.decrypt == nil { 433 return errors.New("dont have decryptor") 434 } 435 436 err := mt.decrypt(&entry.ManifestEntry) 437 if err != nil { 438 return err 439 } 440 } 441 442 if entry.subtrie == nil { 443 hash := common.Hex2Bytes(entry.Hash) 444 entry.subtrie, err = loadManifest(context.TODO(), mt.fileStore, hash, quitC, mt.decrypt) 445 entry.Hash = "" // might not match, should be recalculated 446 } 447 return 448 } 449 450 func (mt *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { 451 plen := len(prefix) 452 var start, stop int 453 if plen == 0 { 454 start = 0 455 stop = 256 456 } else { 457 start = int(prefix[0]) 458 stop = start 459 } 460 461 for i := start; i <= stop; i++ { 462 select { 463 case <-quitC: 464 return fmt.Errorf("aborted") 465 default: 466 } 467 entry := mt.entries[i] 468 if entry != nil { 469 epl := len(entry.Path) 470 if entry.ContentType == ManifestType { 471 l := plen 472 if epl < l { 473 l = epl 474 } 475 if prefix[:l] == entry.Path[:l] { 476 err := mt.loadSubTrie(entry, quitC) 477 if err != nil { 478 return err 479 } 480 err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) 481 if err != nil { 482 return err 483 } 484 } 485 } else { 486 if (epl >= plen) && (prefix == entry.Path[:plen]) { 487 cb(entry, rp+entry.Path[plen:]) 488 } 489 } 490 } 491 } 492 return nil 493 } 494 495 func (mt *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { 496 return mt.listWithPrefixInt(prefix, "", quitC, cb) 497 } 498 499 func (mt *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { 500 log.Trace(fmt.Sprintf("findPrefixOf(%s)", path)) 501 502 if len(path) == 0 { 503 return mt.entries[256], 0 504 } 505 506 //see if first char is in manifest entries 507 b := path[0] 508 entry = mt.entries[b] 509 if entry == nil { 510 return mt.entries[256], 0 511 } 512 513 epl := len(entry.Path) 514 log.Trace(fmt.Sprintf("path = %v entry.Path = %v epl = %v", path, entry.Path, epl)) 515 if len(path) <= epl { 516 if entry.Path[:len(path)] == path { 517 if entry.ContentType == ManifestType { 518 err := mt.loadSubTrie(entry, quitC) 519 if err == nil && entry.subtrie != nil { 520 subentries := entry.subtrie.entries 521 for i := 0; i < len(subentries); i++ { 522 sub := subentries[i] 523 if sub != nil && sub.Path == "" { 524 return sub, len(path) 525 } 526 } 527 } 528 entry.Status = http.StatusMultipleChoices 529 } 530 pos = len(path) 531 return 532 } 533 return nil, 0 534 } 535 if path[:epl] == entry.Path { 536 log.Trace(fmt.Sprintf("entry.ContentType = %v", entry.ContentType)) 537 //the subentry is a manifest, load subtrie 538 if entry.ContentType == ManifestType && (strings.Contains(entry.Path, path) || strings.Contains(path, entry.Path)) { 539 err := mt.loadSubTrie(entry, quitC) 540 if err != nil { 541 return nil, 0 542 } 543 sub, pos := entry.subtrie.findPrefixOf(path[epl:], quitC) 544 if sub != nil { 545 entry = sub 546 pos += epl 547 return sub, pos 548 } else if path == entry.Path { 549 entry.Status = http.StatusMultipleChoices 550 } 551 552 } else { 553 //entry is not a manifest, return it 554 if path != entry.Path { 555 return nil, 0 556 } 557 pos = epl 558 } 559 } 560 return nil, 0 561 } 562 563 // file system manifest always contains regularized paths 564 // no leading or trailing slashes, only single slashes inside 565 func RegularSlashes(path string) (res string) { 566 for i := 0; i < len(path); i++ { 567 if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { 568 res = res + path[i:i+1] 569 } 570 } 571 if (len(res) > 0) && (res[len(res)-1] == '/') { 572 res = res[:len(res)-1] 573 } 574 return 575 } 576 577 func (mt *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { 578 path := RegularSlashes(spath) 579 var pos int 580 quitC := make(chan bool) 581 entry, pos = mt.findPrefixOf(path, quitC) 582 return entry, path[:pos] 583 }