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