block.go 6.96 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"
11

Hector Sanjuan's avatar
Hector Sanjuan committed
12 13
	coreiface "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core"
	options "gx/ipfs/QmVzvYWRABgGEv4iu3M9wivWbZKTW29qsU4VTZ2iZEoExX/interface-go-ipfs-core/options"
Steven Allen's avatar
Steven Allen committed
14
	cmds "gx/ipfs/QmWmn1Fo7ECXJYGAUf6wFWd677wVuryWePqUD678Dkt4ok/go-ipfs-cmds"
15
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
16
	mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
17 18
)

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

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

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

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

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

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

56 57
`,
	},
58

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

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

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

keks's avatar
keks committed
78
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
79 80
			Key:  b.Path().Cid().String(),
			Size: b.Size(),
81
		})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
82 83
	},
	Type: BlockStat{},
Jan Winkelmann's avatar
Jan Winkelmann committed
84
	Encoders: cmds.EncoderMap{
Overbool's avatar
Overbool committed
85
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
86 87 88
			_, 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
	},
keks's avatar
keks committed
104
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
105
		api, err := cmdenv.GetApi(env, req)
Łukasz Magiera's avatar
Łukasz Magiera committed
106
		if err != nil {
keks's avatar
keks committed
107
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
108 109 110 111
		}

		p, err := coreiface.ParsePath(req.Arguments[0])
		if err != nil {
keks's avatar
keks committed
112
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
113 114 115
		}

		r, err := api.Block().Get(req.Context, p)
116
		if err != nil {
keks's avatar
keks committed
117
			return err
118 119
		}

keks's avatar
keks committed
120
		return res.Emit(r)
121 122 123
	},
}

Kejie Zhang's avatar
Kejie Zhang committed
124 125 126 127 128 129
const (
	blockFormatOptionName = "format"
	mhtypeOptionName      = "mhtype"
	mhlenOptionName       = "mhlen"
)

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

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.
139 140
`,
	},
141

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

157 158 159
		file, err := cmdenv.GetFileArg(req.Files.Entries())
		if err != nil {
			return err
160 161
		}

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

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

Kejie Zhang's avatar
Kejie Zhang committed
173
		format, formatSet := req.Options[blockFormatOptionName].(string)
174
		if !formatSet {
175
			if mhtval != mh.SHA2_256 || (mhlen != -1 && mhlen != 32) {
176
				format = "protobuf"
177 178
			} else {
				format = "v0"
179
			}
180
		}
181

Łukasz Magiera's avatar
Łukasz Magiera committed
182 183 184 185 186 187
		pin, _ := req.Options[pinOptionName].(bool)

		p, err := api.Block().Put(req.Context, file,
			options.Block.Hash(mhtval, mhlen),
			options.Block.Format(format),
			options.Block.Pin(pin))
188
		if err != nil {
keks's avatar
keks committed
189
			return err
190 191
		}

keks's avatar
keks committed
192
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
193 194
			Key:  p.Path().Cid().String(),
			Size: p.Size(),
195
		})
196
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
197
	Encoders: cmds.EncoderMap{
Overbool's avatar
Overbool committed
198
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
199 200 201
			_, err := fmt.Fprintf(w, "%s\n", bs.Key)
			return err
		}),
202
	},
Matt Bell's avatar
Matt Bell committed
203
	Type: BlockStat{},
204
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
205

Kejie Zhang's avatar
Kejie Zhang committed
206 207 208 209 210
const (
	forceOptionName      = "force"
	blockQuietOptionName = "quiet"
)

Kevin Atkinson's avatar
Kevin Atkinson committed
211
var blockRmCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
212
	Helptext: cmdkit.HelpText{
Kevin Atkinson's avatar
Kevin Atkinson committed
213 214 215
		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
216
It takes a list of base58 encoded multihashes to remove.
Kevin Atkinson's avatar
Kevin Atkinson committed
217 218
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
219 220
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("hash", true, true, "Bash58 encoded multihash of block(s) to remove."),
Kevin Atkinson's avatar
Kevin Atkinson committed
221
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
222
	Options: []cmdkit.Option{
Kejie Zhang's avatar
Kejie Zhang committed
223 224
		cmdkit.BoolOption(forceOptionName, "f", "Ignore nonexistent blocks."),
		cmdkit.BoolOption(blockQuietOptionName, "q", "Write minimal output."),
225
	},
keks's avatar
keks committed
226
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
227
		api, err := cmdenv.GetApi(env, req)
Kevin Atkinson's avatar
Kevin Atkinson committed
228
		if err != nil {
keks's avatar
keks committed
229
			return err
Kevin Atkinson's avatar
Kevin Atkinson committed
230
		}
Łukasz Magiera's avatar
Łukasz Magiera committed
231

Kejie Zhang's avatar
Kejie Zhang committed
232 233
		force, _ := req.Options[forceOptionName].(bool)
		quiet, _ := req.Options[blockQuietOptionName].(bool)
Łukasz Magiera's avatar
Łukasz Magiera committed
234 235 236 237

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

Łukasz Magiera's avatar
Łukasz Magiera committed
242 243
			rp, err := api.ResolvePath(req.Context, p)
			if err != nil {
keks's avatar
keks committed
244
				return err
Łukasz Magiera's avatar
Łukasz Magiera committed
245
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
246

Łukasz Magiera's avatar
Łukasz Magiera committed
247
			err = api.Block().Rm(req.Context, rp, options.Block.Force(force))
248
			if err != nil {
Overbool's avatar
Overbool committed
249
				if err := res.Emit(&util.RemovedBlock{
Łukasz Magiera's avatar
Łukasz Magiera committed
250 251
					Hash:  rp.Cid().String(),
					Error: err.Error(),
Overbool's avatar
Overbool committed
252
				}); err != nil {
keks's avatar
keks committed
253 254
					return err
				}
Overbool's avatar
Overbool committed
255
				continue
Łukasz Magiera's avatar
Łukasz Magiera committed
256
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
257

258
			if !quiet {
keks's avatar
keks committed
259
				err := res.Emit(&util.RemovedBlock{
260 261
					Hash: rp.Cid().String(),
				})
keks's avatar
keks committed
262 263 264
				if err != nil {
					return err
				}
265
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
266
		}
keks's avatar
keks committed
267 268

		return nil
Kevin Atkinson's avatar
Kevin Atkinson committed
269
	},
270
	PostRun: cmds.PostRunMap{
keks's avatar
keks committed
271 272
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			return util.ProcRmOutput(res.Next, os.Stdout, os.Stderr)
273
		},
Kevin Atkinson's avatar
Kevin Atkinson committed
274
	},
275
	Type: util.RemovedBlock{},
Kevin Atkinson's avatar
Kevin Atkinson committed
276
}