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