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

import (
	"bytes"
5
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"fmt"
7
	"io"
8
	"io/ioutil"
9
	"strings"
10 11 12 13 14 15 16 17 18

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
	"github.com/jbenet/go-ipfs/blocks"
	cmds "github.com/jbenet/go-ipfs/commands"
	u "github.com/jbenet/go-ipfs/util"
)

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{
29 30 31 32
	Helptext: cmds.HelpText{
		Tagline: "Manipulate raw IPFS blocks",
		ShortDescription: `
'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,
42 43 44
	},
}

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

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

55 56
`,
	},
57

58
	Arguments: []cmds.Argument{
59
		cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get").EnableStdin(),
60
	},
61
	Run: func(req cmds.Request, res cmds.Response) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62
		b, err := getBlockForKey(req, req.Arguments()[0])
63
		if err != nil {
64 65
			res.SetError(err, cmds.ErrNormal)
			return
66
		}
67

68
		res.SetOutput(&BlockStat{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70
			Key:  b.Key().Pretty(),
			Size: len(b.Data),
71
		})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73 74 75 76 77 78 79 80
	},
	Type: BlockStat{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			bs := res.Output().(*BlockStat)
			return strings.NewReader(bs.String()), nil
		},
	},
}
81

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91
	Arguments: []cmds.Argument{
92
		cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get").EnableStdin(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93
	},
94
	Run: func(req cmds.Request, res cmds.Response) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95
		b, err := getBlockForKey(req, req.Arguments()[0])
96
		if err != nil {
97 98
			res.SetError(err, cmds.ErrNormal)
			return
99 100
		}

101
		res.SetOutput(bytes.NewReader(b.Data))
102 103 104 105
	},
}

var blockPutCmd = &cmds.Command{
106 107 108 109 110 111 112
	Helptext: cmds.HelpText{
		Tagline: "Stores input as an IPFS block",
		ShortDescription: `
ipfs block put is a plumbing command for storing raw ipfs blocks.
It reads from stdin, and <key> is a base58 encoded multihash.
`,
	},
113

114
	Arguments: []cmds.Argument{
115
		cmds.FileArg("data", true, false, "The data to be stored as an IPFS block").EnableStdin(),
116
	},
117
	Run: func(req cmds.Request, res cmds.Response) {
118 119
		n, err := req.Context().GetNode()
		if err != nil {
120 121
			res.SetError(err, cmds.ErrNormal)
			return
122
		}
123

124 125
		file, err := req.Files().NextFile()
		if err != nil {
126 127
			res.SetError(err, cmds.ErrNormal)
			return
128 129 130 131
		}

		data, err := ioutil.ReadAll(file)
		if err != nil {
132 133
			res.SetError(err, cmds.ErrNormal)
			return
134 135
		}

136
		err = file.Close()
137
		if err != nil {
138 139
			res.SetError(err, cmds.ErrNormal)
			return
140 141 142
		}

		b := blocks.NewBlock(data)
143
		log.Debugf("BlockPut key: '%q'", b.Key())
144 145 146

		k, err := n.Blocks.AddBlock(b)
		if err != nil {
147 148
			res.SetError(err, cmds.ErrNormal)
			return
149 150
		}

151
		res.SetOutput(&BlockStat{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152 153
			Key:  k.String(),
			Size: len(data),
154
		})
155
	},
156
	Marshalers: cmds.MarshalerMap{
157
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158 159
			bs := res.Output().(*BlockStat)
			return strings.NewReader(bs.Key + "\n"), nil
160 161 162
		},
	},
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
163 164 165 166 167 168 169 170

func getBlockForKey(req cmds.Request, key string) (*blocks.Block, error) {
	n, err := req.Context().GetNode()
	if err != nil {
		return nil, err
	}

	if !u.IsValidHash(key) {
171
		return nil, errors.New("Not a valid hash")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172 173 174 175 176 177 178 179 180 181 182 183
	}

	h, err := mh.FromB58String(key)
	if err != nil {
		return nil, err
	}

	k := u.Key(h)
	b, err := n.Blocks.GetBlock(context.TODO(), k)
	if err != nil {
		return nil, err
	}
184

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
185 186 187
	log.Debugf("ipfs block: got block with key: %q", b.Key())
	return b, nil
}