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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44
var blockStatCmd = &cmds.Command{
45
	Helptext: cmds.HelpText{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
46
		Tagline: "Print information of a raw IPFS block",
47
		ShortDescription: `
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48 49 50 51 52 53
'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

54 55
`,
	},
56

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80 81 82 83 84 85 86 87
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.
`,
	},
88

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

98
		return bytes.NewReader(b.Data), nil
99 100 101 102
	},
}

var blockPutCmd = &cmds.Command{
103 104 105 106 107 108 109
	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.
`,
	},
110

111
	Arguments: []cmds.Argument{
112
		cmds.FileArg("data", true, false, "The data to be stored as an IPFS block").EnableStdin(),
113
	},
114
	Run: func(req cmds.Request) (interface{}, error) {
115 116 117 118
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
119

120 121 122 123 124 125 126 127
		file, err := req.Files().NextFile()
		if err != nil {
			return nil, err
		}

		data, err := ioutil.ReadAll(file)
		if err != nil {
			return nil, err
128 129
		}

130
		err = file.Close()
131
		if err != nil {
132
			return nil, err
133 134 135
		}

		b := blocks.NewBlock(data)
136
		log.Debugf("BlockPut key: '%q'", b.Key())
137 138 139

		k, err := n.Blocks.AddBlock(b)
		if err != nil {
140
			return nil, err
141 142
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143 144 145
		return &BlockStat{
			Key:  k.String(),
			Size: len(data),
146
		}, nil
147
	},
148
	Marshalers: cmds.MarshalerMap{
149
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
150 151
			bs := res.Output().(*BlockStat)
			return strings.NewReader(bs.Key + "\n"), nil
152 153 154
		},
	},
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

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) {
		return nil, cmds.Error{"Not a valid hash", cmds.ErrClient}
	}

	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
	}
	log.Debugf("ipfs block: got block with key: %q", b.Key())
	return b, nil
}