add.go 3.91 KB
Newer Older
1 2 3
package commands

import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
4
	"bytes"
5
	"errors"
6 7 8 9
	"fmt"
	"io"

	cmds "github.com/jbenet/go-ipfs/commands"
10 11
	core "github.com/jbenet/go-ipfs/core"
	importer "github.com/jbenet/go-ipfs/importer"
12
	"github.com/jbenet/go-ipfs/importer/chunk"
13
	dag "github.com/jbenet/go-ipfs/merkledag"
14
	pinning "github.com/jbenet/go-ipfs/pin"
15
	u "github.com/jbenet/go-ipfs/util"
16 17 18 19 20
)

// Error indicating the max depth has been exceded.
var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded")

21
type AddOutput struct {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22 23
	Objects []*Object
	Names   []string
24 25
}

26
var addCmd = &cmds.Command{
27 28 29 30 31 32 33 34 35 36
	Helptext: cmds.HelpText{
		Tagline: "Add an object to ipfs.",
		ShortDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
Note that directories are added recursively, to form the ipfs
MerkleDAG. A smarter partial add with a staging area (like git)
remains to be implemented.
`,
	},

37
	Options: []cmds.Option{
38
		cmds.BoolOption("recursive", "r", "Must be specified when adding directories"),
39 40
	},
	Arguments: []cmds.Argument{
41
		cmds.FileArg("path", true, true, "The path to a file to be added to IPFS"),
42
	},
43
	Run: func(req cmds.Request) (interface{}, error) {
44
		added := &AddOutput{}
45 46 47 48
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
49

50
		_, _, err = req.Option("r").Bool()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51 52 53 54
		if err != nil {
			return nil, err
		}

55
		// returns the last one
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56 57 58 59
		addDagnode := func(name string, dn *dag.Node) error {
			o, err := getOutput(dn)
			if err != nil {
				return err
60
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
61 62 63

			added.Objects = append(added.Objects, o)
			added.Names = append(added.Names, name)
64
			return nil
65
		}
66

67 68
		addFile := func(file cmds.File) (*dag.Node, error) {
			dns, err := add(n, []io.Reader{file})
69 70 71 72
			if err != nil {
				return nil, err
			}

73 74
			log.Infof("adding file: %s", file.FileName())
			if err := addDagnode(file.FileName(), dns[len(dns)-1]); err != nil {
75 76 77 78 79
				return nil, err
			}
			return dns[len(dns)-1], nil // last dag node is the file.
		}

80
		// TODO: handle directories
81

82 83 84
		file, err := req.Files().NextFile()
		for file != nil {
			_, err := addFile(file)
85
			if err != nil {
86
				return nil, err
87
			}
88

89
			file, err = req.Files().NextFile()
90
		}
91
		if err != nil && err != io.EOF {
92
			return nil, err
93 94
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95
		return added, nil
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

		// readers, err := internal.CastToReaders(req.Arguments())
		// if err != nil {
		// 	return nil, err
		// }
		//
		// dagnodes, err := add(n, readers)
		// if err != nil {
		// 	return nil, err
		// }
		//
		// // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system)
		// added := make([]*Object, 0, len(req.Arguments()))
		// for _, dagnode := range dagnodes {
		// 	object, err := getOutput(dagnode)
		// 	if err != nil {
		// 		return nil, err
		// 	}
		//
		// 	added = append(added, object)
		// }
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117 118
		//
		// return &AddOutput{added}, nil
119
	},
120
	Marshalers: cmds.MarshalerMap{
121
		cmds.Text: func(res cmds.Response) ([]byte, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
122 123
			val, ok := res.Output().(*AddOutput)
			if !ok {
124
				return nil, u.ErrCast()
125
			}
126

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127 128 129
			var buf bytes.Buffer
			for i, obj := range val.Objects {
				buf.Write([]byte(fmt.Sprintf("added %s %s\n", obj.Hash, val.Names[i])))
130
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
131
			return buf.Bytes(), nil
132
		},
133
	},
134
	Type: &AddOutput{},
135 136
}

137
func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) {
138 139 140 141 142
	mp, ok := n.Pinning.(pinning.ManualPinner)
	if !ok {
		return nil, errors.New("invalid pinner type! expected manual pinner")
	}

143
	dagnodes := make([]*dag.Node, 0)
144

145 146
	// TODO: allow adding directories (will need support for multiple files in filearg system)

147
	for _, reader := range readers {
148
		node, err := importer.BuildDagFromReader(reader, n.DAG, mp, chunk.DefaultSplitter)
149 150 151 152 153
		if err != nil {
			return nil, err
		}
		dagnodes = append(dagnodes, node)
	}
154

155
	return dagnodes, nil
156
}
157 158 159 160 161 162 163 164 165 166 167 168 169 170

func addNode(n *core.IpfsNode, node *dag.Node) error {
	err := n.DAG.AddRecursive(node) // add the file to the graph + local storage
	if err != nil {
		return err
	}

	err = n.Pinning.Pin(node, true) // ensure we keep it
	if err != nil {
		return err
	}

	return nil
}