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