Skip to content

Commit

Permalink
Merge pull request #1506 from ipfs/feat/patch-create
Browse files Browse the repository at this point in the history
allow patch to optionally create intermediate dirs
  • Loading branch information
jbenet committed Jul 29, 2015
2 parents 681da0a + aa7d946 commit e517b65
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 81 deletions.
92 changes: 27 additions & 65 deletions core/commands/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
cmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
dag "github.com/ipfs/go-ipfs/merkledag"
dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
path "github.com/ipfs/go-ipfs/path"
ft "github.com/ipfs/go-ipfs/unixfs"
u "github.com/ipfs/go-ipfs/util"
Expand Down Expand Up @@ -453,7 +454,9 @@ This removes the link named foo from the hash in $FOO_BAR and returns the
resulting object hash.
`,
},
Options: []cmds.Option{},
Options: []cmds.Option{
cmds.BoolOption("create", "p", "create intermediate directories on add-link"),
},
Arguments: []cmds.Argument{
cmds.StringArg("root", true, false, "the hash of the node to modify"),
cmds.StringArg("command", true, false, "the operation to perform"),
Expand All @@ -467,9 +470,13 @@ resulting object hash.
return
}

rhash := key.B58KeyDecode(req.Arguments()[0])
rootarg := req.Arguments()[0]
if strings.HasPrefix(rootarg, "/ipfs/") {
rootarg = rootarg[6:]
}
rhash := key.B58KeyDecode(rootarg)
if rhash == "" {
res.SetError(fmt.Errorf("incorrectly formatted root hash"), cmds.ErrNormal)
res.SetError(fmt.Errorf("incorrectly formatted root hash: %s", req.Arguments()[0]), cmds.ErrNormal)
return
}

Expand Down Expand Up @@ -580,19 +587,18 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
return "", err
}

name := req.Arguments()[2]
path := req.Arguments()[2]

err = root.RemoveNodeLink(name)
if err != nil {
return "", err
}
e := dagutils.NewDagEditor(nd.DAG, root)

newkey, err := nd.DAG.Add(root)
err = e.RmLink(req.Context(), path)
if err != nil {
return "", err
}

return newkey, nil
nnode := e.GetNode()

return nnode.Key()
}

func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
Expand All @@ -608,72 +614,28 @@ func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
path := req.Arguments()[2]
childk := key.B58KeyDecode(req.Arguments()[3])

parts := strings.Split(path, "/")

nnode, err := insertNodeAtPath(req.Context(), nd.DAG, root, parts, childk)
create, _, err := req.Option("create").Bool()
if err != nil {
return "", err
}
return nnode.Key()
}

func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
childnd, err := ds.Get(ctx, childk)
if err != nil {
cancel()
return nil, err
}
cancel()

err = root.AddNodeLinkClean(childname, childnd)
if err != nil {
return nil, err
}

_, err = ds.Add(root)
if err != nil {
return nil, err
}
return root, nil
}

func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key) (*dag.Node, error) {
if len(path) == 1 {
return addLink(ctx, ds, root, path[0], toinsert)
}

child, err := root.GetNodeLink(path[0])
if err != nil {
return nil, err
}

nd, err := child.GetNode(ctx, ds)
if err != nil {
return nil, err
}

ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert)
if err != nil {
return nil, err
var createfunc func() *dag.Node
if create {
createfunc = func() *dag.Node {
return &dag.Node{Data: ft.FolderPBData()}
}
}

err = root.RemoveNodeLink(path[0])
if err != nil {
return nil, err
}
e := dagutils.NewDagEditor(nd.DAG, root)

err = root.AddNodeLinkClean(path[0], ndprime)
err = e.InsertNodeAtPath(req.Context(), path, childk, createfunc)
if err != nil {
return nil, err
return "", err
}

_, err = ds.Add(root)
if err != nil {
return nil, err
}
nnode := e.GetNode()

return root, nil
return nnode.Key()
}

func nodeFromTemplate(template string) (*dag.Node, error) {
Expand Down
12 changes: 1 addition & 11 deletions merkledag/coding.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,6 @@ func (n *Node) Unmarshal(encoded []byte) error {
return nil
}

// MarshalTo encodes a *Node instance into a given byte slice.
// The conversion uses an intermediate PBNode.
func (n *Node) MarshalTo(encoded []byte) error {
pbn := n.getPBNode()
if _, err := pbn.MarshalTo(encoded); err != nil {
return fmt.Errorf("Marshal failed. %v", err)
}
return nil
}

// Marshal encodes a *Node instance into a new byte slice.
// The conversion uses an intermediate PBNode.
func (n *Node) Marshal() ([]byte, error) {
Expand Down Expand Up @@ -82,7 +72,7 @@ func (n *Node) Encoded(force bool) ([]byte, error) {
var err error
n.encoded, err = n.Marshal()
if err != nil {
return []byte{}, err
return nil, err
}
n.cached = u.Hash(n.encoded)
}
Expand Down
68 changes: 68 additions & 0 deletions merkledag/merkledag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"io/ioutil"
"strings"
"sync"
"testing"

Expand Down Expand Up @@ -221,3 +222,70 @@ func runBatchFetchTest(t *testing.T, read io.Reader) {

wg.Wait()
}

func TestRecursiveAdd(t *testing.T) {
a := &Node{Data: []byte("A")}
b := &Node{Data: []byte("B")}
c := &Node{Data: []byte("C")}
d := &Node{Data: []byte("D")}
e := &Node{Data: []byte("E")}

err := a.AddNodeLink("blah", b)
if err != nil {
t.Fatal(err)
}

err = b.AddNodeLink("foo", c)
if err != nil {
t.Fatal(err)
}

err = b.AddNodeLink("bar", d)
if err != nil {
t.Fatal(err)
}

err = d.AddNodeLink("baz", e)
if err != nil {
t.Fatal(err)
}

dsp := getDagservAndPinner(t)
err = dsp.ds.AddRecursive(a)
if err != nil {
t.Fatal(err)
}

assertCanGet(t, dsp.ds, a)
assertCanGet(t, dsp.ds, b)
assertCanGet(t, dsp.ds, c)
assertCanGet(t, dsp.ds, d)
assertCanGet(t, dsp.ds, e)
}

func assertCanGet(t *testing.T, ds DAGService, n *Node) {
k, err := n.Key()
if err != nil {
t.Fatal(err)
}

_, err = ds.Get(context.TODO(), k)
if err != nil {
t.Fatal(err)
}
}

func TestCantGet(t *testing.T) {
dsp := getDagservAndPinner(t)
a := &Node{Data: []byte("A")}

k, err := a.Key()
if err != nil {
t.Fatal(err)
}

_, err = dsp.ds.Get(context.TODO(), k)
if !strings.Contains(err.Error(), "not found") {
t.Fatal("expected err not found, got: ", err)
}
}
29 changes: 24 additions & 5 deletions merkledag/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error {
// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
n.encoded = nil
for i, l := range n.Links {
if l.Name == name {
n.Links = append(n.Links[:i], n.Links[i+1:]...)
return nil
good := make([]*Link, 0, len(n.Links))
var found bool

for _, l := range n.Links {
if l.Name != name {
good = append(good, l)
} else {
found = true
}
}
return ErrNotFound
n.Links = good

if !found {
return ErrNotFound
}

return nil
}

// Return a copy of the link with given name
Expand All @@ -153,6 +163,15 @@ func (n *Node) GetNodeLink(name string) (*Link, error) {
return nil, ErrNotFound
}

func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) {
lnk, err := n.GetNodeLink(name)
if err != nil {
return nil, err
}

return lnk.GetNode(ctx, ds)
}

// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
func (n *Node) Copy() *Node {
Expand Down
54 changes: 54 additions & 0 deletions merkledag/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package merkledag

import (
"testing"
)

func TestRemoveLink(t *testing.T) {
nd := &Node{
Links: []*Link{
&Link{Name: "a"},
&Link{Name: "b"},
&Link{Name: "a"},
&Link{Name: "a"},
&Link{Name: "c"},
&Link{Name: "a"},
},
}

err := nd.RemoveNodeLink("a")
if err != nil {
t.Fatal(err)
}

if len(nd.Links) != 2 {
t.Fatal("number of links incorrect")
}

if nd.Links[0].Name != "b" {
t.Fatal("link order wrong")
}

if nd.Links[1].Name != "c" {
t.Fatal("link order wrong")
}

// should fail
err = nd.RemoveNodeLink("a")
if err != ErrNotFound {
t.Fatal("should have failed to remove link")
}

// ensure nothing else got touched
if len(nd.Links) != 2 {
t.Fatal("number of links incorrect")
}

if nd.Links[0].Name != "b" {
t.Fatal("link order wrong")
}

if nd.Links[1].Name != "c" {
t.Fatal("link order wrong")
}
}
Loading

0 comments on commit e517b65

Please sign in to comment.