block.go 6.58 KB
Newer Older
1 2 3
package commands

import (
keks's avatar
keks committed
4
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"fmt"
6
	"io"
Jan Winkelmann's avatar
Jan Winkelmann committed
7
	"os"
8

9
	util "github.com/ipfs/go-ipfs/blocks/blockstoreutil"
10
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
Jan Winkelmann's avatar
Jan Winkelmann committed
11
	e "github.com/ipfs/go-ipfs/core/commands/e"
Łukasz Magiera's avatar
Łukasz Magiera committed
12 13
	coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
	"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
14

15
	mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
keks's avatar
keks committed
16
	cmdkit "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
17
	cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
18 19
)

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

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

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

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

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

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

57 58
`,
	},
59

Jan Winkelmann's avatar
Jan Winkelmann committed
60 61
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("key", true, false, "The base58 multihash of an existing block to stat.").EnableStdin(),
62
	},
keks's avatar
keks committed
63
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Łukasz Magiera's avatar
Łukasz Magiera committed
64 65
		api, err := cmdenv.GetApi(env)
		if err != nil {
keks's avatar
keks committed
66
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
67 68 69 70
		}

		p, err := coreiface.ParsePath(req.Arguments[0])
		if err != nil {
keks's avatar
keks committed
71
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
72 73 74
		}

		b, err := api.Block().Stat(req.Context, p)
75
		if err != nil {
keks's avatar
keks committed
76
			return err
77
		}
78

keks's avatar
keks committed
79
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
80 81
			Key:  b.Path().Cid().String(),
			Size: b.Size(),
82
		})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83 84
	},
	Type: BlockStat{},
Jan Winkelmann's avatar
Jan Winkelmann committed
85
	Encoders: cmds.EncoderMap{
86
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
87 88 89 90 91 92 93
			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
94 95
	},
}
96

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

Jan Winkelmann's avatar
Jan Winkelmann committed
106 107
	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
108
	},
keks's avatar
keks committed
109
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Łukasz Magiera's avatar
Łukasz Magiera committed
110 111
		api, err := cmdenv.GetApi(env)
		if err != nil {
keks's avatar
keks committed
112
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
113 114 115 116
		}

		p, err := coreiface.ParsePath(req.Arguments[0])
		if err != nil {
keks's avatar
keks committed
117
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
118 119 120
		}

		r, err := api.Block().Get(req.Context, p)
121
		if err != nil {
keks's avatar
keks committed
122
			return err
123 124
		}

keks's avatar
keks committed
125
		return res.Emit(r)
126 127 128 129
	},
}

var blockPutCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
130
	Helptext: cmdkit.HelpText{
131
		Tagline: "Store input as an IPFS block.",
132
		ShortDescription: `
133
'ipfs block put' is a plumbing command for storing raw IPFS blocks.
134
It reads from stdin, and <key> is a base58 encoded multihash.
135 136 137

By default CIDv0 is going to be generated. Setting 'mhtype' to anything other
than 'sha2-256' or format to anything other than 'v0' will result in CIDv1.
138 139
`,
	},
140

Jan Winkelmann's avatar
Jan Winkelmann committed
141 142
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("data", true, false, "The data to be stored as an IPFS block.").EnableStdin(),
143
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
144
	Options: []cmdkit.Option{
145
		cmdkit.StringOption("format", "f", "cid format for blocks to be created with."),
146 147
		cmdkit.StringOption("mhtype", "multihash hash function").WithDefault("sha2-256"),
		cmdkit.IntOption("mhlen", "multihash hash length").WithDefault(-1),
148
	},
keks's avatar
keks committed
149
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Łukasz Magiera's avatar
Łukasz Magiera committed
150
		api, err := cmdenv.GetApi(env)
151
		if err != nil {
keks's avatar
keks committed
152
			return err
153
		}
154

155
		file, err := req.Files.NextFile()
156
		if err != nil {
keks's avatar
keks committed
157
			return err
158 159
		}

160 161 162
		mhtype, _ := req.Options["mhtype"].(string)
		mhtval, ok := mh.Names[mhtype]
		if !ok {
keks's avatar
keks committed
163
			return fmt.Errorf("unrecognized multihash function: %s", mhtype)
164 165
		}

Łukasz Magiera's avatar
Łukasz Magiera committed
166 167
		mhlen, ok := req.Options["mhlen"].(int)
		if !ok {
keks's avatar
keks committed
168
			return errors.New("missing option \"mhlen\"")
Łukasz Magiera's avatar
Łukasz Magiera committed
169
		}
170

171 172
		format, formatSet := req.Options["format"].(string)
		if !formatSet {
173
			if mhtval != mh.SHA2_256 || (mhlen != -1 && mhlen != 32) {
174
				format = "protobuf"
175 176
			} else {
				format = "v0"
177
			}
178
		}
179

Łukasz Magiera's avatar
Łukasz Magiera committed
180
		p, err := api.Block().Put(req.Context, file, options.Block.Hash(mhtval, mhlen), options.Block.Format(format))
181
		if err != nil {
keks's avatar
keks committed
182
			return err
183 184
		}

keks's avatar
keks committed
185
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
186 187
			Key:  p.Path().Cid().String(),
			Size: p.Size(),
188
		})
189
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
190
	Encoders: cmds.EncoderMap{
191
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
192 193 194 195 196 197 198
			bs, ok := v.(*BlockStat)
			if !ok {
				return e.TypeErr(bs, v)
			}
			_, err := fmt.Fprintf(w, "%s\n", bs.Key)
			return err
		}),
199
	},
Matt Bell's avatar
Matt Bell committed
200
	Type: BlockStat{},
201
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
202

Kevin Atkinson's avatar
Kevin Atkinson committed
203
var blockRmCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
204
	Helptext: cmdkit.HelpText{
Kevin Atkinson's avatar
Kevin Atkinson committed
205 206 207
		Tagline: "Remove IPFS block(s).",
		ShortDescription: `
