github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/api/manifest.go (about)

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