block.go 7.48 KB
Newer Older
1 2 3 4
package commands

import (
	"bytes"
5
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"fmt"
7
	"io"
8
	"io/ioutil"
Jan Winkelmann's avatar
Jan Winkelmann committed
9
	"os"
10

11
	util "github.com/ipfs/go-ipfs/blocks/blockstore/util"
Jan Winkelmann's avatar
Jan Winkelmann committed
12
	e "github.com/ipfs/go-ipfs/core/commands/e"
13

Steven Allen's avatar
Steven Allen committed
14
	"gx/ipfs/QmZ9hww8R3FKrDRCYPxhN13m6XgjPDpaSvdUfisPvERzXz/go-ipfs-cmds"
Steven Allen's avatar
Steven Allen committed
15 16
	mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
17
	"gx/ipfs/QmceUdzxkimdYsgtX733uNgzf1DLHyBKN6ehGSp85ayppM/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
18
	blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format"
19 20
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21 22 23 24 25 26 27
type BlockStat struct {
	Key  string
	Size int
}

func (bs BlockStat) String() string {
	return fmt.Sprintf("Key: %s\nSize: %d\n", bs.Key, bs.Size)
28 29
}

30
var BlockCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
31
	Helptext: cmdkit.HelpText{
32
		Tagline: "Interact with raw IPFS blocks.",
33
		ShortDescription: `
34
'ipfs block' is a plumbing command used to manipulate raw IPFS blocks.
35
Reads from stdin or writes to stdout, and <key> is a base58 encoded
36 37 38 39
multihash.
`,
	},

40
	Subcommands: map[string]*cmds.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42 43
		"stat": blockStatCmd,
		"get":  blockGetCmd,
		"put":  blockPutCmd,
Kevin Atkinson's avatar
Kevin Atkinson committed
44
		"rm":   blockRmCmd,
45 46 47
	},
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48
var blockStatCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
49
	Helptext: cmdkit.HelpText{
rht's avatar
rht committed
50
		Tagline: "Print information of a raw IPFS block.",
51
		ShortDescription: `
rht's avatar
rht committed
52
'ipfs block stat' is a plumbing command for retrieving information
53
on raw IPFS blocks. It outputs the following to stdout:
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54 55 56 57

	Key  - the base58 encoded multihash
	Size - the size of the block in bytes

58 59
`,
	},
60

Jan Winkelmann's avatar
Jan Winkelmann committed
61 62
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "The base58 multihash of an existing block to stat.").EnableStdin(),
63
	},
Jeromy's avatar
Jeromy committed
64
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
65
		b, err := getBlockForKey(req.Context, env, req.Arguments[0])
66
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
67
			res.SetError(err, cmdkit.ErrNormal)
68
			return
69
		}
70

keks's avatar
keks committed
71
		err = cmds.EmitOnce(res, &BlockStat{
72
			Key:  b.Cid().String(),
Jeromy's avatar
Jeromy committed
73
			Size: len(b.RawData()),
74
		})
Jan Winkelmann's avatar
Jan Winkelmann committed
75 76 77
		if err != nil {
			log.Error(err)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
78 79
	},
	Type: BlockStat{},
Jan Winkelmann's avatar
Jan Winkelmann committed
80
	Encoders: cmds.EncoderMap{
81
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
82 83 84 85 86 87 88
			bs, ok := v.(*BlockStat)
			if !ok {
				return e.TypeErr(bs, v)
			}
			_, err := fmt.Fprintf(w, "%s", bs)
			return err
		}),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
89 90
	},
}
91

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
92
var blockGetCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
93
	Helptext: cmdkit.HelpText{
rht's avatar
rht committed
94
		Tagline: "Get a raw IPFS block.",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95
		ShortDescription: `
96
'ipfs block get' is a plumbing command for retrieving raw IPFS blocks.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97 98 99
It outputs to stdout, and <key> is a base58 encoded multihash.
`,
	},
100

Jan Winkelmann's avatar
Jan Winkelmann committed
101 102
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "The base58 multihash of an existing block to get.").EnableStdin(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103
	},