'ipfs block rm' is a plumbing command for removing raw ipfs blocks.
Łukasz Magiera's avatar
Łukasz Magiera committed
208
It takes a list of base58 encoded multihashes to remove.
Kevin Atkinson's avatar
Kevin Atkinson committed
209 210
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
211 212
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("hash", true, true, "Bash58 encoded multihash of block(s) to remove."),
Kevin Atkinson's avatar
Kevin Atkinson committed
213
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
214
	Options: []cmdkit.Option{
215 216
		cmdkit.BoolOption("force", "f", "Ignore nonexistent blocks."),
		cmdkit.BoolOption("quiet", "q", "Write minimal output."),
217
	},
keks's avatar
keks committed
218
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Łukasz Magiera's avatar
Łukasz Magiera committed
219
		api, err := cmdenv.GetApi(env)
Kevin Atkinson's avatar
Kevin Atkinson committed
220
		if err != nil {
keks's avatar
keks committed
221
			return err
Kevin Atkinson's avatar
Kevin Atkinson committed
222
		}
Łukasz Magiera's avatar
Łukasz Magiera committed
223

224 225
		force, _ := req.Options["force"].(bool)
		quiet, _ := req.Options["quiet"].(bool)
Łukasz Magiera's avatar
Łukasz Magiera committed
226 227 228 229

		// TODO: use batching coreapi when done
		for _, b := range req.Arguments {
			p, err := coreiface.ParsePath(b)
Jeromy's avatar
Jeromy committed
230
			if err != nil {
keks's avatar
keks committed
231
				return err
Jeromy's avatar
Jeromy committed
232 233
			}

Łukasz Magiera's avatar
Łukasz Magiera committed
234 235
			rp, err := api.ResolvePath(req.Context, p)
			if err != nil {
keks's avatar
keks committed
236
				return err
Łukasz Magiera's avatar
Łukasz Magiera committed
237
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
238

Łukasz Magiera's avatar
Łukasz Magiera committed
239
			err = api.Block().Rm(req.Context, rp, options.Block.Force(force))
240
			if err != nil {
keks's avatar
keks committed
241
				err := res.Emit(&util.RemovedBlock{
Łukasz Magiera's avatar
Łukasz Magiera committed
242 243 244
					Hash:  rp.Cid().String(),
					Error: err.Error(),
				})
keks's avatar
keks committed
245 246 247
				if err != nil {
					return err
				}
Łukasz Magiera's avatar
Łukasz Magiera committed
248
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
249

250
			if !quiet {
keks's avatar
keks committed
251
				err := res.Emit(&util.RemovedBlock{
252 253
					Hash: rp.Cid().String(),
				})
keks's avatar
keks committed
254 255 256
				if err != nil {
					return err
				}
257
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
258
		}
keks's avatar
keks committed
259 260

		return nil
Kevin Atkinson's avatar
Kevin Atkinson committed
261
	},
262
	PostRun: cmds.PostRunMap{
keks's avatar
keks committed
263 264
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			return util.ProcRmOutput(res.Next, os.Stdout, os.Stderr)
265
		},
Kevin Atkinson's avatar
Kevin Atkinson committed
266
	},
267
	Type: util.RemovedBlock{},
Kevin Atkinson's avatar
Kevin Atkinson committed
268
}