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