diff --git a/core/commands/object.go b/core/commands/object.go index d30c5d1f2..bb066a740 100644 --- a/core/commands/object.go +++ b/core/commands/object.go @@ -587,13 +587,17 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) { return "", err } - path := strings.Split(req.Arguments()[2], "/") + path := req.Arguments()[2] - nnode, err := dagutils.RmLink(req.Context(), nd.DAG, root, path) + e := dagutils.NewDagEditor(nd.DAG, root) + + err = e.RmLink(req.Context(), path) if err != nil { return "", err } + nnode := e.GetNode() + return nnode.Key() } @@ -610,17 +614,27 @@ 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, "/") - create, _, err := req.Option("create").Bool() if err != nil { return "", err } - nnode, err := dagutils.InsertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create) + var createfunc func() *dag.Node + if create { + createfunc = func() *dag.Node { + return &dag.Node{Data: ft.FolderPBData()} + } + } + + e := dagutils.NewDagEditor(nd.DAG, root) + + err = e.InsertNodeAtPath(req.Context(), path, childk, createfunc) if err != nil { return "", err } + + nnode := e.GetNode() + return nnode.Key() } diff --git a/merkledag/node.go b/merkledag/node.go index e90ac95e8..8d06077c0 100644 --- a/merkledag/node.go +++ b/merkledag/node.go @@ -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 diff --git a/merkledag/utils/utils.go b/merkledag/utils/utils.go index e82d00229..6ab612c17 100644 --- a/merkledag/utils/utils.go +++ b/merkledag/utils/utils.go @@ -2,22 +2,44 @@ package dagutils import ( "errors" - "time" + "strings" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" key "github.com/ipfs/go-ipfs/blocks/key" dag "github.com/ipfs/go-ipfs/merkledag" - ft "github.com/ipfs/go-ipfs/unixfs" ) -func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { +type Editor struct { + root *dag.Node + ds dag.DAGService +} + +func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor { + return &Editor{ + root: root, + ds: ds, + } +} + +func (e *Editor) GetNode() *dag.Node { + return e.root.Copy() +} + +func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error { + nd, err := addLink(ctx, e.ds, e.root, childname, childk) + if err != nil { + return err + } + e.root = nd + return nil +} + +func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) { if childname == "" { return nil, errors.New("cannot create link with no name!") } - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() childnd, err := ds.Get(ctx, childk) if err != nil { return nil, err @@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s return root, nil } -func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) { +func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error { + splpath := strings.Split(path, "/") + nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create) + if err != nil { + return err + } + e.root = nd + return nil +} + +func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) { if len(path) == 1 { - return AddLink(ctx, ds, root, path[0], toinsert) + return addLink(ctx, ds, root, path[0], toinsert) } nd, err := root.GetLinkedNode(ctx, ds, path[0]) if err != nil { // if 'create' is true, we create directories on the way down as needed - if err == dag.ErrNotFound && create { - nd = &dag.Node{Data: ft.FolderPBData()} + if err == dag.ErrNotFound && create != nil { + nd = create() } else { return nil, err } } - ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) + ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create) if err != nil { return nil, err } @@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa return root, nil } -func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { +func (e *Editor) RmLink(ctx context.Context, path string) error { + splpath := strings.Split(path, "/") + nd, err := rmLink(ctx, e.ds, e.root, splpath) + if err != nil { + return err + } + e.root = nd + return nil +} + +func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) { if len(path) == 1 { // base case, remove node in question err := root.RemoveNodeLink(path[0]) @@ -93,7 +135,7 @@ func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin return nil, err } - nnode, err := RmLink(ctx, ds, nd, path[1:]) + nnode, err := rmLink(ctx, ds, nd, path[1:]) if err != nil { return nil, err } diff --git a/merkledag/utils/utils_test.go b/merkledag/utils/utils_test.go index 36da81687..39b1a519d 100644 --- a/merkledag/utils/utils_test.go +++ b/merkledag/utils/utils_test.go @@ -1,6 +1,7 @@ package dagutils import ( + "strings" "testing" key "github.com/ipfs/go-ipfs/blocks/key" @@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) { } nd := new(dag.Node) - nnode, err := AddLink(context.Background(), ds, nd, "fish", fk) + nnode, err := addLink(context.Background(), ds, nd, "fish", fk) if err != nil { t.Fatal(err) } @@ -42,9 +43,10 @@ func TestAddLink(t *testing.T) { } } -func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) { +func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) { + parts := strings.Split(path, "/") cur := root - for _, e := range path { + for _, e := range parts { nxt, err := cur.GetLinkedNode(context.Background(), ds, e) if err != nil { t.Fatal(err) @@ -66,53 +68,57 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []st func TestInsertNode(t *testing.T) { ds := mdtest.Mock(t) root := new(dag.Node) + e := NewDagEditor(ds, root) - childa := &dag.Node{ - Data: []byte("This is child A"), - } - ak, err := ds.Add(childa) + testInsert(t, e, "a", "anodefortesting", false, "") + testInsert(t, e, "a/b", "data", false, "") + testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found") + testInsert(t, e, "a/b/c/d/e", "foo", true, "") + testInsert(t, e, "a/b/c/d/f", "baz", true, "") + testInsert(t, e, "a/b/c/d/f", "bar", true, "") + + testInsert(t, e, "", "bar", true, "cannot create link with no name!") + testInsert(t, e, "////", "slashes", true, "cannot create link with no name!") + + k, err := e.GetNode().Key() if err != nil { t.Fatal(err) } - path := []string{"a", "b", "c", "d"} - root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true) - if err != nil { - t.Fatal(err) + if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" { + t.Fatal("output was different than expected") } - assertNodeAtPath(t, ds, root_a, path, ak) - - childb := &dag.Node{Data: []byte("this is the second child")} - bk, err := ds.Add(childb) - if err != nil { - t.Fatal(err) - } - - // this one should fail, we are specifying a non-existant path - // with create == false - path2 := []string{"a", "b", "e", "f"} - _, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false) - if err == nil { - t.Fatal("that shouldnt have worked") - } - if err != dag.ErrNotFound { - t.Fatal("expected this to fail with 'not found'") - } - - // inserting a path of length one should work with create == false - path3 := []string{"x"} - root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false) - if err != nil { - t.Fatal(err) - } - - assertNodeAtPath(t, ds, root_b, path3, bk) - - // now try overwriting a path - root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false) - if err != nil { - t.Fatal(err) - } - - assertNodeAtPath(t, ds, root_c, path, bk) +} + +func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) { + child := &dag.Node{Data: []byte(data)} + ck, err := e.ds.Add(child) + if err != nil { + t.Fatal(err) + } + + var c func() *dag.Node + if create { + c = func() *dag.Node { + return &dag.Node{} + } + } + + err = e.InsertNodeAtPath(context.TODO(), path, ck, c) + if experr != "" { + var got string + if err != nil { + got = err.Error() + } + if got != experr { + t.Fatalf("expected '%s' but got '%s'", experr, got) + } + return + } + + if err != nil { + t.Fatal(err) + } + + assertNodeAtPath(t, e.ds, e.root, path, ck) }