manifest.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU 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. // go-ethereum 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 General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. // Command MANIFEST update
  17. package main
  18. import (
  19. "fmt"
  20. "os"
  21. "strings"
  22. "github.com/ethereum/go-ethereum/cmd/utils"
  23. "github.com/ethereum/go-ethereum/swarm/api"
  24. swarm "github.com/ethereum/go-ethereum/swarm/api/client"
  25. "gopkg.in/urfave/cli.v1"
  26. )
  27. var manifestCommand = cli.Command{
  28. Name: "manifest",
  29. CustomHelpTemplate: helpTemplate,
  30. Usage: "perform operations on swarm manifests",
  31. ArgsUsage: "COMMAND",
  32. Description: "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
  33. Subcommands: []cli.Command{
  34. {
  35. Action: manifestAdd,
  36. CustomHelpTemplate: helpTemplate,
  37. Name: "add",
  38. Usage: "add a new path to the manifest",
  39. ArgsUsage: "<MANIFEST> <path> <hash>",
  40. Description: "Adds a new path to the manifest",
  41. },
  42. {
  43. Action: manifestUpdate,
  44. CustomHelpTemplate: helpTemplate,
  45. Name: "update",
  46. Usage: "update the hash for an already existing path in the manifest",
  47. ArgsUsage: "<MANIFEST> <path> <newhash>",
  48. Description: "Update the hash for an already existing path in the manifest",
  49. },
  50. {
  51. Action: manifestRemove,
  52. CustomHelpTemplate: helpTemplate,
  53. Name: "remove",
  54. Usage: "removes a path from the manifest",
  55. ArgsUsage: "<MANIFEST> <path>",
  56. Description: "Removes a path from the manifest",
  57. },
  58. },
  59. }
  60. // manifestAdd adds a new entry to the manifest at the given path.
  61. // New entry hash, the last argument, must be the hash of a manifest
  62. // with only one entry, which meta-data will be added to the original manifest.
  63. // On success, this function will print new (updated) manifest's hash.
  64. func manifestAdd(ctx *cli.Context) {
  65. args := ctx.Args()
  66. if len(args) != 3 {
  67. utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
  68. }
  69. var (
  70. mhash = args[0]
  71. path = args[1]
  72. hash = args[2]
  73. )
  74. bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  75. client := swarm.NewClient(bzzapi)
  76. m, _, err := client.DownloadManifest(hash)
  77. if err != nil {
  78. utils.Fatalf("Error downloading manifest to add: %v", err)
  79. }
  80. l := len(m.Entries)
  81. if l == 0 {
  82. utils.Fatalf("No entries in manifest %s", hash)
  83. } else if l > 1 {
  84. utils.Fatalf("Too many entries in manifest %s", hash)
  85. }
  86. newManifest := addEntryToManifest(client, mhash, path, m.Entries[0])
  87. fmt.Println(newManifest)
  88. }
  89. // manifestUpdate replaces an existing entry of the manifest at the given path.
  90. // New entry hash, the last argument, must be the hash of a manifest
  91. // with only one entry, which meta-data will be added to the original manifest.
  92. // On success, this function will print hash of the updated manifest.
  93. func manifestUpdate(ctx *cli.Context) {
  94. args := ctx.Args()
  95. if len(args) != 3 {
  96. utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
  97. }
  98. var (
  99. mhash = args[0]
  100. path = args[1]
  101. hash = args[2]
  102. )
  103. bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  104. client := swarm.NewClient(bzzapi)
  105. m, _, err := client.DownloadManifest(hash)
  106. if err != nil {
  107. utils.Fatalf("Error downloading manifest to update: %v", err)
  108. }
  109. l := len(m.Entries)
  110. if l == 0 {
  111. utils.Fatalf("No entries in manifest %s", hash)
  112. } else if l > 1 {
  113. utils.Fatalf("Too many entries in manifest %s", hash)
  114. }
  115. newManifest, _, defaultEntryUpdated := updateEntryInManifest(client, mhash, path, m.Entries[0], true)
  116. if defaultEntryUpdated {
  117. // Print informational message to stderr
  118. // allowing the user to get the new manifest hash from stdout
  119. // without the need to parse the complete output.
  120. fmt.Fprintln(os.Stderr, "Manifest default entry is updated, too")
  121. }
  122. fmt.Println(newManifest)
  123. }
  124. // manifestRemove removes an existing entry of the manifest at the given path.
  125. // On success, this function will print hash of the manifest which does not
  126. // contain the path.
  127. func manifestRemove(ctx *cli.Context) {
  128. args := ctx.Args()
  129. if len(args) != 2 {
  130. utils.Fatalf("Need exactly two arguments <MHASH> <path>")
  131. }
  132. var (
  133. mhash = args[0]
  134. path = args[1]
  135. )
  136. bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  137. client := swarm.NewClient(bzzapi)
  138. newManifest := removeEntryFromManifest(client, mhash, path)
  139. fmt.Println(newManifest)
  140. }
  141. func addEntryToManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string {
  142. var longestPathEntry = api.ManifestEntry{}
  143. mroot, isEncrypted, err := client.DownloadManifest(mhash)
  144. if err != nil {
  145. utils.Fatalf("Manifest download failed: %v", err)
  146. }
  147. // See if we path is in this Manifest or do we have to dig deeper
  148. for _, e := range mroot.Entries {
  149. if path == e.Path {
  150. utils.Fatalf("Path %s already present, not adding anything", path)
  151. } else {
  152. if e.ContentType == api.ManifestType {
  153. prfxlen := strings.HasPrefix(path, e.Path)
  154. if prfxlen && len(path) > len(longestPathEntry.Path) {
  155. longestPathEntry = e
  156. }
  157. }
  158. }
  159. }
  160. if longestPathEntry.Path != "" {
  161. // Load the child Manifest add the entry there
  162. newPath := path[len(longestPathEntry.Path):]
  163. newHash := addEntryToManifest(client, longestPathEntry.Hash, newPath, entry)
  164. // Replace the hash for parent Manifests
  165. newMRoot := &api.Manifest{}
  166. for _, e := range mroot.Entries {
  167. if longestPathEntry.Path == e.Path {
  168. e.Hash = newHash
  169. }
  170. newMRoot.Entries = append(newMRoot.Entries, e)
  171. }
  172. mroot = newMRoot
  173. } else {
  174. // Add the entry in the leaf Manifest
  175. entry.Path = path
  176. mroot.Entries = append(mroot.Entries, entry)
  177. }
  178. newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
  179. if err != nil {
  180. utils.Fatalf("Manifest upload failed: %v", err)
  181. }
  182. return newManifestHash
  183. }
  184. // updateEntryInManifest updates an existing entry o path with a new one in the manifest with provided mhash
  185. // finding the path recursively through all nested manifests. Argument isRoot is used for default
  186. // entry update detection. If the updated entry has the same hash as the default entry, then the
  187. // default entry in root manifest will be updated too.
  188. // Returned values are the new manifest hash, hash of the entry that was replaced by the new entry and
  189. // a a bool that is true if default entry is updated.
  190. func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry, isRoot bool) (newManifestHash, oldHash string, defaultEntryUpdated bool) {
  191. var (
  192. newEntry = api.ManifestEntry{}
  193. longestPathEntry = api.ManifestEntry{}
  194. )
  195. mroot, isEncrypted, err := client.DownloadManifest(mhash)
  196. if err != nil {
  197. utils.Fatalf("Manifest download failed: %v", err)
  198. }
  199. // See if we path is in this Manifest or do we have to dig deeper
  200. for _, e := range mroot.Entries {
  201. if path == e.Path {
  202. newEntry = e
  203. // keep the reference of the hash of the entry that should be replaced
  204. // for default entry detection
  205. oldHash = e.Hash
  206. } else {
  207. if e.ContentType == api.ManifestType {
  208. prfxlen := strings.HasPrefix(path, e.Path)
  209. if prfxlen && len(path) > len(longestPathEntry.Path) {
  210. longestPathEntry = e
  211. }
  212. }
  213. }
  214. }
  215. if longestPathEntry.Path == "" && newEntry.Path == "" {
  216. utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
  217. }
  218. if longestPathEntry.Path != "" {
  219. // Load the child Manifest add the entry there
  220. newPath := path[len(longestPathEntry.Path):]
  221. var newHash string
  222. newHash, oldHash, _ = updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry, false)
  223. // Replace the hash for parent Manifests
  224. newMRoot := &api.Manifest{}
  225. for _, e := range mroot.Entries {
  226. if longestPathEntry.Path == e.Path {
  227. e.Hash = newHash
  228. }
  229. newMRoot.Entries = append(newMRoot.Entries, e)
  230. }
  231. mroot = newMRoot
  232. }
  233. // update the manifest if the new entry is found and
  234. // check if default entry should be updated
  235. if newEntry.Path != "" || isRoot {
  236. // Replace the hash for leaf Manifest
  237. newMRoot := &api.Manifest{}
  238. for _, e := range mroot.Entries {
  239. if newEntry.Path == e.Path {
  240. entry.Path = e.Path
  241. newMRoot.Entries = append(newMRoot.Entries, entry)
  242. } else if isRoot && e.Path == "" && e.Hash == oldHash {
  243. entry.Path = e.Path
  244. newMRoot.Entries = append(newMRoot.Entries, entry)
  245. defaultEntryUpdated = true
  246. } else {
  247. newMRoot.Entries = append(newMRoot.Entries, e)
  248. }
  249. }
  250. mroot = newMRoot
  251. }
  252. newManifestHash, err = client.UploadManifest(mroot, isEncrypted)
  253. if err != nil {
  254. utils.Fatalf("Manifest upload failed: %v", err)
  255. }
  256. return newManifestHash, oldHash, defaultEntryUpdated
  257. }
  258. func removeEntryFromManifest(client *swarm.Client, mhash, path string) string {
  259. var (
  260. entryToRemove = api.ManifestEntry{}
  261. longestPathEntry = api.ManifestEntry{}
  262. )
  263. mroot, isEncrypted, err := client.DownloadManifest(mhash)
  264. if err != nil {
  265. utils.Fatalf("Manifest download failed: %v", err)
  266. }
  267. // See if we path is in this Manifest or do we have to dig deeper
  268. for _, entry := range mroot.Entries {
  269. if path == entry.Path {
  270. entryToRemove = entry
  271. } else {
  272. if entry.ContentType == api.ManifestType {
  273. prfxlen := strings.HasPrefix(path, entry.Path)
  274. if prfxlen && len(path) > len(longestPathEntry.Path) {
  275. longestPathEntry = entry
  276. }
  277. }
  278. }
  279. }
  280. if longestPathEntry.Path == "" && entryToRemove.Path == "" {
  281. utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
  282. }
  283. if longestPathEntry.Path != "" {
  284. // Load the child Manifest remove the entry there
  285. newPath := path[len(longestPathEntry.Path):]
  286. newHash := removeEntryFromManifest(client, longestPathEntry.Hash, newPath)
  287. // Replace the hash for parent Manifests
  288. newMRoot := &api.Manifest{}
  289. for _, entry := range mroot.Entries {
  290. if longestPathEntry.Path == entry.Path {
  291. entry.Hash = newHash
  292. }
  293. newMRoot.Entries = append(newMRoot.Entries, entry)
  294. }
  295. mroot = newMRoot
  296. }
  297. if entryToRemove.Path != "" {
  298. // remove the entry in this Manifest
  299. newMRoot := &api.Manifest{}
  300. for _, entry := range mroot.Entries {
  301. if entryToRemove.Path != entry.Path {
  302. newMRoot.Entries = append(newMRoot.Entries, entry)
  303. }
  304. }
  305. mroot = newMRoot
  306. }
  307. newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
  308. if err != nil {
  309. utils.Fatalf("Manifest upload failed: %v", err)
  310. }
  311. return newManifestHash
  312. }