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