| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-01-26 20:33:39 +01:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 05:36:02 +05:30
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"mime" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/swarm/storage" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	hashMatcher      = regexp.MustCompile("^[0-9A-Fa-f]{64}") | 
					
						
							|  |  |  | 	slashes          = regexp.MustCompile("/+") | 
					
						
							|  |  |  | 	domainAndVersion = regexp.MustCompile("[@:;,]+") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Resolver interface { | 
					
						
							|  |  |  | 	Resolve(string) (common.Hash, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | Api implements webserver/file system related content storage and retrieval | 
					
						
							|  |  |  | on top of the dpa | 
					
						
							|  |  |  | it is the public interface of the dpa which is included in the ethereum stack | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | type Api struct { | 
					
						
							|  |  |  | 	dpa *storage.DPA | 
					
						
							|  |  |  | 	dns Resolver | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //the api constructor initialises | 
					
						
							|  |  |  | func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { | 
					
						
							|  |  |  | 	self = &Api{ | 
					
						
							|  |  |  | 		dpa: dpa, | 
					
						
							|  |  |  | 		dns: dns, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 05:36:02 +05:30
										 |  |  | // to be used only in TEST | 
					
						
							|  |  |  | func (self *Api) Upload(uploadDir, index string) (hash string, err error) { | 
					
						
							|  |  |  | 	fs := NewFileSystem(self) | 
					
						
							|  |  |  | 	hash, err = fs.Upload(uploadDir, index) | 
					
						
							|  |  |  | 	return hash, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | // DPA reader API | 
					
						
							|  |  |  | func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { | 
					
						
							|  |  |  | 	return self.dpa.Retrieve(key) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { | 
					
						
							|  |  |  | 	return self.dpa.Store(data, size, wg, nil) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ErrResolve error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DNS Resolver | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | func (self *Api) Resolve(uri *URI) (storage.Key, error) { | 
					
						
							|  |  |  | 	log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr)) | 
					
						
							| 
									
										
										
										
											2017-04-13 04:06:19 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 	// if the URI is immutable, check if the address is a hash | 
					
						
							|  |  |  | 	isHash := hashMatcher.MatchString(uri.Addr) | 
					
						
							|  |  |  | 	if uri.Immutable() { | 
					
						
							|  |  |  | 		if !isHash { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("immutable address not a content hash: %q", uri.Addr) | 
					
						
							| 
									
										
										
										
											2017-04-13 04:06:19 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 		return common.Hex2Bytes(uri.Addr), nil | 
					
						
							| 
									
										
										
										
											2017-04-13 04:06:19 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// if DNS is not configured, check if the address is a hash | 
					
						
							|  |  |  | 	if self.dns == nil { | 
					
						
							|  |  |  | 		if !isHash { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return common.Hex2Bytes(uri.Addr), nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// try and resolve the address | 
					
						
							|  |  |  | 	resolved, err := self.dns.Resolve(uri.Addr) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return resolved[:], nil | 
					
						
							|  |  |  | 	} else if !isHash { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-12 17:02:25 -07:00
										 |  |  | 	return common.Hex2Bytes(uri.Addr), nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Put provides singleton manifest creation on top of dpa store | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | func (self *Api) Put(content, contentType string) (storage.Key, error) { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	r := strings.NewReader(content) | 
					
						
							|  |  |  | 	wg := &sync.WaitGroup{} | 
					
						
							|  |  |  | 	key, err := self.dpa.Store(r, int64(len(content)), wg, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) | 
					
						
							|  |  |  | 	r = strings.NewReader(manifest) | 
					
						
							|  |  |  | 	key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	return key, nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get uses iterative manifest retrieval and prefix matching | 
					
						
							| 
									
										
										
										
											2017-04-12 05:36:02 +05:30
										 |  |  | // to resolve basePath to content using dpa retrieve | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | // it returns a section reader, mimeType, status and an error | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) { | 
					
						
							|  |  |  | 	trie, err := loadManifest(self.dpa, key, nil) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 		log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err)) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	log.Trace(fmt.Sprintf("getEntry(%s)", path)) | 
					
						
							| 
									
										
										
										
											2017-01-26 20:33:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	entry, _ := trie.getEntry(path) | 
					
						
							| 
									
										
										
										
											2017-01-26 20:33:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if entry != nil { | 
					
						
							|  |  |  | 		key = common.Hex2Bytes(entry.Hash) | 
					
						
							|  |  |  | 		status = entry.Status | 
					
						
							|  |  |  | 		mimeType = entry.ContentType | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 		log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType)) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		reader = self.dpa.Retrieve(key) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-01-26 20:33:39 +01:00
										 |  |  | 		status = http.StatusNotFound | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		err = fmt.Errorf("manifest entry for '%s' not found", path) | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 		log.Warn(fmt.Sprintf("%v", err)) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) { | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	quitC := make(chan bool) | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	trie, err := loadManifest(self.dpa, key, quitC) | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if contentHash != "" { | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		entry := newManifestTrieEntry(&ManifestEntry{ | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 			Path:        path, | 
					
						
							|  |  |  | 			ContentType: contentType, | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 		}, nil) | 
					
						
							|  |  |  | 		entry.Hash = contentHash | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 		trie.addEntry(entry, quitC) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		trie.deleteEntry(path, quitC) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	if err := trie.recalcAndStore(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:22:22 +01:00
										 |  |  | 	return trie.hash, nil | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-12 05:36:02 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uri, err := Parse("bzz:/" + mhash) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mkey, err := self.Resolve(uri) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// trim the root dir we added | 
					
						
							|  |  |  | 	if path[:1] == "/" { | 
					
						
							|  |  |  | 		path = path[1:] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entry := &ManifestEntry{ | 
					
						
							|  |  |  | 		Path:        filepath.Join(path, fname), | 
					
						
							|  |  |  | 		ContentType: mime.TypeByExtension(filepath.Ext(fname)), | 
					
						
							|  |  |  | 		Mode:        0700, | 
					
						
							|  |  |  | 		Size:        int64(len(content)), | 
					
						
							|  |  |  | 		ModTime:     time.Now(), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mw, err := self.NewManifestWriter(mkey, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fkey, err := mw.AddEntry(bytes.NewReader(content), entry) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newMkey, err := mw.Store() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fkey, newMkey.String(), nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uri, err := Parse("bzz:/" + mhash) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mkey, err := self.Resolve(uri) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// trim the root dir we added | 
					
						
							|  |  |  | 	if path[:1] == "/" { | 
					
						
							|  |  |  | 		path = path[1:] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mw, err := self.NewManifestWriter(mkey, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mw.RemoveEntry(filepath.Join(path, fname)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newMkey, err := mw.Store() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return newMkey.String(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (self *Api) AppendFile(mhash, path, fname string, existingSize int64, content []byte, oldKey storage.Key, offset int64, addSize int64, nameresolver bool) (storage.Key, string, error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buffSize := offset + addSize | 
					
						
							|  |  |  | 	if buffSize < existingSize { | 
					
						
							|  |  |  | 		buffSize = existingSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf := make([]byte, buffSize) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	oldReader := self.Retrieve(oldKey) | 
					
						
							|  |  |  | 	io.ReadAtLeast(oldReader, buf, int(offset)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newReader := bytes.NewReader(content) | 
					
						
							|  |  |  | 	io.ReadAtLeast(newReader, buf[offset:], int(addSize)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if buffSize < existingSize { | 
					
						
							|  |  |  | 		io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	combinedReader := bytes.NewReader(buf) | 
					
						
							|  |  |  | 	totalSize := int64(len(buf)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO(jmozah): to append using pyramid chunker when it is ready | 
					
						
							|  |  |  | 	//oldReader := self.Retrieve(oldKey) | 
					
						
							|  |  |  | 	//newReader := bytes.NewReader(content) | 
					
						
							|  |  |  | 	//combinedReader := io.MultiReader(oldReader, newReader) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uri, err := Parse("bzz:/" + mhash) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mkey, err := self.Resolve(uri) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// trim the root dir we added | 
					
						
							|  |  |  | 	if path[:1] == "/" { | 
					
						
							|  |  |  | 		path = path[1:] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mw, err := self.NewManifestWriter(mkey, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mw.RemoveEntry(filepath.Join(path, fname)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entry := &ManifestEntry{ | 
					
						
							|  |  |  | 		Path:        filepath.Join(path, fname), | 
					
						
							|  |  |  | 		ContentType: mime.TypeByExtension(filepath.Ext(fname)), | 
					
						
							|  |  |  | 		Mode:        0700, | 
					
						
							|  |  |  | 		Size:        totalSize, | 
					
						
							|  |  |  | 		ModTime:     time.Now(), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fkey, err := mw.AddEntry(io.Reader(combinedReader), entry) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newMkey, err := mw.Store() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, "", err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fkey, newMkey.String(), nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uri, err := Parse("bzz:/" + mhash) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	key, err = self.Resolve(uri) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	quitC := make(chan bool) | 
					
						
							|  |  |  | 	rootTrie, err := loadManifest(self.dpa, key, quitC) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	manifestEntryMap = map[string]*manifestTrieEntry{} | 
					
						
							|  |  |  | 	err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) { | 
					
						
							|  |  |  | 		manifestEntryMap[suffix] = entry | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return key, manifestEntryMap, nil | 
					
						
							|  |  |  | } |