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