| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | // Copyright 2016 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 09:15:17 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/swarm/storage/feed" | 
					
						
							| 
									
										
											  
											
												Swarm MRUs: Adaptive frequency / Predictable lookups / API simplification (#17559)
* swarm/storage/mru: Adaptive Frequency
swarm/storage/mru/lookup: fixed getBaseTime
Added NewEpoch constructor
swarm/api/client: better error handling in GetResource()
swarm/storage/mru: Renamed structures.
Renamed ResourceMetadata to ResourceID. 
Renamed ResourceID.Name to ResourceID.Topic
swarm/storage/mru: Added binarySerializer interface and test tools
swarm/storage/mru/lookup: Changed base time to time and + marshallers
swarm/storage/mru:  Added ResourceID (former resourceMetadata)
swarm/storage/mru: Added ResourceViewId and serialization tests
swarm/storage/mru/lookup: fixed epoch unmarshaller. Added Epoch Equals
swarm/storage/mru: Fixes as per review comments
cmd/swarm: reworded resource create/update help text regarding topic
swarm/storage/mru: Added UpdateLookup and serializer tests
swarm/storage/mru: Added UpdateHeader, serializers and tests
swarm/storage/mru: changed UpdateAddr / epoch to Base()
swarm/storage/mru: Added resourceUpdate serializer and tests
swarm/storage/mru: Added SignedResourceUpdate tests and serializers
swarm/storage/mru/lookup: fixed GetFirstEpoch bug
swarm/storage/mru: refactor, comments, cleanup
Also added tests for Topic
swarm/storage/mru: handler tests pass
swarm/storage/mru: all resource package tests pass
swarm/storage/mru: resource test pass after adding
timestamp checking support
swarm/storage/mru: Added JSON serializers to ResourceIDView structures
swarm/storage/mru: Sever, client, API test pass
swarm/storage/mru: server test pass
swarm/storage/mru: Added topic length check
swarm/storage/mru: removed some literals,
improved "previous lookup" test case
swarm/storage/mru: some fixes and comments as per review
swarm/storage/mru: first working version without metadata chunk
swarm/storage/mru: Various fixes as per review
swarm/storage/mru: client test pass
swarm/storage/mru: resource query strings and manifest-less queries
swarm/storage/mru: simplify naming
swarm/storage/mru: first autofreq working version
swarm/storage/mru: renamed ToValues to AppendValues
swarm/resource/mru: Added ToValues / FromValues for URL query strings
swarm/storage/mru: Changed POST resource to work with query strings.
No more JSON.
swarm/storage/mru: removed resourceid
swarm/storage/mru: Opened up structures
swarm/storage/mru: Merged Request and SignedResourceUpdate
swarm/storage/mru: removed initial data from CLI resource create
swarm/storage/mru: Refactor Topic as a direct fixed-length array
swarm/storage/mru/lookup: Comprehensive GetNextLevel tests
swarm/storage/mru: Added comments
Added length checks in Topic
swarm/storage/mru: fixes in tests and some code comments
swarm/storage/mru/lookup: new optimized lookup algorithm
swarm/api: moved getResourceView to api out of server
swarm/storage/mru: Lookup algorithm working
swarm/storage/mru: comments and renamed NewLookupParams
Deleted commented code
swarm/storage/mru/lookup: renamed Epoch.LaterThan to After
swarm/storage/mru/lookup: Comments and tidying naming
swarm/storage/mru: fix lookup algorithm
swarm/storage/mru: exposed lookup hint
removed updateheader
swarm/storage/mru/lookup: changed GetNextEpoch for initial values
swarm/storage/mru: resource tests pass
swarm/storage/mru: valueSerializer interface and tests
swarm/storage/mru/lookup: Comments, improvements, fixes, more tests
swarm/storage/mru: renamed UpdateLookup to ID, LookupParams to Query
swarm/storage/mru: renamed query receiver var
swarm/cmd: MRU CLI tests
* cmd/swarm: remove rogue fmt
* swarm/storage/mru: Add version / header for future use
* swarm/storage/mru: Fixes/comments as per review
cmd/swarm: remove rogue fmt
swarm/storage/mru: Add version / header for future use-
* swarm/storage/mru: fix linter errors
* cmd/swarm: Speeded up TestCLIResourceUpdate
											
										 
											2018-09-28 12:07:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/swarm/log" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/swarm/storage" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2018-09-30 09:43:10 +02:00
										 |  |  | 	ManifestType    = "application/bzz-manifest+json" | 
					
						
							|  |  |  | 	FeedContentType = "application/bzz-feed" | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	manifestSizeLimit = 5 * 1024 * 1024 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // Manifest represents a swarm manifest | 
					
						
							|  |  |  | type Manifest struct { | 
					
						
							|  |  |  | 	Entries []ManifestEntry `json:"entries,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ManifestEntry represents an entry in a swarm manifest | 
					
						
							|  |  |  | type ManifestEntry struct { | 
					
						
							| 
									
										
										
										
											2018-09-30 09:43:10 +02:00
										 |  |  | 	Hash        string       `json:"hash,omitempty"` | 
					
						
							|  |  |  | 	Path        string       `json:"path,omitempty"` | 
					
						
							|  |  |  | 	ContentType string       `json:"contentType,omitempty"` | 
					
						
							|  |  |  | 	Mode        int64        `json:"mode,omitempty"` | 
					
						
							|  |  |  | 	Size        int64        `json:"size,omitempty"` | 
					
						
							|  |  |  | 	ModTime     time.Time    `json:"mod_time,omitempty"` | 
					
						
							|  |  |  | 	Status      int          `json:"status,omitempty"` | 
					
						
							|  |  |  | 	Access      *AccessEntry `json:"access,omitempty"` | 
					
						
							| 
									
										
										
										
											2018-10-03 09:15:17 +02:00
										 |  |  | 	Feed        *feed.Feed   `json:"feed,omitempty"` | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ManifestList represents the result of listing files in a manifest | 
					
						
							|  |  |  | type ManifestList struct { | 
					
						
							|  |  |  | 	CommonPrefixes []string         `json:"common_prefixes,omitempty"` | 
					
						
							|  |  |  | 	Entries        []*ManifestEntry `json:"entries,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewManifest creates and stores a new, empty manifest | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | func (a *API) NewManifest(ctx context.Context, toEncrypt bool) (storage.Address, error) { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	var manifest Manifest | 
					
						
							|  |  |  | 	data, err := json.Marshal(&manifest) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	addr, wait, err := a.Store(ctx, bytes.NewReader(data), int64(len(data)), toEncrypt) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = wait(ctx) | 
					
						
							|  |  |  | 	return addr, err | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-02 09:32:46 +02:00
										 |  |  | // Manifest hack for supporting Swarm feeds from the bzz: scheme | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // see swarm/api/api.go:API.Get() for more information | 
					
						
							| 
									
										
										
										
											2018-10-03 09:15:17 +02:00
										 |  |  | func (a *API) NewFeedManifest(ctx context.Context, feed *feed.Feed) (storage.Address, error) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	var manifest Manifest | 
					
						
							|  |  |  | 	entry := ManifestEntry{ | 
					
						
							| 
									
										
										
										
											2018-09-30 09:43:10 +02:00
										 |  |  | 		Feed:        feed, | 
					
						
							|  |  |  | 		ContentType: FeedContentType, | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	manifest.Entries = append(manifest.Entries, entry) | 
					
						
							|  |  |  | 	data, err := json.Marshal(&manifest) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	addr, wait, err := a.Store(ctx, bytes.NewReader(data), int64(len(data)), false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = wait(ctx) | 
					
						
							|  |  |  | 	return addr, err | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ManifestWriter is used to add and remove entries from an underlying manifest | 
					
						
							|  |  |  | type ManifestWriter struct { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	api   *API | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	trie  *manifestTrie | 
					
						
							|  |  |  | 	quitC chan bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | func (a *API) NewManifestWriter(ctx context.Context, addr storage.Address, quitC chan bool) (*ManifestWriter, error) { | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 	trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return nil, fmt.Errorf("error loading manifest %s: %s", addr, err) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return &ManifestWriter{a, trie, quitC}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | // AddEntry stores the given data and adds the resulting address to the manifest | 
					
						
							|  |  |  | func (m *ManifestWriter) AddEntry(ctx context.Context, data io.Reader, e *ManifestEntry) (addr storage.Address, err error) { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	entry := newManifestTrieEntry(e, nil) | 
					
						
							| 
									
										
										
										
											2018-08-10 16:12:55 +02:00
										 |  |  | 	if data != nil { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		var wait func(context.Context) error | 
					
						
							|  |  |  | 		addr, wait, err = m.api.Store(ctx, data, e.Size, m.trie.encrypted) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err = wait(ctx) | 
					
						
							| 
									
										
										
										
											2018-08-10 16:12:55 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		entry.Hash = addr.Hex() | 
					
						
							| 
									
										
										
										
											2018-08-10 16:12:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if entry.Hash == "" { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		return addr, errors.New("missing entry hash") | 
					
						
							| 
									
										
										
										
											2018-08-10 16:12:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	m.trie.addEntry(entry, m.quitC) | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	return addr, nil | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveEntry removes the given path from the manifest | 
					
						
							|  |  |  | func (m *ManifestWriter) RemoveEntry(path string) error { | 
					
						
							|  |  |  | 	m.trie.deleteEntry(path, m.quitC) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | // Store stores the manifest, returning the resulting storage address | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (m *ManifestWriter) Store() (storage.Address, error) { | 
					
						
							|  |  |  | 	return m.trie.ref, m.trie.recalcAndStore() | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ManifestWalker is used to recursively walk the entries in the manifest and | 
					
						
							|  |  |  | // all of its submanifests | 
					
						
							|  |  |  | type ManifestWalker struct { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	api   *API | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	trie  *manifestTrie | 
					
						
							|  |  |  | 	quitC chan bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | func (a *API) NewManifestWalker(ctx context.Context, addr storage.Address, decrypt DecryptFunc, quitC chan bool) (*ManifestWalker, error) { | 
					
						
							|  |  |  | 	trie, err := loadManifest(ctx, a.fileStore, addr, quitC, decrypt) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return nil, fmt.Errorf("error loading manifest %s: %s", addr, err) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return &ManifestWalker{a, trie, quitC}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // ErrSkipManifest is used as a return value from WalkFn to indicate that the | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | // manifest should be skipped | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | var ErrSkipManifest = errors.New("skip this manifest") | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // WalkFn is the type of function called for each entry visited by a recursive | 
					
						
							|  |  |  | // manifest walk | 
					
						
							|  |  |  | type WalkFn func(entry *ManifestEntry) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Walk recursively walks the manifest calling walkFn for each entry in the | 
					
						
							|  |  |  | // manifest, including submanifests | 
					
						
							|  |  |  | func (m *ManifestWalker) Walk(walkFn WalkFn) error { | 
					
						
							|  |  |  | 	return m.walk(m.trie, "", walkFn) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *ManifestWalker) walk(trie *manifestTrie, prefix string, walkFn WalkFn) error { | 
					
						
							| 
									
										
										
										
											2018-08-07 12:56:40 +02:00
										 |  |  | 	for _, entry := range &trie.entries { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		if entry == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		entry.Path = prefix + entry.Path | 
					
						
							|  |  |  | 		err := walkFn(&entry.ManifestEntry) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			if entry.ContentType == ManifestType && err == ErrSkipManifest { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if entry.ContentType != ManifestType { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := trie.loadSubTrie(entry, nil); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := m.walk(entry.subtrie, entry.Path, walkFn); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | type manifestTrie struct { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	fileStore *storage.FileStore | 
					
						
							|  |  |  | 	entries   [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry | 
					
						
							|  |  |  | 	ref       storage.Address         // if ref != nil, it is stored | 
					
						
							|  |  |  | 	encrypted bool | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 	decrypt   DecryptFunc | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | func newManifestTrieEntry(entry *ManifestEntry, subtrie *manifestTrie) *manifestTrieEntry { | 
					
						
							|  |  |  | 	return &manifestTrieEntry{ | 
					
						
							|  |  |  | 		ManifestEntry: *entry, | 
					
						
							|  |  |  | 		subtrie:       subtrie, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type manifestTrieEntry struct { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	ManifestEntry | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	subtrie *manifestTrie | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 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 | 
					
						
							|  |  |  | 	log.Trace("manifest lookup", "addr", addr) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	// retrieve manifest via FileStore | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	manifestReader, isEncrypted := fileStore.Retrieve(ctx, addr) | 
					
						
							|  |  |  | 	log.Trace("reader retrieved", "addr", addr) | 
					
						
							|  |  |  | 	return readManifest(manifestReader, addr, fileStore, isEncrypted, quitC, decrypt) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// TODO check size for oversized manifests | 
					
						
							| 
									
										
										
										
											2018-07-13 17:40:28 +02:00
										 |  |  | 	size, err := mr.Size(mr.Context(), quitC) | 
					
						
							| 
									
										
										
										
											2016-12-22 00:32:08 +01:00
										 |  |  | 	if err != nil { // size == 0 | 
					
						
							|  |  |  | 		// can't determine size means we don't have the root chunk | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		log.Trace("manifest not found", "addr", addr) | 
					
						
							| 
									
										
										
										
											2016-12-22 00:32:08 +01:00
										 |  |  | 		err = fmt.Errorf("Manifest not Found") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	if size > manifestSizeLimit { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		log.Warn("manifest exceeds size limit", "addr", addr, "size", size, "limit", manifestSizeLimit) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		err = fmt.Errorf("Manifest size of %v bytes exceeds the %v byte limit", size, manifestSizeLimit) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	manifestData := make([]byte, size) | 
					
						
							| 
									
										
										
										
											2018-07-13 17:40:28 +02:00
										 |  |  | 	read, err := mr.Read(manifestData) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if int64(read) < size { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		log.Trace("manifest not found", "addr", addr) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	log.Debug("manifest retrieved", "addr", addr) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	var man struct { | 
					
						
							|  |  |  | 		Entries []*manifestTrieEntry `json:"entries"` | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	err = json.Unmarshal(manifestData, &man) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		err = fmt.Errorf("Manifest %v is malformed: %v", addr.Log(), err) | 
					
						
							|  |  |  | 		log.Trace("malformed manifest", "addr", addr) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	log.Trace("manifest entries", "addr", addr, "len", len(man.Entries)) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	trie = &manifestTrie{ | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		fileStore: fileStore, | 
					
						
							|  |  |  | 		encrypted: isEncrypted, | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		decrypt:   decrypt, | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for _, entry := range man.Entries { | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		err = trie.addEntry(entry, quitC) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) error { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	mt.ref = nil // trie modified, hash needs to be re-calculated on demand | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 	if entry.ManifestEntry.Access != nil { | 
					
						
							|  |  |  | 		if mt.decrypt == nil { | 
					
						
							|  |  |  | 			return errors.New("dont have decryptor") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err := mt.decrypt(&entry.ManifestEntry) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if len(entry.Path) == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		mt.entries[256] = entry | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:06:45 +01:00
										 |  |  | 	b := entry.Path[0] | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	oldentry := mt.entries[b] | 
					
						
							| 
									
										
										
										
											2017-05-21 23:57:03 -07:00
										 |  |  | 	if (oldentry == nil) || (oldentry.Path == entry.Path && oldentry.ContentType != ManifestType) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		mt.entries[b] = entry | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpl := 0 | 
					
						
							|  |  |  | 	for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { | 
					
						
							|  |  |  | 		cpl++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	if (oldentry.ContentType == ManifestType) && (cpl == len(oldentry.Path)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		if mt.loadSubTrie(oldentry, quitC) != nil { | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		entry.Path = entry.Path[cpl:] | 
					
						
							|  |  |  | 		oldentry.subtrie.addEntry(entry, quitC) | 
					
						
							|  |  |  | 		oldentry.Hash = "" | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	commonPrefix := entry.Path[:cpl] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	subtrie := &manifestTrie{ | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		fileStore: mt.fileStore, | 
					
						
							|  |  |  | 		encrypted: mt.encrypted, | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	entry.Path = entry.Path[cpl:] | 
					
						
							|  |  |  | 	oldentry.Path = oldentry.Path[cpl:] | 
					
						
							|  |  |  | 	subtrie.addEntry(entry, quitC) | 
					
						
							|  |  |  | 	subtrie.addEntry(oldentry, quitC) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	mt.entries[b] = newManifestTrieEntry(&ManifestEntry{ | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		Path:        commonPrefix, | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		ContentType: ManifestType, | 
					
						
							|  |  |  | 	}, subtrie) | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { | 
					
						
							| 
									
										
										
										
											2018-08-07 12:56:40 +02:00
										 |  |  | 	for _, e := range &mt.entries { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		if e != nil { | 
					
						
							|  |  |  | 			cnt++ | 
					
						
							|  |  |  | 			entry = e | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) deleteEntry(path string, quitC chan bool) { | 
					
						
							|  |  |  | 	mt.ref = nil // trie modified, hash needs to be re-calculated on demand | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(path) == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		mt.entries[256] = nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:06:45 +01:00
										 |  |  | 	b := path[0] | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	entry := mt.entries[b] | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if entry == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if entry.Path == path { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		mt.entries[b] = nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	epl := len(entry.Path) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	if (entry.ContentType == ManifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		if mt.loadSubTrie(entry, quitC) != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		entry.subtrie.deleteEntry(path[epl:], quitC) | 
					
						
							|  |  |  | 		entry.Hash = "" | 
					
						
							|  |  |  | 		// remove subtree if it has less than 2 elements | 
					
						
							|  |  |  | 		cnt, lastentry := entry.subtrie.getCountLast() | 
					
						
							|  |  |  | 		if cnt < 2 { | 
					
						
							|  |  |  | 			if lastentry != nil { | 
					
						
							|  |  |  | 				lastentry.Path = entry.Path + lastentry.Path | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			mt.entries[b] = lastentry | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) recalcAndStore() error { | 
					
						
							|  |  |  | 	if mt.ref != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var buffer bytes.Buffer | 
					
						
							|  |  |  | 	buffer.WriteString(`{"entries":[`) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	list := &Manifest{} | 
					
						
							| 
									
										
										
										
											2018-08-07 12:56:40 +02:00
										 |  |  | 	for _, entry := range &mt.entries { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		if entry != nil { | 
					
						
							|  |  |  | 			if entry.Hash == "" { // TODO: paralellize | 
					
						
							|  |  |  | 				err := entry.subtrie.recalcAndStore() | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 				entry.Hash = entry.subtrie.ref.Hex() | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 			list.Entries = append(list.Entries, entry.ManifestEntry) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 05:36:02 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	manifest, err := json.Marshal(list) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sr := bytes.NewReader(manifest) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	ctx := context.TODO() | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	addr, wait, err2 := mt.fileStore.Store(ctx, sr, int64(len(manifest)), mt.encrypted) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	if err2 != nil { | 
					
						
							|  |  |  | 		return err2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err2 = wait(ctx) | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	mt.ref = addr | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	return err2 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 	if entry.ManifestEntry.Access != nil { | 
					
						
							|  |  |  | 		if mt.decrypt == nil { | 
					
						
							|  |  |  | 			return errors.New("dont have decryptor") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err := mt.decrypt(&entry.ManifestEntry) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if entry.subtrie == nil { | 
					
						
							|  |  |  | 		hash := common.Hex2Bytes(entry.Hash) | 
					
						
							| 
									
										
										
										
											2018-08-15 17:41:52 +02:00
										 |  |  | 		entry.subtrie, err = loadManifest(context.TODO(), mt.fileStore, hash, quitC, mt.decrypt) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		entry.Hash = "" // might not match, should be recalculated | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	plen := len(prefix) | 
					
						
							|  |  |  | 	var start, stop int | 
					
						
							|  |  |  | 	if plen == 0 { | 
					
						
							|  |  |  | 		start = 0 | 
					
						
							|  |  |  | 		stop = 256 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		start = int(prefix[0]) | 
					
						
							|  |  |  | 		stop = start | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := start; i <= stop; i++ { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-quitC: | 
					
						
							|  |  |  | 			return fmt.Errorf("aborted") | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		entry := mt.entries[i] | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		if entry != nil { | 
					
						
							|  |  |  | 			epl := len(entry.Path) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 			if entry.ContentType == ManifestType { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 				l := plen | 
					
						
							|  |  |  | 				if epl < l { | 
					
						
							|  |  |  | 					l = epl | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if prefix[:l] == entry.Path[:l] { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 					err := mt.loadSubTrie(entry, quitC) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				if (epl >= plen) && (prefix == entry.Path[:plen]) { | 
					
						
							|  |  |  | 					cb(entry, rp+entry.Path[plen:]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { | 
					
						
							|  |  |  | 	return mt.listWithPrefixInt(prefix, "", quitC, cb) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	log.Trace(fmt.Sprintf("findPrefixOf(%s)", path)) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(path) == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return mt.entries[256], 0 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 	//see if first char is in manifest entries | 
					
						
							| 
									
										
										
										
											2017-11-10 18:06:45 +01:00
										 |  |  | 	b := path[0] | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	entry = mt.entries[b] | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if entry == nil { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return mt.entries[256], 0 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	epl := len(entry.Path) | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	log.Trace(fmt.Sprintf("path = %v  entry.Path = %v  epl = %v", path, entry.Path, epl)) | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 	if len(path) <= epl { | 
					
						
							|  |  |  | 		if entry.Path[:len(path)] == path { | 
					
						
							|  |  |  | 			if entry.ContentType == ManifestType { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 				err := mt.loadSubTrie(entry, quitC) | 
					
						
							| 
									
										
										
										
											2017-10-18 08:33:50 -05:00
										 |  |  | 				if err == nil && entry.subtrie != nil { | 
					
						
							|  |  |  | 					subentries := entry.subtrie.entries | 
					
						
							|  |  |  | 					for i := 0; i < len(subentries); i++ { | 
					
						
							|  |  |  | 						sub := subentries[i] | 
					
						
							|  |  |  | 						if sub != nil && sub.Path == "" { | 
					
						
							|  |  |  | 							return sub, len(path) | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 				entry.Status = http.StatusMultipleChoices | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			pos = len(path) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if path[:epl] == entry.Path { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 		log.Trace(fmt.Sprintf("entry.ContentType = %v", entry.ContentType)) | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 		//the subentry is a manifest, load subtrie | 
					
						
							|  |  |  | 		if entry.ContentType == ManifestType && (strings.Contains(entry.Path, path) || strings.Contains(path, entry.Path)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			err := mt.loadSubTrie(entry, quitC) | 
					
						
							| 
									
										
										
										
											2017-01-26 20:33:39 +01:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 				return nil, 0 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 			sub, pos := entry.subtrie.findPrefixOf(path[epl:], quitC) | 
					
						
							|  |  |  | 			if sub != nil { | 
					
						
							|  |  |  | 				entry = sub | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 				pos += epl | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 				return sub, pos | 
					
						
							|  |  |  | 			} else if path == entry.Path { | 
					
						
							|  |  |  | 				entry.Status = http.StatusMultipleChoices | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2017-10-06 08:45:54 -05:00
										 |  |  | 			//entry is not a manifest, return it | 
					
						
							|  |  |  | 			if path != entry.Path { | 
					
						
							|  |  |  | 				return nil, 0 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 			pos = epl | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	return nil, 0 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // file system manifest always contains regularized paths | 
					
						
							|  |  |  | // no leading or trailing slashes, only single slashes inside | 
					
						
							|  |  |  | func RegularSlashes(path string) (res string) { | 
					
						
							|  |  |  | 	for i := 0; i < len(path); i++ { | 
					
						
							|  |  |  | 		if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { | 
					
						
							|  |  |  | 			res = res + path[i:i+1] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (len(res) > 0) && (res[len(res)-1] == '/') { | 
					
						
							|  |  |  | 		res = res[:len(res)-1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (mt *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	path := RegularSlashes(spath) | 
					
						
							|  |  |  | 	var pos int | 
					
						
							|  |  |  | 	quitC := make(chan bool) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	entry, pos = mt.findPrefixOf(path, quitC) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	return entry, path[:pos] | 
					
						
							|  |  |  | } |