diff --git a/bzz/api.go b/bzz/api.go index 18eb533b8f..2aa706f6a4 100644 --- a/bzz/api.go +++ b/bzz/api.go @@ -122,6 +122,31 @@ func (self *Api) Put(content, contentType string) (string, error) { return fmt.Sprintf("%064x", key), nil } +func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) { + root := common.Hex2Bytes(rootHash) + trie, err := loadManifestTrie(self.dpa, root) + if err != nil { + return + } + + if contentHash != "" { + entry := &manifestTrieEntry{ + Path: path, + Hash: contentHash, + ContentType: contentType, + } + trie.addEntry(entry) + } else { + trie.deleteEntry(path) + } + + err = trie.recalcAndStore() + if err != nil { + return + } + return fmt.Sprintf("%064x", trie.hash), nil +} + // Download replicates the manifest path structure on the local filesystem // under localpath func (self *Api) Download(bzzpath, localpath string) (string, error) { @@ -205,7 +230,9 @@ func (self *Api) Upload(lpath string) (string, error) { dcnt++ } - trie := &manifestTrie{} + trie := &manifestTrie{ + dpa: self.dpa, + } for i, entry := range list { if errors[i] != nil { return "", errors[i] @@ -214,7 +241,7 @@ func (self *Api) Upload(lpath string) (string, error) { trie.addEntry(entry) } - err2 := trie.recalcAndStore(self.dpa) + err2 := trie.recalcAndStore() var hs string if err2 == nil { hs = fmt.Sprintf("%064x", trie.hash) @@ -290,7 +317,7 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta return } - entry, _ := trie.getEntry(self.dpa, path) + entry, _ := trie.getEntry(path) if entry != nil { key = common.Hex2Bytes(entry.Hash) status = entry.Status diff --git a/bzz/manifest_trie.go b/bzz/manifest_trie.go index 6b164337aa..cea527ca21 100644 --- a/bzz/manifest_trie.go +++ b/bzz/manifest_trie.go @@ -15,6 +15,7 @@ const ( ) type manifestTrie struct { + dpa *DPA entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry hash Key // if hash != nil, it is stored } @@ -31,7 +32,7 @@ type manifestTrieEntry struct { subtrie *manifestTrie } -func loadManifestTrie(dpa *DPA, hash Key) (trie *manifestTrie, err error) { +func loadManifestTrie(dpa *DPA, hash Key) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand dpaLogger.Debugf("Swarm: manifest lookup key: '%064x'.", hash) // retrieve manifest via DPA @@ -59,7 +60,9 @@ func loadManifestTrie(dpa *DPA, hash Key) (trie *manifestTrie, err error) { dpaLogger.Debugf("Swarm: Manifest %064x has %d entries.", hash, len(man.Entries)) - trie = &manifestTrie{} + trie = &manifestTrie{ + dpa: dpa, + } for _, entry := range man.Entries { trie.addEntry(entry) } @@ -87,6 +90,9 @@ func (self *manifestTrie) addEntry(entry *manifestTrieEntry) { } if (oldentry.ContentType == manifestType) && (cpl == len(oldentry.Path)) { + if self.loadSubTrie(oldentry) != nil { + return + } entry.Path = entry.Path[cpl:] oldentry.subtrie.addEntry(entry) oldentry.Hash = "" @@ -95,7 +101,9 @@ func (self *manifestTrie) addEntry(entry *manifestTrieEntry) { commonPrefix := entry.Path[:cpl] - subtrie := &manifestTrie{} + subtrie := &manifestTrie{ + dpa: self.dpa, + } entry.Path = entry.Path[cpl:] oldentry.Path = oldentry.Path[cpl:] subtrie.addEntry(entry) @@ -136,6 +144,9 @@ func (self *manifestTrie) deleteEntry(path string) { epl := len(entry.Path) if (entry.ContentType == manifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { + if self.loadSubTrie(entry) != nil { + return + } entry.subtrie.deleteEntry(path[epl:]) entry.Hash = "" // remove subtree if it has less than 2 elements @@ -149,7 +160,7 @@ func (self *manifestTrie) deleteEntry(path string) { } } -func (self *manifestTrie) recalcAndStore(dpa *DPA) error { +func (self *manifestTrie) recalcAndStore() error { if self.hash != nil { return nil } @@ -161,7 +172,7 @@ func (self *manifestTrie) recalcAndStore(dpa *DPA) error { for _, entry := range self.entries { if entry != nil { if entry.Hash == "" { // TODO: paralellize - err := entry.subtrie.recalcAndStore(dpa) + err := entry.subtrie.recalcAndStore() if err != nil { return err } @@ -178,13 +189,22 @@ func (self *manifestTrie) recalcAndStore(dpa *DPA) error { sr := io.NewSectionReader(bytes.NewReader(manifest), 0, int64(len(manifest))) wg := &sync.WaitGroup{} - key, err2 := dpa.Store(sr, wg) + key, err2 := self.dpa.Store(sr, wg) wg.Wait() self.hash = key return err2 } -func (self *manifestTrie) findPrefixOf(dpa *DPA, path string) (entry *manifestTrieEntry, pos int) { +func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry) (err error) { + if entry.subtrie == nil { + hash := common.Hex2Bytes(entry.Hash) + entry.subtrie, err = loadManifestTrie(self.dpa, hash) + entry.Hash = "" // might not match, should be recalculated + } + return +} + +func (self *manifestTrie) findPrefixOf(path string) (entry *manifestTrieEntry, pos int) { if len(path) == 0 { return self.entries[256], 0 } @@ -194,16 +214,10 @@ func (self *manifestTrie) findPrefixOf(dpa *DPA, path string) (entry *manifestTr epl := len(entry.Path) if (len(path) >= epl) && (path[:epl] == entry.Path) { if entry.ContentType == manifestType { - if entry.subtrie == nil { - hash := common.Hex2Bytes(entry.Hash) - var err error - entry.subtrie, err = loadManifestTrie(dpa, hash) - if err != nil { - return nil, 0 - } - entry.Hash = "" // might not match, should be recalculated + if self.loadSubTrie(entry) != nil { + return nil, 0 } - entry, pos = entry.subtrie.findPrefixOf(dpa, path[epl:]) + entry, pos = entry.subtrie.findPrefixOf(path[epl:]) if entry != nil { pos += epl } @@ -216,8 +230,8 @@ func (self *manifestTrie) findPrefixOf(dpa *DPA, path string) (entry *manifestTr return } -func (self *manifestTrie) getEntryNLS(dpa *DPA, path string) (entry *manifestTrieEntry, pos int) { - entry, pos = self.findPrefixOf(dpa, path) +func (self *manifestTrie) getEntryNLS(path string) (entry *manifestTrieEntry, pos int) { + entry, pos = self.findPrefixOf(path) if entry != nil { for (pos < len(path)) && (path[pos] == '/') { pos++ @@ -229,10 +243,10 @@ func (self *manifestTrie) getEntryNLS(dpa *DPA, path string) (entry *manifestTri return } -func (self *manifestTrie) getEntry(dpa *DPA, path string) (entry *manifestTrieEntry, pos int) { +func (self *manifestTrie) getEntry(path string) (entry *manifestTrieEntry, pos int) { var slash string for { - entry, pos = self.getEntryNLS(dpa, slash+path) + entry, pos = self.getEntryNLS(slash + path) if pos < len(slash) { dpaLogger.Debugf("Path '%s' on manifest not found.", path) return nil, 0