block.go 6.51 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

Jakub Sztandera's avatar
Jakub Sztandera committed
12 13
	cmds "github.com/ipfs/go-ipfs-cmds"
	options "github.com/ipfs/interface-go-ipfs-core/options"
14
	path "github.com/ipfs/interface-go-ipfs-core/path"
Jakub Sztandera's avatar
Jakub Sztandera committed
15
	mh "github.com/multiformats/go-multihash"
16 17
)

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

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

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

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

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

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

55 56
`,
	},
57

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

67
		b, err := api.Block().Stat(req.Context, path.New(req.Arguments[0]))
68
		if err != nil {
keks's avatar
keks committed
69
			return err
70
		}
71

keks's avatar
keks committed
72
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
73 74
			Key:  b.Path().Cid().String(),
			Size: b.Size(),
75
		})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76 77
	},
	Type: BlockStat{},
Jan Winkelmann's avatar
Jan Winkelmann committed
78
	Encoders: cmds.EncoderMap{
Overbool's avatar
Overbool committed
79
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
80 81 82
			_, err := fmt.Fprintf(w, "%s", bs)
			return err
		}),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83 84
	},
}
85

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
86
var blockGetCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
87
	Helptext: cmds.HelpText{
rht's avatar
rht committed
88
		Tagline: "Get a raw IPFS block.",
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
89
		ShortDescription: `
90
'ipfs block get' is a plumbing command for retrieving raw IPFS blocks.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91 92 93
It outputs to stdout, and <key> is a base58 encoded multihash.
`,
	},
94

Steven Allen's avatar
Steven Allen committed
95 96
	Arguments: []cmds.Argument{
		cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get.").EnableStdin(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97
	},
keks's avatar
keks committed
98
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
99
		api, err := cmdenv.GetApi(env, req)
Łukasz Magiera's avatar
Łukasz Magiera committed
100
		if err != nil {
keks's avatar
keks committed
101
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
102 103
		}

104
		r, err := api.Block().Get(req.Context, path.New(req.Arguments[0]))
105
		if err != nil {
keks's avatar
keks committed
106
			return err
107 108
		}

keks's avatar
keks committed
109
		return res.Emit(r)
110 111 112
	},
}

Kejie Zhang's avatar
Kejie Zhang committed
113 114 115 116 117 118
const (
	blockFormatOptionName = "format"
	mhtypeOptionName      = "mhtype"
	mhlenOptionName       = "mhlen"
)

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

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.
128 129
`,
	},
130

Steven Allen's avatar
Steven Allen committed
131 132
	Arguments: []cmds.Argument{
		cmds.FileArg("data", true, false, "The data to be stored as an IPFS block.").EnableStdin(),
133
	},
Steven Allen's avatar
Steven Allen committed
134 135 136 137 138
	Options: []cmds.Option{
		cmds.StringOption(blockFormatOptionName, "f", "cid format for blocks to be created with."),
		cmds.StringOption(mhtypeOptionName, "multihash hash function").WithDefault("sha2-256"),
		cmds.IntOption(mhlenOptionName, "multihash hash length").WithDefault(-1),
		cmds.BoolOption(pinOptionName, "pin added blocks recursively").WithDefault(false),
139
	},
keks's avatar
keks committed
140
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
141
		api, err := cmdenv.GetApi(env, req)
142
		if err != nil {
keks's avatar
keks committed
143
			return err
144
		}
145

146 147 148
		file, err := cmdenv.GetFileArg(req.Files.Entries())
		if err != nil {
			return err
149 150
		}

Kejie Zhang's avatar
Kejie Zhang committed
151
		mhtype, _ := req.Options[mhtypeOptionName].(string)
152 153
		mhtval, ok := mh.Names[mhtype]
		if !ok {
keks's avatar
keks committed
154
			return fmt.Errorf("unrecognized multihash function: %s", mhtype)
155 156
		}

Kejie Zhang's avatar
Kejie Zhang committed
157
		mhlen, ok := req.Options[mhlenOptionName].(int)
Łukasz Magiera's avatar
Łukasz Magiera committed
158
		if !ok {
keks's avatar
keks committed
159
			return errors.New("missing option \"mhlen\"")
Łukasz Magiera's avatar
Łukasz Magiera committed
160
		}
161

Kejie Zhang's avatar
Kejie Zhang committed
162
		format, formatSet := req.Options[blockFormatOptionName].(string)
