github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/api/manifest.go (about)

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