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

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

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

14
	"gx/ipfs/QmPTfgFTo9PFr1PvPKyKoeMgBvYPh6cX3aDP7DHKVbnCbi/go-ipfs-cmds"
15
	mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
16
	"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
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
	},
Jeromy's avatar
Jeromy committed
62
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
Łukasz Magiera's avatar
Łukasz Magiera committed
63 64 65 66 67 68 69 70 71 72 73 74 75
		api, err := cmdenv.GetApi(env)
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		p, err := coreiface.ParsePath(req.Arguments[0])
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		b, err := api.Block().Stat(req.Context, p)
76
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
77
			res.SetError(err, cmdkit.ErrNormal)
78
			return
79
		}
80

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

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

Jan Winkelmann's avatar
Jan Winkelmann committed
111 112
	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
113
	},
Jeromy's avatar
Jeromy committed
114
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
Łukasz Magiera's avatar
Łukasz Magiera committed
115 116 117 118 119 120 121 122 123 124 125 126 127
		api, err := cmdenv.GetApi(env)
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		p, err := coreiface.ParsePath(req.Arguments[0])
		if err != nil {
			res.SetError(err, cmdkit.ErrNormal)
			return
		}

		r, err := api.Block().Get(req.Context, p)
128
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
129
			res.SetError(err, cmdkit.ErrNormal)
130
			return
131 132
		}

Łukasz Magiera's avatar
Łukasz Magiera committed
133
		err = res.Emit(r)
Jan Winkelmann's avatar
Jan Winkelmann committed
134 135 136
		if err != nil {
			log.Error(err)
		}
137 138 139 140
	},
}

var blockPutCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
141
	Helptext: cmdkit.HelpText{
142
		Tagline: "Store input as an IPFS block.",
143
		ShortDescription: `
144
'ipfs block put' is a plumbing command for storing raw IPFS blocks.
145
It reads from stdin, and <key> is a base58 encoded multihash.
146 147 148

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.
149 150
`,
	},
151

Jan Winkelmann's avatar
Jan Winkelmann committed
152 153
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("data", true, false, "The data to be stored as an IPFS block.").EnableStdin(),
154
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
155
	Options: []cmdkit.Option{
156
		cmdkit.StringOption("format", "f", "cid format for blocks to be created with."),
157 158
		cmdkit.StringOption("mhtype", "multihash hash function").WithDefault("sha2-256"),
		cmdkit.IntOption("mhlen", "multihash hash length").WithDefault(-1),
159
	},
Jeromy's avatar
Jeromy committed
160
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
Łukasz Magiera's avatar
Łukasz Magiera committed
161
		api, err := cmdenv.GetApi(env)
162
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
163
			res.SetError(err, cmdkit.ErrNormal)
164
			return
165
		}
166

167
		file, err := req.Files.NextFile()
168
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
169
			res.SetError(err, cmdkit.ErrNormal)
170
			return
171 172
		}

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

Łukasz Magiera's avatar
Łukasz Magiera committed
181 182 183 184 185
		mhlen, ok := req.Options["mhlen"].(int)
		if !ok {
			res.SetError("missing option \"mhlen\"", cmdkit.ErrNormal)
			return
		}
186

187 188
		format, formatSet := req.Options["format"].(string)
		if !formatSet {
189
			if mhtval != mh.SHA2_256 || (mhlen != -1 && mhlen != 32) {
190
				format = "protobuf"
191 192
			} else {
				format = "v0"
193
			}
194
		}
195

Łukasz Magiera's avatar
Łukasz Magiera committed
196
		p, err := api.Block().Put(req.Context, file, options.Block.Hash(mhtval, mhlen), options.Block.Format(format))
197
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
198
			res.SetError(err, cmdkit.ErrNormal)
199
			return
200 201
		}

keks's avatar
keks committed
202
		err = cmds.EmitOnce(res, &BlockStat{
Łukasz Magiera's avatar
Łukasz Magiera committed
203 204
			Key:  p.Path().Cid().String(),
			Size: p.Size(),
205
		})
