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