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