Jan Winkelmann's avatar
Jan Winkelmann committed
206 207 208
		if err != nil {
			log.Error(err)
		}
209
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
210
	Encoders: cmds.EncoderMap{
211
		cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Jan Winkelmann's avatar
Jan Winkelmann committed
212 213 214 215 216 217 218
			bs, ok := v.(*BlockStat)
			if !ok {
				return e.TypeErr(bs, v)
			}
			_, err := fmt.Fprintf(w, "%s\n", bs.Key)
			return err
		}),
219
	},
Matt Bell's avatar
Matt Bell committed
220
	Type: BlockStat{},
221
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
222

Kevin Atkinson's avatar
Kevin Atkinson committed
223
var blockRmCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
224
	Helptext: cmdkit.HelpText{
Kevin Atkinson's avatar
Kevin Atkinson committed
225 226 227
		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
228
It takes a list of base58 encoded multihashes to remove.
Kevin Atkinson's avatar
Kevin Atkinson committed
229 230
`,
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
231 232
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("hash", true, true, "Bash58 encoded multihash of block(s) to remove."),
Kevin Atkinson's avatar
Kevin Atkinson committed
233
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
234
	Options: []cmdkit.Option{
235 236
		cmdkit.BoolOption("force", "f", "Ignore nonexistent blocks."),
		cmdkit.BoolOption("quiet", "q", "Write minimal output."),
237
	},
Jeromy's avatar
Jeromy committed
238
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
Łukasz Magiera's avatar
Łukasz Magiera committed
239
		api, err := cmdenv.GetApi(env)
Kevin Atkinson's avatar
Kevin Atkinson committed
240
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
241
			res.SetError(err, cmdkit.ErrNormal)
Kevin Atkinson's avatar
Kevin Atkinson committed
242 243
			return
		}
Łukasz Magiera's avatar
Łukasz Magiera committed
244

245 246
		force, _ := req.Options["force"].(bool)
		quiet, _ := req.Options["quiet"].(bool)
Łukasz Magiera's avatar
Łukasz Magiera committed
247 248 249 250

		// TODO: use batching coreapi when done
		for _, b := range req.Arguments {
			p, err := coreiface.ParsePath(b)
Jeromy's avatar
Jeromy committed
251
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
252
				res.SetError(err, cmdkit.ErrNormal)
Jeromy's avatar
Jeromy committed
253 254 255
				return
			}

Łukasz Magiera's avatar
Łukasz Magiera committed
256 257 258 259 260
			rp, err := api.ResolvePath(req.Context, p)
			if err != nil {
				res.SetError(err, cmdkit.ErrNormal)
				return
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
261

Łukasz Magiera's avatar
Łukasz Magiera committed
262
			err = api.Block().Rm(req.Context, rp, options.Block.Force(force))
263
			if err != nil {
Łukasz Magiera's avatar
Łukasz Magiera committed
264 265 266 267 268
				res.Emit(&util.RemovedBlock{
					Hash:  rp.Cid().String(),
					Error: err.Error(),
				})
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
269

270 271 272 273 274
			if !quiet {
				res.Emit(&util.RemovedBlock{
					Hash: rp.Cid().String(),
				})
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
275
		}
Kevin Atkinson's avatar
Kevin Atkinson committed
276
	},
277
	PostRun: cmds.PostRunMap{
278
		cmds.CLI: func(req *cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
Jan Winkelmann's avatar
Jan Winkelmann committed
279 280 281 282 283 284 285 286
			reNext, res := cmds.NewChanResponsePair(req)

			go func() {
				defer re.Close()

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

Jan Winkelmann's avatar
Jan Winkelmann committed
288
			return reNext
289
		},
Kevin Atkinson's avatar
Kevin Atkinson committed
290
	},
291
	Type: util.RemovedBlock{},
Kevin Atkinson's avatar
Kevin Atkinson committed
292
}