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