github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/swarm/api/api.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 //go:generate mimegen --types=./../../cmd/swarm/mimegen/mime.types --package=api --out=gen_mime.go 20 //go:generate gofmt -s -w gen_mime.go 21 22 import ( 23 "archive/tar" 24 "bytes" 25 "context" 26 "crypto/ecdsa" 27 "encoding/hex" 28 "errors" 29 "fmt" 30 "io" 31 "math/big" 32 "mime" 33 "net/http" 34 "path" 35 "path/filepath" 36 "strings" 37 "time" 38 39 "github.com/ethereum/go-ethereum/common" 40 "github.com/ethereum/go-ethereum/contracts/ens" 41 "github.com/ethereum/go-ethereum/core/types" 42 "github.com/ethereum/go-ethereum/metrics" 43 "github.com/ethereum/go-ethereum/swarm/log" 44 "github.com/ethereum/go-ethereum/swarm/spancontext" 45 "github.com/ethereum/go-ethereum/swarm/storage" 46 "github.com/ethereum/go-ethereum/swarm/storage/feed" 47 "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" 48 opentracing "github.com/opentracing/opentracing-go" 49 ) 50 51 var ( 52 apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil) 53 apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil) 54 apiPutCount = metrics.NewRegisteredCounter("api.put.count", nil) 55 apiPutFail = metrics.NewRegisteredCounter("api.put.fail", nil) 56 apiGetCount = metrics.NewRegisteredCounter("api.get.count", nil) 57 apiGetNotFound = metrics.NewRegisteredCounter("api.get.notfound", nil) 58 apiGetHTTP300 = metrics.NewRegisteredCounter("api.get.http.300", nil) 59 apiManifestUpdateCount = metrics.NewRegisteredCounter("api.manifestupdate.count", nil) 60 apiManifestUpdateFail = metrics.NewRegisteredCounter("api.manifestupdate.fail", nil) 61 apiManifestListCount = metrics.NewRegisteredCounter("api.manifestlist.count", nil) 62 apiManifestListFail = metrics.NewRegisteredCounter("api.manifestlist.fail", nil) 63 apiDeleteCount = metrics.NewRegisteredCounter("api.delete.count", nil) 64 apiDeleteFail = metrics.NewRegisteredCounter("api.delete.fail", nil) 65 apiGetTarCount = metrics.NewRegisteredCounter("api.gettar.count", nil) 66 apiGetTarFail = metrics.NewRegisteredCounter("api.gettar.fail", nil) 67 apiUploadTarCount = metrics.NewRegisteredCounter("api.uploadtar.count", nil) 68 apiUploadTarFail = metrics.NewRegisteredCounter("api.uploadtar.fail", nil) 69 apiModifyCount = metrics.NewRegisteredCounter("api.modify.count", nil) 70 apiModifyFail = metrics.NewRegisteredCounter("api.modify.fail", nil) 71 apiAddFileCount = metrics.NewRegisteredCounter("api.addfile.count", nil) 72 apiAddFileFail = metrics.NewRegisteredCounter("api.addfile.fail", nil) 73 apiRmFileCount = metrics.NewRegisteredCounter("api.removefile.count", nil) 74 apiRmFileFail = metrics.NewRegisteredCounter("api.removefile.fail", nil) 75 apiAppendFileCount = metrics.NewRegisteredCounter("api.appendfile.count", nil) 76 apiAppendFileFail = metrics.NewRegisteredCounter("api.appendfile.fail", nil) 77 apiGetInvalid = metrics.NewRegisteredCounter("api.get.invalid", nil) 78 ) 79 80 // Resolver interface resolve a domain name to a hash using ENS 81 type Resolver interface { 82 Resolve(string) (common.Hash, error) 83 } 84 85 // ResolveValidator is used to validate the contained Resolver 86 type ResolveValidator interface { 87 Resolver 88 Owner(node [32]byte) (common.Address, error) 89 HeaderByNumber(context.Context, *big.Int) (*types.Header, error) 90 } 91 92 // NoResolverError is returned by MultiResolver.Resolve if no resolver 93 // can be found for the address. 94 type NoResolverError struct { 95 TLD string 96 } 97 98 // NewNoResolverError creates a NoResolverError for the given top level domain 99 func NewNoResolverError(tld string) *NoResolverError { 100 return &NoResolverError{TLD: tld} 101 } 102 103 // Error NoResolverError implements error 104 func (e *NoResolverError) Error() string { 105 if e.TLD == "" { 106 return "no ENS resolver" 107 } 108 return fmt.Sprintf("no ENS endpoint configured to resolve .%s TLD names", e.TLD) 109 } 110 111 // MultiResolver is used to resolve URL addresses based on their TLDs. 112 // Each TLD can have multiple resolvers, and the resolution from the 113 // first one in the sequence will be returned. 114 type MultiResolver struct { 115 resolvers map[string][]ResolveValidator 116 nameHash func(string) common.Hash 117 } 118 119 // MultiResolverOption sets options for MultiResolver and is used as 120 // arguments for its constructor. 121 type MultiResolverOption func(*MultiResolver) 122 123 // MultiResolverOptionWithResolver adds a Resolver to a list of resolvers 124 // for a specific TLD. If TLD is an empty string, the resolver will be added 125 // to the list of default resolver, the ones that will be used for resolution 126 // of addresses which do not have their TLD resolver specified. 127 func MultiResolverOptionWithResolver(r ResolveValidator, tld string) MultiResolverOption { 128 return func(m *MultiResolver) { 129 m.resolvers[tld] = append(m.resolvers[tld], r) 130 } 131 } 132 133 // NewMultiResolver creates a new instance of MultiResolver. 134 func NewMultiResolver(opts ...MultiResolverOption) (m *MultiResolver) { 135 m = &MultiResolver{ 136 resolvers: make(map[string][]ResolveValidator), 137 nameHash: ens.EnsNode, 138 } 139 for _, o := range opts { 140 o(m) 141 } 142 return m 143 } 144 145 // Resolve resolves address by choosing a Resolver by TLD. 146 // If there are more default Resolvers, or for a specific TLD, 147 // the Hash from the first one which does not return error 148 // will be returned. 149 func (m *MultiResolver) Resolve(addr string) (h common.Hash, err error) { 150 rs, err := m.getResolveValidator(addr) 151 if err != nil { 152 return h, err 153 } 154 for _, r := range rs { 155 h, err = r.Resolve(addr) 156 if err == nil { 157 return 158 } 159 } 160 return 161 } 162 163 // getResolveValidator uses the hostname to retrieve the resolver associated with the top level domain 164 func (m *MultiResolver) getResolveValidator(name string) ([]ResolveValidator, error) { 165 rs := m.resolvers[""] 166 tld := path.Ext(name) 167 if tld != "" { 168 tld = tld[1:] 169 rstld, ok := m.resolvers[tld] 170 if ok { 171 return rstld, nil 172 } 173 } 174 if len(rs) == 0 { 175 return rs, NewNoResolverError(tld) 176 } 177 return rs, nil 178 } 179 180 /* 181 API implements webserver/file system related content storage and retrieval 182 on top of the FileStore 183 it is the public interface of the FileStore which is included in the ethereum stack 184 */ 185 type API struct { 186 feed *feed.Handler 187 fileStore *storage.FileStore 188 dns Resolver 189 Decryptor func(context.Context, string) DecryptFunc 190 } 191 192 // NewAPI the api constructor initialises a new API instance. 193 func NewAPI(fileStore *storage.FileStore, dns Resolver, feedHandler *feed.Handler, pk *ecdsa.PrivateKey) (self *API) { 194 self = &API{ 195 fileStore: fileStore, 196 dns: dns, 197 feed: feedHandler, 198 Decryptor: func(ctx context.Context, credentials string) DecryptFunc { 199 return self.doDecrypt(ctx, credentials, pk) 200 }, 201 } 202 return 203 } 204 205 // Retrieve FileStore reader API 206 func (a *API) Retrieve(ctx context.Context, addr storage.Address) (reader storage.LazySectionReader, isEncrypted bool) { 207 return a.fileStore.Retrieve(ctx, addr) 208 } 209 210 // Store wraps the Store API call of the embedded FileStore 211 func (a *API) Store(ctx context.Context, data io.Reader, size int64, toEncrypt bool) (addr storage.Address, wait func(ctx context.Context) error, err error) { 212 log.Debug("api.store", "size", size) 213 return a.fileStore.Store(ctx, data, size, toEncrypt) 214 } 215 216 // Resolve a name into a content-addressed hash 217 // where address could be an ENS name, or a content addressed hash 218 func (a *API) Resolve(ctx context.Context, address string) (storage.Address, error) { 219 // if DNS is not configured, return an error 220 if a.dns == nil { 221 if hashMatcher.MatchString(address) { 222 return common.Hex2Bytes(address), nil 223 } 224 apiResolveFail.Inc(1) 225 return nil, fmt.Errorf("no DNS to resolve name: %q", address) 226 } 227 // try and resolve the address 228 resolved, err := a.dns.Resolve(address) 229 if err != nil { 230 if hashMatcher.MatchString(address) { 231 return common.Hex2Bytes(address), nil 232 } 233 return nil, err 234 } 235 return resolved[:], nil 236 } 237 238 // Resolve resolves a URI to an Address using the MultiResolver. 239 func (a *API) ResolveURI(ctx context.Context, uri *URI, credentials string) (storage.Address, error) { 240 apiResolveCount.Inc(1) 241 log.Trace("resolving", "uri", uri.Addr) 242 243 var sp opentracing.Span 244 ctx, sp = spancontext.StartSpan( 245 ctx, 246 "api.resolve") 247 defer sp.Finish() 248 249 // if the URI is immutable, check if the address looks like a hash 250 if uri.Immutable() { 251 key := uri.Address() 252 if key == nil { 253 return nil, fmt.Errorf("immutable address not a content hash: %q", uri.Addr) 254 } 255 return key, nil 256 } 257 258 addr, err := a.Resolve(ctx, uri.Addr) 259 if err != nil { 260 return nil, err 261 } 262 263 if uri.Path == "" { 264 return addr, nil 265 } 266 walker, err := a.NewManifestWalker(ctx, addr, a.Decryptor(ctx, credentials), nil) 267 if err != nil { 268 return nil, err 269 } 270 var entry *ManifestEntry 271 walker.Walk(func(e *ManifestEntry) error { 272 // if the entry matches the path, set entry and stop 273 // the walk 274 if e.Path == uri.Path { 275 entry = e 276 // return an error to cancel the walk 277 return errors.New("found") 278 } 279 // ignore non-manifest files 280 if e.ContentType != ManifestType { 281 return nil 282 } 283 // if the manifest's path is a prefix of the 284 // requested path, recurse into it by returning 285 // nil and continuing the walk 286 if strings.HasPrefix(uri.Path, e.Path) { 287 return nil 288 } 289 return ErrSkipManifest 290 }) 291 if entry == nil { 292 return nil, errors.New("not found") 293 } 294 addr = storage.Address(common.Hex2Bytes(entry.Hash)) 295 return addr, nil 296 } 297 298 // Put provides singleton manifest creation on top of FileStore store 299 func (a *API) Put(ctx context.Context, content string, contentType string, toEncrypt bool) (k storage.Address, wait func(context.Context) error, err error) { 300 apiPutCount.Inc(1) 301 r := strings.NewReader(content) 302 key, waitContent, err := a.fileStore.Store(ctx, r, int64(len(content)), toEncrypt) 303 if err != nil { 304 apiPutFail.Inc(1) 305 return nil, nil, err 306 } 307 manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) 308 r = strings.NewReader(manifest) 309 key, waitManifest, err := a.fileStore.Store(ctx, r, int64(len(manifest)), toEncrypt) 310 if err != nil { 311 apiPutFail.Inc(1) 312 return nil, nil, err 313 } 314 return key, func(ctx context.Context) error { 315 err := waitContent(ctx) 316 if err != nil { 317 return err 318 } 319 return waitManifest(ctx) 320 }, nil 321 } 322 323 // Get uses iterative manifest retrieval and prefix matching 324 // to resolve basePath to content using FileStore retrieve 325 // it returns a section reader, mimeType, status, the key of the actual content and an error 326 func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage.Address, path string) (reader storage.LazySectionReader, mimeType string, status int, contentAddr storage.Address, err error) { 327 log.Debug("api.get", "key", manifestAddr, "path", path) 328 apiGetCount.Inc(1) 329 trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt) 330 if err != nil { 331 apiGetNotFound.Inc(1) 332 status = http.StatusNotFound 333 return nil, "", http.StatusNotFound, nil, err 334 } 335 336 log.Debug("trie getting entry", "key", manifestAddr, "path", path) 337 entry, _ := trie.getEntry(path) 338 339 if entry != nil { 340 log.Debug("trie got entry", "key", manifestAddr, "path", path, "entry.Hash", entry.Hash) 341 342 if entry.ContentType == ManifestType { 343 log.Debug("entry is manifest", "key", manifestAddr, "new key", entry.Hash) 344 adr, err := hex.DecodeString(entry.Hash) 345 if err != nil { 346 return nil, "", 0, nil, err 347 } 348 return a.Get(ctx, decrypt, adr, entry.Path) 349 } 350 351 // we need to do some extra work if this is a Swarm feed manifest 352 if entry.ContentType == FeedContentType { 353 if entry.Feed == nil { 354 return reader, mimeType, status, nil, fmt.Errorf("Cannot decode Feed in manifest") 355 } 356 _, err := a.feed.Lookup(ctx, feed.NewQueryLatest(entry.Feed, lookup.NoClue)) 357 if err != nil { 358 apiGetNotFound.Inc(1) 359 status = http.StatusNotFound 360 log.Debug(fmt.Sprintf("get feed update content error: %v", err)) 361 return reader, mimeType, status, nil, err 362 } 363 // get the data of the update 364 _, contentAddr, err := a.feed.GetContent(entry.Feed) 365 if err != nil { 366 apiGetNotFound.Inc(1) 367 status = http.StatusNotFound 368 log.Warn(fmt.Sprintf("get feed update content error: %v", err)) 369 return reader, mimeType, status, nil, err 370 } 371 372 // extract content hash 373 if len(contentAddr) != storage.AddressLength { 374 apiGetInvalid.Inc(1) 375 status = http.StatusUnprocessableEntity 376 errorMessage := fmt.Sprintf("invalid swarm hash in feed update. Expected %d bytes. Got %d", storage.AddressLength, len(contentAddr)) 377 log.Warn(errorMessage) 378 return reader, mimeType, status, nil, errors.New(errorMessage) 379 } 380 manifestAddr = storage.Address(contentAddr) 381 log.Trace("feed update contains swarm hash", "key", manifestAddr) 382 383 // get the manifest the swarm hash points to 384 trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, NOOPDecrypt) 385 if err != nil { 386 apiGetNotFound.Inc(1) 387 status = http.StatusNotFound 388 log.Warn(fmt.Sprintf("loadManifestTrie (feed update) error: %v", err)) 389 return reader, mimeType, status, nil, err 390 } 391 392 // finally, get the manifest entry 393 // it will always be the entry on path "" 394 entry, _ = trie.getEntry(path) 395 if entry == nil { 396 status = http.StatusNotFound 397 apiGetNotFound.Inc(1) 398 err = fmt.Errorf("manifest (feed update) entry for '%s' not found", path) 399 log.Trace("manifest (feed update) entry not found", "key", manifestAddr, "path", path) 400 return reader, mimeType, status, nil, err 401 } 402 } 403 404 // regardless of feed update manifests or normal manifests we will converge at this point 405 // get the key the manifest entry points to and serve it if it's unambiguous 406 contentAddr = common.Hex2Bytes(entry.Hash) 407 status = entry.Status 408 if status == http.StatusMultipleChoices { 409 apiGetHTTP300.Inc(1) 410 return nil, entry.ContentType, status, contentAddr, err 411 } 412 mimeType = entry.ContentType 413 log.Debug("content lookup key", "key", contentAddr, "mimetype", mimeType) 414 reader, _ = a.fileStore.Retrieve(ctx, contentAddr) 415 } else { 416 // no entry found 417 status = http.StatusNotFound 418 apiGetNotFound.Inc(1) 419 err = fmt.Errorf("Not found: could not find resource '%s'", path) 420 log.Trace("manifest entry not found", "key", contentAddr, "path", path) 421 } 422 return 423 } 424 425 func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Address, error) { 426 apiDeleteCount.Inc(1) 427 uri, err := Parse("bzz:/" + addr) 428 if err != nil { 429 apiDeleteFail.Inc(1) 430 return nil, err 431 } 432 key, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS) 433 434 if err != nil { 435 return nil, err 436 } 437 newKey, err := a.UpdateManifest(ctx, key, func(mw *ManifestWriter) error { 438 log.Debug(fmt.Sprintf("removing %s from manifest %s", path, key.Log())) 439 return mw.RemoveEntry(path) 440 }) 441 if err != nil { 442 apiDeleteFail.Inc(1) 443 return nil, err 444 } 445 446 return newKey, nil 447 } 448 449 // GetDirectoryTar fetches a requested directory as a tarstream 450 // it returns an io.Reader and an error. Do not forget to Close() the returned ReadCloser 451 func (a *API) GetDirectoryTar(ctx context.Context, decrypt DecryptFunc, uri *URI) (io.ReadCloser, error) { 452 apiGetTarCount.Inc(1) 453 addr, err := a.Resolve(ctx, uri.Addr) 454 if err != nil { 455 return nil, err 456 } 457 walker, err := a.NewManifestWalker(ctx, addr, decrypt, nil) 458 if err != nil { 459 apiGetTarFail.Inc(1) 460 return nil, err 461 } 462 463 piper, pipew := io.Pipe() 464 465 tw := tar.NewWriter(pipew) 466 467 go func() { 468 err := walker.Walk(func(entry *ManifestEntry) error { 469 // ignore manifests (walk will recurse into them) 470 if entry.ContentType == ManifestType { 471 return nil 472 } 473 474 // retrieve the entry's key and size 475 reader, _ := a.Retrieve(ctx, storage.Address(common.Hex2Bytes(entry.Hash))) 476 size, err := reader.Size(ctx, nil) 477 if err != nil { 478 return err 479 } 480 481 // write a tar header for the entry 482 hdr := &tar.Header{ 483 Name: entry.Path, 484 Mode: entry.Mode, 485 Size: size, 486 ModTime: entry.ModTime, 487 Xattrs: map[string]string{ 488 "user.swarm.content-type": entry.ContentType, 489 }, 490 } 491 492 if err := tw.WriteHeader(hdr); err != nil { 493 return err 494 } 495 496 // copy the file into the tar stream 497 n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size)) 498 if err != nil { 499 return err 500 } else if n != size { 501 return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n) 502 } 503 504 return nil 505 }) 506 // close tar writer before closing pipew 507 // to flush remaining data to pipew 508 // regardless of error value 509 tw.Close() 510 if err != nil { 511 apiGetTarFail.Inc(1) 512 pipew.CloseWithError(err) 513 } else { 514 pipew.Close() 515 } 516 }() 517 518 return piper, nil 519 } 520 521 // GetManifestList lists the manifest entries for the specified address and prefix 522 // and returns it as a ManifestList 523 func (a *API) GetManifestList(ctx context.Context, decryptor DecryptFunc, addr storage.Address, prefix string) (list ManifestList, err error) { 524 apiManifestListCount.Inc(1) 525 walker, err := a.NewManifestWalker(ctx, addr, decryptor, nil) 526 if err != nil { 527 apiManifestListFail.Inc(1) 528 return ManifestList{}, err 529 } 530 531 err = walker.Walk(func(entry *ManifestEntry) error { 532 // handle non-manifest files 533 if entry.ContentType != ManifestType { 534 // ignore the file if it doesn't have the specified prefix 535 if !strings.HasPrefix(entry.Path, prefix) { 536 return nil 537 } 538 539 // if the path after the prefix contains a slash, add a 540 // common prefix to the list, otherwise add the entry 541 suffix := strings.TrimPrefix(entry.Path, prefix) 542 if index := strings.Index(suffix, "/"); index > -1 { 543 list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1]) 544 return nil 545 } 546 if entry.Path == "" { 547 entry.Path = "/" 548 } 549 list.Entries = append(list.Entries, entry) 550 return nil 551 } 552 553 // if the manifest's path is a prefix of the specified prefix 554 // then just recurse into the manifest by returning nil and 555 // continuing the walk 556 if strings.HasPrefix(prefix, entry.Path) { 557 return nil 558 } 559 560 // if the manifest's path has the specified prefix, then if the 561 // path after the prefix contains a slash, add a common prefix 562 // to the list and skip the manifest, otherwise recurse into 563 // the manifest by returning nil and continuing the walk 564 if strings.HasPrefix(entry.Path, prefix) { 565 suffix := strings.TrimPrefix(entry.Path, prefix) 566 if index := strings.Index(suffix, "/"); index > -1 { 567 list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1]) 568 return ErrSkipManifest 569 } 570 return nil 571 } 572 573 // the manifest neither has the prefix or needs recursing in to 574 // so just skip it 575 return ErrSkipManifest 576 }) 577 578 if err != nil { 579 apiManifestListFail.Inc(1) 580 return ManifestList{}, err 581 } 582 583 return list, nil 584 } 585 586 func (a *API) UpdateManifest(ctx context.Context, addr storage.Address, update func(mw *ManifestWriter) error) (storage.Address, error) { 587 apiManifestUpdateCount.Inc(1) 588 mw, err := a.NewManifestWriter(ctx, addr, nil) 589 if err != nil { 590 apiManifestUpdateFail.Inc(1) 591 return nil, err 592 } 593 594 if err := update(mw); err != nil { 595 apiManifestUpdateFail.Inc(1) 596 return nil, err 597 } 598 599 addr, err = mw.Store() 600 if err != nil { 601 apiManifestUpdateFail.Inc(1) 602 return nil, err 603 } 604 log.Debug(fmt.Sprintf("generated manifest %s", addr)) 605 return addr, nil 606 } 607 608 // Modify loads manifest and checks the content hash before recalculating and storing the manifest. 609 func (a *API) Modify(ctx context.Context, addr storage.Address, path, contentHash, contentType string) (storage.Address, error) { 610 apiModifyCount.Inc(1) 611 quitC := make(chan bool) 612 trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt) 613 if err != nil { 614 apiModifyFail.Inc(1) 615 return nil, err 616 } 617 if contentHash != "" { 618 entry := newManifestTrieEntry(&ManifestEntry{ 619 Path: path, 620 ContentType: contentType, 621 }, nil) 622 entry.Hash = contentHash 623 trie.addEntry(entry, quitC) 624 } else { 625 trie.deleteEntry(path, quitC) 626 } 627 628 if err := trie.recalcAndStore(); err != nil { 629 apiModifyFail.Inc(1) 630 return nil, err 631 } 632 return trie.ref, nil 633 } 634 635 // AddFile creates a new manifest entry, adds it to swarm, then adds a file to swarm. 636 func (a *API) AddFile(ctx context.Context, mhash, path, fname string, content []byte, nameresolver bool) (storage.Address, string, error) { 637 apiAddFileCount.Inc(1) 638 639 uri, err := Parse("bzz:/" + mhash) 640 if err != nil { 641 apiAddFileFail.Inc(1) 642 return nil, "", err 643 } 644 mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS) 645 if err != nil { 646 apiAddFileFail.Inc(1) 647 return nil, "", err 648 } 649 650 // trim the root dir we added 651 if path[:1] == "/" { 652 path = path[1:] 653 } 654 655 entry := &ManifestEntry{ 656 Path: filepath.Join(path, fname), 657 ContentType: mime.TypeByExtension(filepath.Ext(fname)), 658 Mode: 0700, 659 Size: int64(len(content)), 660 ModTime: time.Now(), 661 } 662 663 mw, err := a.NewManifestWriter(ctx, mkey, nil) 664 if err != nil { 665 apiAddFileFail.Inc(1) 666 return nil, "", err 667 } 668 669 fkey, err := mw.AddEntry(ctx, bytes.NewReader(content), entry) 670 if err != nil { 671 apiAddFileFail.Inc(1) 672 return nil, "", err 673 } 674 675 newMkey, err := mw.Store() 676 if err != nil { 677 apiAddFileFail.Inc(1) 678 return nil, "", err 679 680 } 681 682 return fkey, newMkey.String(), nil 683 } 684 685 func (a *API) UploadTar(ctx context.Context, bodyReader io.ReadCloser, manifestPath, defaultPath string, mw *ManifestWriter) (storage.Address, error) { 686 apiUploadTarCount.Inc(1) 687 var contentKey storage.Address 688 tr := tar.NewReader(bodyReader) 689 defer bodyReader.Close() 690 var defaultPathFound bool 691 for { 692 hdr, err := tr.Next() 693 if err == io.EOF { 694 break 695 } else if err != nil { 696 apiUploadTarFail.Inc(1) 697 return nil, fmt.Errorf("error reading tar stream: %s", err) 698 } 699 700 // only store regular files 701 if !hdr.FileInfo().Mode().IsRegular() { 702 continue 703 } 704 705 // add the entry under the path from the request 706 manifestPath := path.Join(manifestPath, hdr.Name) 707 contentType := hdr.Xattrs["user.swarm.content-type"] 708 if contentType == "" { 709 contentType = mime.TypeByExtension(filepath.Ext(hdr.Name)) 710 } 711 //DetectContentType("") 712 entry := &ManifestEntry{ 713 Path: manifestPath, 714 ContentType: contentType, 715 Mode: hdr.Mode, 716 Size: hdr.Size, 717 ModTime: hdr.ModTime, 718 } 719 contentKey, err = mw.AddEntry(ctx, tr, entry) 720 if err != nil { 721 apiUploadTarFail.Inc(1) 722 return nil, fmt.Errorf("error adding manifest entry from tar stream: %s", err) 723 } 724 if hdr.Name == defaultPath { 725 contentType := hdr.Xattrs["user.swarm.content-type"] 726 if contentType == "" { 727 contentType = mime.TypeByExtension(filepath.Ext(hdr.Name)) 728 } 729 730 entry := &ManifestEntry{ 731 Hash: contentKey.Hex(), 732 Path: "", // default entry 733 ContentType: contentType, 734 Mode: hdr.Mode, 735 Size: hdr.Size, 736 ModTime: hdr.ModTime, 737 } 738 contentKey, err = mw.AddEntry(ctx, nil, entry) 739 if err != nil { 740 apiUploadTarFail.Inc(1) 741 return nil, fmt.Errorf("error adding default manifest entry from tar stream: %s", err) 742 } 743 defaultPathFound = true 744 } 745 } 746 if defaultPath != "" && !defaultPathFound { 747 return contentKey, fmt.Errorf("default path %q not found", defaultPath) 748 } 749 return contentKey, nil 750 } 751 752 // RemoveFile removes a file entry in a manifest. 753 func (a *API) RemoveFile(ctx context.Context, mhash string, path string, fname string, nameresolver bool) (string, error) { 754 apiRmFileCount.Inc(1) 755 756 uri, err := Parse("bzz:/" + mhash) 757 if err != nil { 758 apiRmFileFail.Inc(1) 759 return "", err 760 } 761 mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS) 762 if err != nil { 763 apiRmFileFail.Inc(1) 764 return "", err 765 } 766 767 // trim the root dir we added 768 if path[:1] == "/" { 769 path = path[1:] 770 } 771 772 mw, err := a.NewManifestWriter(ctx, mkey, nil) 773 if err != nil { 774 apiRmFileFail.Inc(1) 775 return "", err 776 } 777 778 err = mw.RemoveEntry(filepath.Join(path, fname)) 779 if err != nil { 780 apiRmFileFail.Inc(1) 781 return "", err 782 } 783 784 newMkey, err := mw.Store() 785 if err != nil { 786 apiRmFileFail.Inc(1) 787 return "", err 788 789 } 790 791 return newMkey.String(), nil 792 } 793 794 // AppendFile removes old manifest, appends file entry to new manifest and adds it to Swarm. 795 func (a *API) AppendFile(ctx context.Context, mhash, path, fname string, existingSize int64, content []byte, oldAddr storage.Address, offset int64, addSize int64, nameresolver bool) (storage.Address, string, error) { 796 apiAppendFileCount.Inc(1) 797 798 buffSize := offset + addSize 799 if buffSize < existingSize { 800 buffSize = existingSize 801 } 802 803 buf := make([]byte, buffSize) 804 805 oldReader, _ := a.Retrieve(ctx, oldAddr) 806 io.ReadAtLeast(oldReader, buf, int(offset)) 807 808 newReader := bytes.NewReader(content) 809 io.ReadAtLeast(newReader, buf[offset:], int(addSize)) 810 811 if buffSize < existingSize { 812 io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize)) 813 } 814 815 combinedReader := bytes.NewReader(buf) 816 totalSize := int64(len(buf)) 817 818 // TODO(jmozah): to append using pyramid chunker when it is ready 819 //oldReader := a.Retrieve(oldKey) 820 //newReader := bytes.NewReader(content) 821 //combinedReader := io.MultiReader(oldReader, newReader) 822 823 uri, err := Parse("bzz:/" + mhash) 824 if err != nil { 825 apiAppendFileFail.Inc(1) 826 return nil, "", err 827 } 828 mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS) 829 if err != nil { 830 apiAppendFileFail.Inc(1) 831 return nil, "", err 832 } 833 834 // trim the root dir we added 835 if path[:1] == "/" { 836 path = path[1:] 837 } 838 839 mw, err := a.NewManifestWriter(ctx, mkey, nil) 840 if err != nil { 841 apiAppendFileFail.Inc(1) 842 return nil, "", err 843 } 844 845 err = mw.RemoveEntry(filepath.Join(path, fname)) 846 if err != nil { 847 apiAppendFileFail.Inc(1) 848 return nil, "", err 849 } 850 851 entry := &ManifestEntry{ 852 Path: filepath.Join(path, fname), 853 ContentType: mime.TypeByExtension(filepath.Ext(fname)), 854 Mode: 0700, 855 Size: totalSize, 856 ModTime: time.Now(), 857 } 858 859 fkey, err := mw.AddEntry(ctx, io.Reader(combinedReader), entry) 860 if err != nil { 861 apiAppendFileFail.Inc(1) 862 return nil, "", err 863 } 864 865 newMkey, err := mw.Store() 866 if err != nil { 867 apiAppendFileFail.Inc(1) 868 return nil, "", err 869 870 } 871 872 return fkey, newMkey.String(), nil 873 } 874 875 // BuildDirectoryTree used by swarmfs_unix 876 func (a *API) BuildDirectoryTree(ctx context.Context, mhash string, nameresolver bool) (addr storage.Address, manifestEntryMap map[string]*manifestTrieEntry, err error) { 877 878 uri, err := Parse("bzz:/" + mhash) 879 if err != nil { 880 return nil, nil, err 881 } 882 addr, err = a.Resolve(ctx, uri.Addr) 883 if err != nil { 884 return nil, nil, err 885 } 886 887 quitC := make(chan bool) 888 rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt) 889 if err != nil { 890 return nil, nil, fmt.Errorf("can't load manifest %v: %v", addr.String(), err) 891 } 892 893 manifestEntryMap = map[string]*manifestTrieEntry{} 894 err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) { 895 manifestEntryMap[suffix] = entry 896 }) 897 898 if err != nil { 899 return nil, nil, fmt.Errorf("list with prefix failed %v: %v", addr.String(), err) 900 } 901 return addr, manifestEntryMap, nil 902 } 903 904 // FeedsLookup finds Swarm feeds updates at specific points in time, or the latest update 905 func (a *API) FeedsLookup(ctx context.Context, query *feed.Query) ([]byte, error) { 906 _, err := a.feed.Lookup(ctx, query) 907 if err != nil { 908 return nil, err 909 } 910 var data []byte 911 _, data, err = a.feed.GetContent(&query.Feed) 912 if err != nil { 913 return nil, err 914 } 915 return data, nil 916 } 917 918 // FeedsNewRequest creates a Request object to update a specific feed 919 func (a *API) FeedsNewRequest(ctx context.Context, feed *feed.Feed) (*feed.Request, error) { 920 return a.feed.NewRequest(ctx, feed) 921 } 922 923 // FeedsUpdate publishes a new update on the given feed 924 func (a *API) FeedsUpdate(ctx context.Context, request *feed.Request) (storage.Address, error) { 925 return a.feed.Update(ctx, request) 926 } 927 928 // ErrCannotLoadFeedManifest is returned when looking up a feeds manifest fails 929 var ErrCannotLoadFeedManifest = errors.New("Cannot load feed manifest") 930 931 // ErrNotAFeedManifest is returned when the address provided returned something other than a valid manifest 932 var ErrNotAFeedManifest = errors.New("Not a feed manifest") 933 934 // ResolveFeedManifest retrieves the Swarm feed manifest for the given address, and returns the referenced Feed. 935 func (a *API) ResolveFeedManifest(ctx context.Context, addr storage.Address) (*feed.Feed, error) { 936 trie, err := loadManifest(ctx, a.fileStore, addr, nil, NOOPDecrypt) 937 if err != nil { 938 return nil, ErrCannotLoadFeedManifest 939 } 940 941 entry, _ := trie.getEntry("") 942 if entry.ContentType != FeedContentType { 943 return nil, ErrNotAFeedManifest 944 } 945 946 return entry.Feed, nil 947 } 948 949 // ErrCannotResolveFeedURI is returned when the ENS resolver is not able to translate a name to a Swarm feed 950 var ErrCannotResolveFeedURI = errors.New("Cannot resolve Feed URI") 951 952 // ErrCannotResolveFeed is returned when values provided are not enough or invalid to recreate a 953 // feed out of them. 954 var ErrCannotResolveFeed = errors.New("Cannot resolve Feed") 955 956 // ResolveFeed attempts to extract feed information out of the manifest, if provided 957 // If not, it attempts to extract the feed out of a set of key-value pairs 958 func (a *API) ResolveFeed(ctx context.Context, uri *URI, values feed.Values) (*feed.Feed, error) { 959 var fd *feed.Feed 960 var err error 961 if uri.Addr != "" { 962 // resolve the content key. 963 manifestAddr := uri.Address() 964 if manifestAddr == nil { 965 manifestAddr, err = a.Resolve(ctx, uri.Addr) 966 if err != nil { 967 return nil, ErrCannotResolveFeedURI 968 } 969 } 970 971 // get the Swarm feed from the manifest 972 fd, err = a.ResolveFeedManifest(ctx, manifestAddr) 973 if err != nil { 974 return nil, err 975 } 976 log.Debug("handle.get.feed: resolved", "manifestkey", manifestAddr, "feed", fd.Hex()) 977 } else { 978 var f feed.Feed 979 if err := f.FromValues(values); err != nil { 980 return nil, ErrCannotResolveFeed 981 982 } 983 fd = &f 984 } 985 return fd, nil 986 } 987 988 // MimeOctetStream default value of http Content-Type header 989 const MimeOctetStream = "application/octet-stream" 990 991 // DetectContentType by file file extension, or fallback to content sniff 992 func DetectContentType(fileName string, f io.ReadSeeker) (string, error) { 993 ctype := mime.TypeByExtension(filepath.Ext(fileName)) 994 if ctype != "" { 995 return ctype, nil 996 } 997 998 // save/rollback to get content probe from begin of file 999 currentPosition, err := f.Seek(0, io.SeekCurrent) 1000 if err != nil { 1001 return MimeOctetStream, fmt.Errorf("seeker can't seek, %s", err) 1002 } 1003 1004 // read a chunk to decide between utf-8 text and binary 1005 var buf [512]byte 1006 n, _ := f.Read(buf[:]) 1007 ctype = http.DetectContentType(buf[:n]) 1008 1009 _, err = f.Seek(currentPosition, io.SeekStart) // rewind to output whole file 1010 if err != nil { 1011 return MimeOctetStream, fmt.Errorf("seeker can't seek, %s", err) 1012 } 1013 1014 return ctype, nil 1015 }