manifest.go 14 KB

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