pin.go 4.62 KB
Newer Older
1 2 3 4 5 6
package commands

import (
	"fmt"

	cmds "github.com/jbenet/go-ipfs/commands"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
7 8
	"github.com/jbenet/go-ipfs/core"
	"github.com/jbenet/go-ipfs/merkledag"
9
	u "github.com/jbenet/go-ipfs/util"
10 11 12
)

var pinCmd = &cmds.Command{
13
	Helptext: cmds.HelpText{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
14
		Tagline: "Pin (and unpin) objects to local storage",
15
	},
16

Brian Tiger Chow's avatar
Brian Tiger Chow committed
17 18 19
	Subcommands: map[string]*cmds.Command{
		"add": addPinCmd,
		"rm":  rmPinCmd,
20
		"ls":  listPinCmd,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
21 22 23 24
	},
}

var addPinCmd = &cmds.Command{
25 26 27 28
	Helptext: cmds.HelpText{
		Tagline: "Pins objects to local storage",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and stores it locally
29
on disk.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
30
`,
31
	},
32

33
	Arguments: []cmds.Argument{
34
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned"),
35 36
	},
	Options: []cmds.Option{
37
		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
38
	},
39
	Run: func(req cmds.Request) (interface{}, error) {
40 41 42 43
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
44 45

		// set recursive flag
46 47 48 49 50 51 52
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false
		}
53

54
		_, err = pin(n, req.Arguments(), recursive)
55
		if err != nil {
56
			return nil, err
57 58 59
		}

		// TODO: create some output to show what got pinned
60
		return nil, nil
61 62 63
	},
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
64
var rmPinCmd = &cmds.Command{
65 66 67 68
	Helptext: cmds.HelpText{
		Tagline: "Unpin an object from local storage",
		ShortDescription: `
Removes the pin from the given object allowing it to be garbage
69
collected if needed.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
70
`,
71
	},
72

73
	Arguments: []cmds.Argument{
74
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned"),
75 76
	},
	Options: []cmds.Option{
77
		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
78
	},
79
	Run: func(req cmds.Request) (interface{}, error) {
80 81 82 83
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
84 85

		// set recursive flag
86 87 88 89 90 91 92
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false // default
		}
93

94
		_, err = unpin(n, req.Arguments(), recursive)
95
		if err != nil {
96
			return nil, err
97 98 99
		}

		// TODO: create some output to show what got unpinned
100
		return nil, nil
101 102
	},
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
103

104 105 106 107 108 109 110 111 112
var listPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List objects pinned to local storage",
		ShortDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.
`,
	},

113 114 115
	Options: []cmds.Option{
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\""),
	},
116 117 118 119 120 121
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
		typeStr, found, err := req.Option("type").String()
		if err != nil {
			return nil, err
		}
		if !found {
			typeStr = "all"
		}

		if typeStr != "all" && typeStr != "direct" && typeStr != "indirect" && typeStr != "recursive" {
			return nil, cmds.ClientError("Invalid type '" + typeStr + "', must be \"direct\", \"indirect\", \"recursive\", or \"all\"")
		}

		keys := make([]u.Key, 0)
		if typeStr == "direct" || typeStr == "all" {
			keys = append(keys, n.Pinning.DirectKeys()...)
		}
		if typeStr == "indirect" || typeStr == "all" {
			keys = append(keys, n.Pinning.IndirectKeys()...)
		}
		if typeStr == "recursive" || typeStr == "all" {
			keys = append(keys, n.Pinning.RecursiveKeys()...)
		}

		return &KeyList{Keys: keys}, nil
146 147 148 149
	},
	Type: &KeyList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: KeyListTextMarshaler,
150 151 152
	},
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, fmt.Errorf("pin error: %v", err)
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		err := n.Pinning.Pin(dagnode, recursive)
		if err != nil {
			return nil, fmt.Errorf("pin: %v", err)
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}

	return dagnodes, nil
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

func unpin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, err
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		k, _ := dagnode.Key()
		err := n.Pinning.Unpin(k, recursive)
		if err != nil {
			return nil, err
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}
	return dagnodes, nil
}