github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/api/manifest.go (about)

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