Jeromy's avatar
Jeromy committed
104
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
105
		b, err := getBlockForKey(req.Context, env, req.Arguments[0])
106
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
107
			res.SetError(err, cmdkit.ErrNormal)
108
			return
109 110
		}

Jan Winkelmann's avatar
Jan Winkelmann committed
111 112 113 114
		err = res.Emit(bytes.NewReader(b.RawData()))
		if err != nil {
			log.Error(err)
		}
115 116 117 118
	},
}

var blockPutCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
119
	Helptext: cmdkit.HelpText{
120
		Tagline: "Store input as an IPFS block.",
121
		ShortDescription: `
122
'ipfs block put' is a plumbing command for storing raw IPFS blocks.
123 124 125
It reads from stdin, and <key> is a base58 encoded multihash.
`,
	},
126

Jan Winkelmann's avatar
Jan Winkelmann committed
127 128
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("data", true, false, "The data to be stored as an IPFS block.").EnableStdin(),
129
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
130
	Options: []cmdkit.Option{
131 132 133
		cmdkit.StringOption("format", "f", "cid format for blocks to be created with.").WithDefault("v0"),
		cmdkit.StringOption("mhtype", "multihash hash function").WithDefault("sha2-256"),
		cmdkit.IntOption("mhlen", "multihash hash length").WithDefault(-1),
134
	},
Jeromy's avatar
Jeromy committed
135
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
136
		n, err := GetNode(env)
137
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
138
			res.SetError(err, cmdkit.ErrNormal)
139
			return
140
		}
141

142
		file, err := req.Files.NextFile()
143
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
144
			res.SetError(err, cmdkit.ErrNormal)
145
			return
146 147 148 149
		}

		data, err := ioutil.ReadAll(file)
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
150
			res.SetError(err, cmdkit.ErrNormal)
151
			return
152 153
		}

154
		err = file.Close()
155
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
156
			res.SetError(err, cmdkit.ErrNormal)
157
			return
158 159
		}

160 161
		var pref cid.Prefix
		pref.Version = 1
162

163
		format, _ := req.Options["format"].(string)
164 165
		formatval, ok := cid.Codecs[format]
		if !ok {
Jan Winkelmann's avatar
Jan Winkelmann committed
166
			res.SetError(fmt.Errorf("unrecognized format: %s", format), cmdkit.ErrNormal)
167 168
			return
		}
169 170 171 172
		if format == "v0" {
			pref.Version = 0
		}
		pref.Codec = formatval
173

174
		mhtype, _ := req.Options["mhtype"].(string)
175 176
		mhtval, ok := mh.Names[mhtype]
		if !ok {
Jan Winkelmann's avatar
Jan Winkelmann committed
177 178
			err := fmt.Errorf("unrecognized multihash function: %s", mhtype)
			res.SetError(err, cmdkit.ErrNormal)
179 180 181 182
			return
		}
		pref.MhType = mhtval

183 184 185
		mhlen, ok := req.Options["mhlen"].(int)
		if !ok {
			res.SetError("missing option \"mhlen\"", cmdkit.ErrNormal)
186 187 188 189
			return
		}
		pref.MhLength = mhlen

190 191
		bcid, err := pref.Sum(data)
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
192
			res.SetError(err, cmdkit.ErrNormal)
193 194 195 196 197
			return
		}

		b, err := blocks.NewBlockWithCid(data, bcid)
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
198
			res.SetError(err, cmdkit.ErrNormal)
199 200
			return
		}
201

202
		err = n.Blocks.AddBlock(b)
203
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
204
			res.SetError(err, cmdkit.ErrNormal)
205
			return
206 207
		}

keks's avatar
keks committed
208
		err = cmds.EmitOnce(res, &BlockStat{
209
			Key:  b.Cid().String(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
210
			Size: len(data),
211
		})
Jan Winkelmann's avatar
Jan Winkelmann committed
212 213 214
		if err != nil {
			log.Error(err)
		}
215
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
216
	Encoders: cmds.EncoderMap{
217
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
218 219 220 221 222 223 224
			bs, ok := v.(*BlockStat)
			if !ok {
				return e.TypeErr(bs, v)
			}
			_, err := fmt.Fprintf(w, "%s\n", bs.Key)
			return err
		}),