163
		if !formatSet {
164
			if mhtval != mh.SHA2_256 || (mhlen != -1 && mhlen != 32) {
165
				format = "protobuf"
166 167
			} else {
				format = "v0"
168
			}
169
		}
170

Łukasz Magiera's avatar
Łukasz Magiera committed
171 172 173 174 175 176
		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))
177
		if err != nil {
keks's avatar
keks committed
178
			return err
179 180
		}

keks's avatar
keks committed
181
		return cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
182 183
			Key:  p.Path().Cid().String(),
			Size: p.Size(),
184
		})
185
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
186
	Encoders: cmds.EncoderMap{
Overbool's avatar
Overbool committed
187
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
188 189 190
			_, err := fmt.Fprintf(w, "%s\n", bs.Key)
			return err
		}),
191
	},
Matt Bell's avatar
Matt Bell committed
192
	Type: BlockStat{},
193
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
194

Kejie Zhang's avatar
Kejie Zhang committed
195 196 197 198 199
const (
	forceOptionName      = "force"
	blockQuietOptionName = "quiet"
)

Kevin Atkinson's avatar
Kevin Atkinson committed
200
var blockRmCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
201
	Helptext: cmds.HelpText{
Kevin Atkinson's avatar
Kevin Atkinson committed
202 203 204
		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
205
It takes a list of base58 encoded multihashes to remove.
Kevin Atkinson's avatar
Kevin Atkinson committed
206 207
`,
	},
Steven Allen's avatar
Steven Allen committed
208 209
	Arguments: []cmds.Argument{
		cmds.StringArg("hash", true, true, "Bash58 encoded multihash of block(s) to remove."),
Kevin Atkinson's avatar
Kevin Atkinson committed
210
	},
Steven Allen's avatar
Steven Allen committed
211 212 213
	Options: []cmds.Option{
		cmds.BoolOption(forceOptionName, "f", "Ignore nonexistent blocks."),
		cmds.BoolOption(blockQuietOptionName, "q", "Write minimal output."),
214
	},
keks's avatar
keks committed
215
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
216
		api, err := cmdenv.GetApi(env, req)
Kevin Atkinson's avatar
Kevin Atkinson committed
217
		if err != nil {
keks's avatar
keks committed
218
			return err
Kevin Atkinson's avatar
Kevin Atkinson committed
219
		}
Łukasz Magiera's avatar
Łukasz Magiera committed
220

Kejie Zhang's avatar
Kejie Zhang committed
221 222
		force, _ := req.Options[forceOptionName].(bool)
		quiet, _ := req.Options[blockQuietOptionName].(bool)
Łukasz Magiera's avatar
Łukasz Magiera committed
223 224 225

		// TODO: use batching coreapi when done
		for _, b := range req.Arguments {
226
			rp, err := api.ResolvePath(req.Context, path.New(b))
Steven Allen's avatar
Steven Allen committed
227 228 229
			if err != nil {
				return err
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
230

Łukasz Magiera's avatar
Łukasz Magiera committed
231
			err = api.Block().Rm(req.Context, rp, options.Block.Force(force))
232
			if err != nil {
Overbool's avatar
Overbool committed
233
				if err := res.Emit(&util.RemovedBlock{
Łukasz Magiera's avatar
Łukasz Magiera committed
234 235
					Hash:  rp.Cid().String(),
					Error: err.Error(),
Overbool's avatar
Overbool committed
236
				}); err != nil {
keks's avatar
keks committed
237 238
					return err
				}
Overbool's avatar
Overbool committed
239
				continue
Łukasz Magiera's avatar
Łukasz Magiera committed
240
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
241

242
			if !quiet {
keks's avatar
keks committed
243
				err := res.Emit(&util.RemovedBlock{
244 245
					Hash: rp.Cid().String(),
				})
keks's avatar
keks committed
246 247 248
				if err != nil {
					return err
				}
249
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
250
		}
keks's avatar
keks committed
251 252

		return nil
Kevin Atkinson's avatar
Kevin Atkinson committed
253
	},
254
	PostRun: cmds.PostRunMap{
keks's avatar
keks committed
255 256
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			return util.ProcRmOutput(res.Next, os.Stdout, os.Stderr)
257
		},
Kevin Atkinson's avatar
Kevin Atkinson committed
258
	},
259
	Type: util.RemovedBlock{},
Kevin Atkinson's avatar
Kevin Atkinson committed
260
}