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  }