github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/core/commands2/object.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "io" 8 "io/ioutil" 9 10 cmds "github.com/jbenet/go-ipfs/commands" 11 "github.com/jbenet/go-ipfs/core" 12 dag "github.com/jbenet/go-ipfs/merkledag" 13 ) 14 15 // ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k 16 var ErrObjectTooLarge = errors.New("input object was too large. limit is 512kbytes") 17 18 const inputLimit = 512 * 1024 19 20 type Node struct { 21 Links []Link 22 Data []byte 23 } 24 25 var objectCmd = &cmds.Command{ 26 Description: "Interact with ipfs objects", 27 Help: `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`, 28 29 Subcommands: map[string]*cmds.Command{ 30 "data": objectDataCmd, 31 "links": objectLinksCmd, 32 "get": objectGetCmd, 33 "put": objectPutCmd, 34 }, 35 } 36 37 var objectDataCmd = &cmds.Command{ 38 Description: "Outputs the raw bytes in an IPFS object", 39 Help: `ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node. 40 It outputs to stdout, and <key> is a base58 encoded multihash. 41 42 Note that the "--encoding" option does not affect the output, since the 43 output is the raw data of the object. 44 `, 45 46 Arguments: []cmds.Argument{ 47 cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), 48 }, 49 Run: func(req cmds.Request) (interface{}, error) { 50 n := req.Context().Node 51 52 key, ok := req.Arguments()[0].(string) 53 if !ok { 54 return nil, errors.New("cast error") 55 } 56 57 return objectData(n, key) 58 }, 59 } 60 61 var objectLinksCmd = &cmds.Command{ 62 Description: "Outputs the links pointed to by the specified object", 63 Help: `'ipfs block get' is a plumbing command for retreiving raw IPFS blocks. 64 It outputs to stdout, and <key> is a base58 encoded multihash.`, 65 66 Arguments: []cmds.Argument{ 67 cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), 68 }, 69 Run: func(req cmds.Request) (interface{}, error) { 70 n := req.Context().Node 71 72 key, ok := req.Arguments()[0].(string) 73 if !ok { 74 return nil, errors.New("cast error") 75 } 76 77 return objectLinks(n, key) 78 }, 79 Type: &Object{}, 80 } 81 82 var objectGetCmd = &cmds.Command{ 83 Description: "Get and serialize the DAG node named by <key>", 84 Help: `'ipfs object get' is a plumbing command for retreiving DAG nodes. 85 It serializes the DAG node to the format specified by the "--encoding" flag. 86 It outputs to stdout, and <key> is a base58 encoded multihash. 87 88 This command outputs data in the following encodings: 89 * "protobuf" 90 * "json" 91 * "xml" 92 (Specified by the "--encoding" or "-enc" flags)`, 93 94 Arguments: []cmds.Argument{ 95 cmds.StringArg("key", true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"), 96 }, 97 Run: func(req cmds.Request) (interface{}, error) { 98 n := req.Context().Node 99 100 key, ok := req.Arguments()[0].(string) 101 if !ok { 102 return nil, errors.New("cast error") 103 } 104 105 object, err := objectGet(n, key) 106 if err != nil { 107 return nil, err 108 } 109 110 node := &Node{ 111 Links: make([]Link, len(object.Links)), 112 Data: object.Data, 113 } 114 115 for i, link := range object.Links { 116 node.Links[i] = Link{ 117 Hash: link.Hash.B58String(), 118 Name: link.Name, 119 Size: link.Size, 120 } 121 } 122 123 return node, nil 124 }, 125 Type: &Node{}, 126 Marshallers: map[cmds.EncodingType]cmds.Marshaller{ 127 cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { 128 object := res.Output().(*dag.Node) 129 return object.Marshal() 130 }, 131 }, 132 } 133 134 var objectPutCmd = &cmds.Command{ 135 Description: "Stores input as a DAG object, outputs its key", 136 Help: `'ipfs object put' is a plumbing command for storing DAG nodes. 137 It reads from stdin, and the output is a base58 encoded multihash. 138 139 Data should be in the format specified by <encoding>. 140 <encoding> may be one of the following: 141 * "protobuf" 142 * "json" 143 `, 144 145 Arguments: []cmds.Argument{ 146 cmds.FileArg("data", true, false, "Data to be stored as a DAG object\nMust be encoded as specified in <encoding>"), 147 cmds.StringArg("encoding", true, false, "Encoding type of <data>, either \"protobuf\" or \"json\""), 148 }, 149 Run: func(req cmds.Request) (interface{}, error) { 150 n := req.Context().Node 151 152 input, ok := req.Arguments()[0].(io.Reader) 153 if !ok { 154 return nil, errors.New("cast error") 155 } 156 157 encoding, ok := req.Arguments()[1].(string) 158 if !ok { 159 return nil, errors.New("cast error") 160 } 161 162 output, err := objectPut(n, input, encoding) 163 if err != nil { 164 errType := cmds.ErrNormal 165 if err == ErrUnknownObjectEnc { 166 errType = cmds.ErrClient 167 } 168 return nil, cmds.Error{err.Error(), errType} 169 } 170 171 return output, nil 172 }, 173 Type: &Object{}, 174 } 175 176 // objectData takes a key string and writes out the raw bytes of that node (if there is one) 177 func objectData(n *core.IpfsNode, key string) (io.Reader, error) { 178 dagnode, err := n.Resolver.ResolvePath(key) 179 if err != nil { 180 return nil, err 181 } 182 183 log.Debugf("objectData: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) 184 185 return bytes.NewReader(dagnode.Data), nil 186 } 187 188 // objectLinks takes a key string and lists the links it points to 189 func objectLinks(n *core.IpfsNode, key string) (*Object, error) { 190 dagnode, err := n.Resolver.ResolvePath(key) 191 if err != nil { 192 return nil, err 193 } 194 195 log.Debugf("objectLinks: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) 196 197 return getOutput(dagnode) 198 } 199 200 // objectGet takes a key string from args and a format option and serializes the dagnode to that format 201 func objectGet(n *core.IpfsNode, key string) (*dag.Node, error) { 202 dagnode, err := n.Resolver.ResolvePath(key) 203 if err != nil { 204 return nil, err 205 } 206 207 log.Debugf("objectGet: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) 208 209 return dagnode, nil 210 } 211 212 // objectPut takes a format option, serializes bytes from stdin and updates the dag with that data 213 func objectPut(n *core.IpfsNode, input io.Reader, encoding string) (*Object, error) { 214 var ( 215 dagnode *dag.Node 216 data []byte 217 err error 218 ) 219 220 data, err = ioutil.ReadAll(io.LimitReader(input, inputLimit+10)) 221 if err != nil { 222 return nil, err 223 } 224 225 if len(data) >= inputLimit { 226 return nil, ErrObjectTooLarge 227 } 228 229 switch getObjectEnc(encoding) { 230 case objectEncodingJSON: 231 dagnode = new(dag.Node) 232 err = json.Unmarshal(data, dagnode) 233 234 case objectEncodingProtobuf: 235 dagnode, err = dag.Decoded(data) 236 237 default: 238 return nil, ErrUnknownObjectEnc 239 } 240 241 if err != nil { 242 return nil, err 243 } 244 245 err = addNode(n, dagnode) 246 if err != nil { 247 return nil, err 248 } 249 250 return getOutput(dagnode) 251 } 252 253 // ErrUnknownObjectEnc is returned if a invalid encoding is supplied 254 var ErrUnknownObjectEnc = errors.New("unknown object encoding") 255 256 type objectEncoding string 257 258 const ( 259 objectEncodingJSON objectEncoding = "json" 260 objectEncodingProtobuf = "protobuf" 261 ) 262 263 func getObjectEnc(o interface{}) objectEncoding { 264 v, ok := o.(string) 265 if !ok { 266 // chosen as default because it's human readable 267 log.Warning("option is not a string - falling back to json") 268 return objectEncodingJSON 269 } 270 271 return objectEncoding(v) 272 } 273 274 func getOutput(dagnode *dag.Node) (*Object, error) { 275 key, err := dagnode.Key() 276 if err != nil { 277 return nil, err 278 } 279 280 output := &Object{ 281 Hash: key.Pretty(), 282 Links: make([]Link, len(dagnode.Links)), 283 } 284 285 for i, link := range dagnode.Links { 286 output.Links[i] = Link{ 287 Name: link.Name, 288 Hash: link.Hash.B58String(), 289 Size: link.Size, 290 } 291 } 292 293 return output, nil 294 }