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

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

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

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

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

28
var addCmd = &cmds.Command{
29 30 31 32 33 34 35 36 37 38
	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.
`,
	},

39
	Arguments: []cmds.Argument{
40
		cmds.FileArg("path", true, true, "The path to a file to be added to IPFS").EnableRecursive(),
41
	},
42
	Run: func(req cmds.Request) (interface{}, error) {
43
		added := &AddOutput{}
44 45 46 47
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
48

49 50 51
		for {
			file, err := req.Files().NextFile()
			if err != nil && err != io.EOF {
52 53
				return nil, err
			}
54 55
			if file == nil {
				break
56 57
			}

58
			_, err = addFile(n, file, added)
59
			if err != nil {
60
				return nil, err
61
			}
62 63
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
64
		return added, nil
65
	},
66
	Marshalers: cmds.MarshalerMap{
67
		cmds.Text: func(res cmds.Response) ([]byte, error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
68 69
			val, ok := res.Output().(*AddOutput)
			if !ok {
70
				return nil, u.ErrCast()
71
			}
72

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
73 74 75
			var buf bytes.Buffer
			for i, obj := range val.Objects {
				buf.Write([]byte(fmt.Sprintf("added %s %s\n", obj.Hash, val.Names[i])))
76
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
			return buf.Bytes(), nil
78
		},
79
	},
80
	Type: &AddOutput{},
81 82
}

83
func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) {
84 85 86 87 88
	mp, ok := n.Pinning.(pinning.ManualPinner)
	if !ok {
		return nil, errors.New("invalid pinner type! expected manual pinner")
	}

89
	dagnodes := make([]*dag.Node, 0)
90

91
	for _, reader := range readers {
92
		node, err := importer.BuildDagFromReader(reader, n.DAG, mp, chunk.DefaultSplitter)
93 94 95 96 97
		if err != nil {
			return nil, err
		}
		dagnodes = append(dagnodes, node)
	}
98

99
	return dagnodes, nil
100
}
101 102 103 104 105 106 107 108 109 110 111 112 113 114

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
}
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

func addFile(n *core.IpfsNode, file cmds.File, added *AddOutput) (*dag.Node, error) {
	if file.IsDirectory() {
		return addDir(n, file, added)
	}

	dns, err := add(n, []io.Reader{file})
	if err != nil {
		return nil, err
	}

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

func addDir(n *core.IpfsNode, dir cmds.File, added *AddOutput) (*dag.Node, error) {
	log.Infof("adding directory: %s", dir.FileName())

	tree := &dag.Node{Data: ft.FolderPBData()}

	for {
		file, err := dir.NextFile()
		if err != nil && err != io.EOF {
			return nil, err
		}
		if file == nil {
			break
		}

		node, err := addFile(n, file, added)
		if err != nil {
			return nil, err
		}

152
		_, name := path.Split(file.FileName())
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

		err = tree.AddNodeLink(name, node)
		if err != nil {
			return nil, err
		}
	}

	addDagnode(added, dir.FileName(), tree)

	return tree, addNode(n, tree)
}

// addDagnode adds dagnode info to an output object
func addDagnode(output *AddOutput, name string, dn *dag.Node) error {
	o, err := getOutput(dn)
	if err != nil {
		return err
	}

	output.Objects = append(output.Objects, o)
	output.Names = append(output.Names, name)
	return nil
}