225
	},
Matt Bell's avatar
Matt Bell committed
226
	Type: BlockStat{},
227
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
228

Jeromy's avatar
Jeromy committed
229
func getBlockForKey(ctx context.Context, env cmds.Environment, skey string) (blocks.Block, error) {
230 231 232 233
	if len(skey) == 0 {
		return nil, fmt.Errorf("zero length cid invalid")
	}

234
	n, err := GetNode(env)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
235 236 237 238
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
239
	c, err := cid.Decode(skey)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
240 241 242 243
	if err != nil {
		return nil, err
	}

244
	b, err := n.Blocks.GetBlock(ctx, c)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
245 246 247
	if err != nil {
		return nil, err
	}
248

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
249 250
	return b, nil
}
Kevin Atkinson's avatar
Kevin Atkinson committed
251 252

var blockRmCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
253
	Helptext: cmdkit.HelpText{
Kevin Atkinson's avatar
Kevin Atkinson committed
254 255 256 257 258 259
		Tagline: "Remove IPFS block(s).",
		ShortDescription: `
'ipfs block rm' is a plumbing command for removing raw ipfs blocks.
It takes a list of base58 encoded multihashs to remove.
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
260 261
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("hash", true, true, "Bash58 encoded multihash of block(s) to remove."),
Kevin Atkinson's avatar
Kevin Atkinson committed
262
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
263
	Options: []cmdkit.Option{
264 265
		cmdkit.BoolOption("force", "f", "Ignore nonexistent blocks."),
		cmdkit.BoolOption("quiet", "q", "Write minimal output."),
266
	},
Jeromy's avatar
Jeromy committed
267
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
268
		n, err := GetNode(env)
Kevin Atkinson's avatar
Kevin Atkinson committed
269
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
270
			res.SetError(err, cmdkit.ErrNormal)
Kevin Atkinson's avatar
Kevin Atkinson committed
271 272
			return
		}
273 274 275
		hashes := req.Arguments
		force, _ := req.Options["force"].(bool)
		quiet, _ := req.Options["quiet"].(bool)
Jeromy's avatar
Jeromy committed
276
		cids := make([]*cid.Cid, 0, len(hashes))
Kevin Atkinson's avatar
Kevin Atkinson committed
277
		for _, hash := range hashes {
Jeromy's avatar
Jeromy committed
278 279
			c, err := cid.Decode(hash)
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
280 281
				err = fmt.Errorf("invalid content id: %s (%s)", hash, err)
				res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
282 283 284 285
				return
			}

			cids = append(cids, c)
Kevin Atkinson's avatar
Kevin Atkinson committed
286
		}
287
		ch, err := util.RmBlocks(n.Blockstore, n.Pinning, cids, util.RmBlocksOpts{
288 289 290
			Quiet: quiet,
			Force: force,
		})
Jan Winkelmann's avatar
Jan Winkelmann committed
291

292
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
293
			res.SetError(err, cmdkit.ErrNormal)
294 295
			return
		}
Jan Winkelmann's avatar
Jan Winkelmann committed
296 297 298 299 300

		err = res.Emit(ch)
		if err != nil {
			log.Error(err)
		}
Kevin Atkinson's avatar
Kevin Atkinson committed
301
	},
302
	PostRun: cmds.PostRunMap{
303
		cmds.CLI: func(req *cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
Jan Winkelmann's avatar
Jan Winkelmann committed
304 305 306 307 308 309 310 311
			reNext, res := cmds.NewChanResponsePair(req)

			go func() {
				defer re.Close()

				err := util.ProcRmOutput(res.Next, os.Stdout, os.Stderr)
				cmds.HandleError(err, res, re)
			}()
312

Jan Winkelmann's avatar
Jan Winkelmann committed
313
			return reNext
314
		},
Kevin Atkinson's avatar
Kevin Atkinson committed
315
	},
316
	Type: util.RemovedBlock{},
Kevin Atkinson's avatar
Kevin Atkinson committed
317
}