From 26f42c4c46c0b4d1465e6a086b02de52941891d5 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Wed, 19 Oct 2016 10:05:58 -0700 Subject: [PATCH] make path resolver no longer require whole node for construction License: MIT Signed-off-by: Jeromy --- core/builder.go | 2 +- core/commands/files/files.go | 9 +++++-- core/commands/get.go | 2 +- core/commands/ls.go | 14 +++++++++- core/commands/object/diff.go | 4 +-- core/commands/object/object.go | 8 +++--- core/commands/object/patch.go | 10 +++---- core/commands/pin.go | 2 +- core/commands/publish.go | 2 +- core/commands/refs.go | 9 +++++-- core/commands/resolve.go | 2 +- core/commands/tar.go | 2 +- core/commands/unixfs/ls.go | 9 ++++++- core/core.go | 4 +-- core/corehttp/gateway_handler.go | 23 ++++++++++++---- core/corerepo/pinning.go | 7 ++++- core/coreunix/cat.go | 2 +- core/pathresolver.go | 13 ++++----- core/pathresolver_test.go | 6 ++--- fuse/ipns/ipns_unix.go | 2 +- path/resolver.go | 34 ++++++++++++++++------- path/resolver_test.go | 2 +- unixfs/io/resolve.go | 46 ++++++++++++++++++++++++++++++++ 23 files changed, 162 insertions(+), 52 deletions(-) create mode 100644 unixfs/io/resolve.go diff --git a/core/builder.go b/core/builder.go index 4ddde171d95e..451e771d32c0 100644 --- a/core/builder.go +++ b/core/builder.go @@ -214,7 +214,7 @@ func setupNode(ctx context.Context, n *IpfsNode, cfg *BuildCfg) error { // this is kinda sketchy and could cause data loss n.Pinning = pin.NewPinner(n.Repo.Datastore(), n.DAG, internalDag) } - n.Resolver = &path.Resolver{DAG: n.DAG} + n.Resolver = path.NewBasicResolver(n.DAG) err = n.loadFilesRoot() if err != nil { diff --git a/core/commands/files/files.go b/core/commands/files/files.go index 2445dfd388a4..9942239af3f3 100644 --- a/core/commands/files/files.go +++ b/core/commands/files/files.go @@ -16,6 +16,7 @@ import ( mfs "github.com/ipfs/go-ipfs/mfs" path "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" @@ -259,11 +260,15 @@ func getNodeFromPath(ctx context.Context, node *core.IpfsNode, p string) (node.N return nil, err } - nd, err := core.Resolve(ctx, node, np) + resolver := &path.Resolver{ + DAG: node.DAG, + ResolveOnce: uio.ResolveUnixfsOnce, + } + + nd, err := core.Resolve(ctx, node.Namesys, resolver, np) if err != nil { return nil, err } - pbnd, ok := nd.(*dag.ProtoNode) if !ok { return nil, dag.ErrNotProtobuf diff --git a/core/commands/get.go b/core/commands/get.go index c299336ef9cb..62a040024981 100644 --- a/core/commands/get.go +++ b/core/commands/get.go @@ -64,7 +64,7 @@ may also specify the level of compression by specifying '-l=<1-9>'. } p := path.Path(req.Arguments()[0]) ctx := req.Context() - dn, err := core.Resolve(ctx, node, p) + dn, err := core.Resolve(ctx, node.Namesys, node.Resolver, p) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/ls.go b/core/commands/ls.go index e2546699fa71..17301a7ce787 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -11,6 +11,7 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" unixfs "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" unixfspb "github.com/ipfs/go-ipfs/unixfs/pb" node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" @@ -74,7 +75,18 @@ The JSON output contains type information. var dagnodes []node.Node for _, fpath := range paths { - dagnode, err := core.Resolve(req.Context(), nd, path.Path(fpath)) + p, err := path.ParsePath(fpath) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + r := &path.Resolver{ + DAG: nd.DAG, + ResolveOnce: uio.ResolveUnixfsOnce, + } + + dagnode, err := core.Resolve(req.Context(), nd.Namesys, r, p) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/object/diff.go b/core/commands/object/diff.go index 69697ef3edd7..c435892c2f39 100644 --- a/core/commands/object/diff.go +++ b/core/commands/object/diff.go @@ -74,13 +74,13 @@ Example: ctx := req.Context() - obj_a, err := core.Resolve(ctx, node, pa) + obj_a, err := core.Resolve(ctx, node.Namesys, node.Resolver, pa) if err != nil { res.SetError(err, cmds.ErrNormal) return } - obj_b, err := core.Resolve(ctx, node, pb) + obj_b, err := core.Resolve(ctx, node.Namesys, node.Resolver, pb) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/object/object.go b/core/commands/object/object.go index 6f2f2d291224..9c4f6aeb0972 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -94,7 +94,7 @@ is the raw data of the object. return } - node, err := core.Resolve(req.Context(), n, fpath) + node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -140,7 +140,7 @@ multihash. } fpath := path.Path(req.Arguments()[0]) - node, err := core.Resolve(req.Context(), n, fpath) + node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -204,7 +204,7 @@ This command outputs data in the following encodings: fpath := path.Path(req.Arguments()[0]) - object, err := core.Resolve(req.Context(), n, fpath) + object, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -277,7 +277,7 @@ var ObjectStatCmd = &cmds.Command{ fpath := path.Path(req.Arguments()[0]) - object, err := core.Resolve(req.Context(), n, fpath) + object, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, fpath) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/object/patch.go b/core/commands/object/patch.go index 5b933e9346c5..f76c95d750e8 100644 --- a/core/commands/object/patch.go +++ b/core/commands/object/patch.go @@ -73,7 +73,7 @@ the limit will not be respected by the network. return } - rootnd, err := core.Resolve(req.Context(), nd, root) + rootnd, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, root) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -141,7 +141,7 @@ Example: return } - root, err := core.Resolve(req.Context(), nd, rp) + root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rp) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -205,7 +205,7 @@ Removes a link by the given name from root. return } - root, err := core.Resolve(req.Context(), nd, rootp) + root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rootp) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -280,7 +280,7 @@ to a file containing 'bar', and returns the hash of the new object. return } - root, err := core.Resolve(req.Context(), nd, rootp) + root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, rootp) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -312,7 +312,7 @@ to a file containing 'bar', and returns the hash of the new object. e := dagutils.NewDagEditor(rtpb, nd.DAG) - childnd, err := core.Resolve(req.Context(), nd, childp) + childnd, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, childp) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/pin.go b/core/commands/pin.go index ef10fbdb1f23..43d3d2533392 100644 --- a/core/commands/pin.go +++ b/core/commands/pin.go @@ -277,7 +277,7 @@ func pinLsKeys(args []string, typeStr string, ctx context.Context, n *core.IpfsN return nil, err } - dagNode, err := core.Resolve(ctx, n, pth) + dagNode, err := core.Resolve(ctx, n.Namesys, n.Resolver, pth) if err != nil { return nil, err } diff --git a/core/commands/publish.go b/core/commands/publish.go index 2bdfd5437b5c..fbecf3bf630c 100644 --- a/core/commands/publish.go +++ b/core/commands/publish.go @@ -135,7 +135,7 @@ func publish(ctx context.Context, n *core.IpfsNode, k crypto.PrivKey, ref path.P if opts.verifyExists { // verify the path exists - _, err := core.Resolve(ctx, n, ref) + _, err := core.Resolve(ctx, n.Namesys, n.Resolver, ref) if err != nil { return nil, err } diff --git a/core/commands/refs.go b/core/commands/refs.go index 3cd23291596c..f7ece128ef99 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -198,8 +198,13 @@ var refsMarshallerMap = cmds.MarshalerMap{ func objectsForPaths(ctx context.Context, n *core.IpfsNode, paths []string) ([]node.Node, error) { objects := make([]node.Node, len(paths)) - for i, p := range paths { - o, err := core.Resolve(ctx, n, path.Path(p)) + for i, sp := range paths { + p, err := path.ParsePath(sp) + if err != nil { + return nil, err + } + + o, err := core.Resolve(ctx, n.Namesys, n.Resolver, p) if err != nil { return nil, err } diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 95840b4b96fd..0c87fe87b47f 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -99,7 +99,7 @@ Resolve the value of an IPFS DAG path: return } - node, err := core.Resolve(req.Context(), n, p) + node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, p) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/tar.go b/core/commands/tar.go index 00b757549caf..129aa1c2c36e 100644 --- a/core/commands/tar.go +++ b/core/commands/tar.go @@ -95,7 +95,7 @@ var tarCatCmd = &cmds.Command{ return } - root, err := core.Resolve(req.Context(), nd, p) + root, err := core.Resolve(req.Context(), nd.Namesys, nd.Resolver, p) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands/unixfs/ls.go b/core/commands/unixfs/ls.go index 939edf2f5894..1fdb81ee9546 100644 --- a/core/commands/unixfs/ls.go +++ b/core/commands/unixfs/ls.go @@ -12,6 +12,7 @@ import ( merkledag "github.com/ipfs/go-ipfs/merkledag" path "github.com/ipfs/go-ipfs/path" unixfs "github.com/ipfs/go-ipfs/unixfs" + uio "github.com/ipfs/go-ipfs/unixfs/io" unixfspb "github.com/ipfs/go-ipfs/unixfs/pb" ) @@ -87,7 +88,13 @@ possible, please use 'ipfs ls' instead. for _, fpath := range paths { ctx := req.Context() - merkleNode, err := core.Resolve(ctx, node, path.Path(fpath)) + + resolver := &path.Resolver{ + DAG: node.DAG, + ResolveOnce: uio.ResolveUnixfsOnce, + } + + merkleNode, err := core.Resolve(ctx, node.Namesys, resolver, path.Path(fpath)) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/core.go b/core/core.go index 46e0d954bd17..e4a8205f7927 100644 --- a/core/core.go +++ b/core/core.go @@ -60,7 +60,7 @@ import ( pin "github.com/ipfs/go-ipfs/pin" repo "github.com/ipfs/go-ipfs/repo" config "github.com/ipfs/go-ipfs/repo/config" - uio "github.com/ipfs/go-ipfs/unixfs/io" + ft "github.com/ipfs/go-ipfs/unixfs" u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util" ) @@ -504,7 +504,7 @@ func (n *IpfsNode) loadFilesRoot() error { switch { case err == ds.ErrNotFound || val == nil: - nd = uio.NewEmptyDirectory() + nd = ft.EmptyDirNode() _, err := n.DAG.Add(nd) if err != nil { return fmt.Errorf("failure writing to dagstore: %s", err) diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index 3e71129dcf64..b840a2577bb2 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -17,6 +17,7 @@ import ( 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" uio "github.com/ipfs/go-ipfs/unixfs/io" routing "gx/ipfs/QmNUgVQTYnXQVrGT2rajZYsuKV8GYdiL91cdZSQDKNPNgE/go-libp2p-routing" @@ -153,7 +154,13 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request ipnsHostname = true } - nd, err := core.Resolve(ctx, i.node, path.Path(urlPath)) + p, err := path.ParsePath(urlPath) + if err != nil { + webError(w, "Invalid Path Error", err, http.StatusBadRequest) + return + } + + nd, err := core.Resolve(ctx, i.node.Namesys, i.node.Resolver, p) // If node is in offline mode the error code and message should be different if err == core.ErrNoNamesys && !i.node.OnlineMode() { w.WriteHeader(http.StatusServiceUnavailable) @@ -240,8 +247,14 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request return } + p, err := path.ParsePath(urlPath + "/index.html") + if err != nil { + internalWebError(w, err) + return + } + // return index page instead. - nd, err := core.Resolve(ctx, i.node, path.Path(urlPath+"/index.html")) + nd, err := core.Resolve(ctx, i.node.Namesys, i.node.Resolver, p) if err != nil { internalWebError(w, err) return @@ -356,7 +369,7 @@ func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { var newnode node.Node if rsegs[len(rsegs)-1] == "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" { - newnode = uio.NewEmptyDirectory() + newnode = ft.EmptyDirNode() } else { putNode, err := i.newDagFromReader(r.Body) if err != nil { @@ -372,7 +385,7 @@ func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { } var newcid *cid.Cid - rnode, err := core.Resolve(ctx, i.node, rootPath) + rnode, err := core.Resolve(ctx, i.node.Namesys, i.node.Resolver, rootPath) switch ev := err.(type) { case path.ErrNoLink: // ev.Node < node where resolve failed @@ -397,7 +410,7 @@ func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { } e := dagutils.NewDagEditor(pbnd, i.node.DAG) - err = e.InsertNodeAtPath(ctx, newPath, newnode, uio.NewEmptyDirectory) + err = e.InsertNodeAtPath(ctx, newPath, newnode, ft.EmptyDirNode) if err != nil { webError(w, "putHandler: InsertNodeAtPath failed", err, http.StatusInternalServerError) return diff --git a/core/corerepo/pinning.go b/core/corerepo/pinning.go index 41ade74e94ab..dfeca5566b8f 100644 --- a/core/corerepo/pinning.go +++ b/core/corerepo/pinning.go @@ -27,7 +27,12 @@ import ( func Pin(n *core.IpfsNode, ctx context.Context, paths []string, recursive bool) ([]*cid.Cid, error) { dagnodes := make([]node.Node, 0) for _, fpath := range paths { - dagnode, err := core.Resolve(ctx, n, path.Path(fpath)) + p, err := path.ParsePath(fpath) + if err != nil { + return nil, err + } + + dagnode, err := core.Resolve(ctx, n.Namesys, n.Resolver, p) if err != nil { return nil, fmt.Errorf("pin: %s", err) } diff --git a/core/coreunix/cat.go b/core/coreunix/cat.go index 38fbc157a3b7..1f102fbcf24c 100644 --- a/core/coreunix/cat.go +++ b/core/coreunix/cat.go @@ -9,7 +9,7 @@ import ( ) func Cat(ctx context.Context, n *core.IpfsNode, pstr string) (*uio.DagReader, error) { - dagNode, err := core.Resolve(ctx, n, path.Path(pstr)) + dagNode, err := core.Resolve(ctx, n.Namesys, n.Resolver, path.Path(pstr)) if err != nil { return nil, err } diff --git a/core/pathresolver.go b/core/pathresolver.go index 8bcf011695fe..f0632c77ea16 100644 --- a/core/pathresolver.go +++ b/core/pathresolver.go @@ -5,6 +5,7 @@ import ( "errors" "strings" + namesys "github.com/ipfs/go-ipfs/namesys" path "github.com/ipfs/go-ipfs/path" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" @@ -18,13 +19,13 @@ var ErrNoNamesys = errors.New( // Resolve resolves the given path by parsing out protocol-specific // entries (e.g. /ipns/) and then going through the /ipfs/ -// entries and returning the final merkledag node. -func Resolve(ctx context.Context, n *IpfsNode, p path.Path) (node.Node, error) { +// entries and returning the final node. +func Resolve(ctx context.Context, nsys namesys.NameSystem, r *path.Resolver, p path.Path) (node.Node, error) { if strings.HasPrefix(p.String(), "/ipns/") { // resolve ipns paths // TODO(cryptix): we sould be able to query the local cache for the path - if n.Namesys == nil { + if nsys == nil { return nil, ErrNoNamesys } @@ -40,7 +41,7 @@ func Resolve(ctx context.Context, n *IpfsNode, p path.Path) (node.Node, error) { return nil, err } - respath, err := n.Namesys.Resolve(ctx, resolvable.String()) + respath, err := nsys.Resolve(ctx, resolvable.String()) if err != nil { return nil, err } @@ -53,7 +54,7 @@ func Resolve(ctx context.Context, n *IpfsNode, p path.Path) (node.Node, error) { } // ok, we have an ipfs path now (or what we'll treat as one) - return n.Resolver.ResolvePath(ctx, p) + return r.ResolvePath(ctx, p) } // ResolveToKey resolves a path to a key. @@ -76,7 +77,7 @@ func ResolveToCid(ctx context.Context, n *IpfsNode, p path.Path) (*cid.Cid, erro if err != nil { return nil, err } - dagnode, err := Resolve(ctx, n, head) + dagnode, err := Resolve(ctx, n.Namesys, n.Resolver, head) if err != nil { return nil, err } diff --git a/core/pathresolver_test.go b/core/pathresolver_test.go index 9db0bfbeb184..3f5f5a25e7f1 100644 --- a/core/pathresolver_test.go +++ b/core/pathresolver_test.go @@ -14,17 +14,17 @@ func TestResolveNoComponents(t *testing.T) { t.Fatal("Should have constructed a mock node", err) } - _, err = core.Resolve(n.Context(), n, path.Path("/ipns/")) + _, err = core.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipns/")) if err != path.ErrNoComponents { t.Fatal("Should error with no components (/ipns/).", err) } - _, err = core.Resolve(n.Context(), n, path.Path("/ipfs/")) + _, err = core.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/ipfs/")) if err != path.ErrNoComponents { t.Fatal("Should error with no components (/ipfs/).", err) } - _, err = core.Resolve(n.Context(), n, path.Path("/../..")) + _, err = core.Resolve(n.Context(), n.Namesys, n.Resolver, path.Path("/../..")) if err != path.ErrBadPath { t.Fatal("Should error with invalid path.", err) } diff --git a/fuse/ipns/ipns_unix.go b/fuse/ipns/ipns_unix.go index aea9d88eb54e..106a3987becb 100644 --- a/fuse/ipns/ipns_unix.go +++ b/fuse/ipns/ipns_unix.go @@ -94,7 +94,7 @@ func loadRoot(ctx context.Context, rt *keyRoot, ipfs *core.IpfsNode, name string return nil, err } - node, err := core.Resolve(ctx, ipfs, p) + node, err := core.Resolve(ctx, ipfs.Namesys, ipfs.Resolver, p) if err != nil { log.Errorf("looking up %s: %s", p, err) return nil, err diff --git a/path/resolver.go b/path/resolver.go index e4bfe8f796cd..187cac92ade1 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - merkledag "github.com/ipfs/go-ipfs/merkledag" + dag "github.com/ipfs/go-ipfs/merkledag" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log" cid "gx/ipfs/QmXUuRadqDq5BuFWzVU6VuKaSjTcNm1gNCtLvvP1TJCW4z/go-cid" @@ -32,8 +32,19 @@ func (e ErrNoLink) Error() string { // Resolver provides path resolution to IPFS // It has a pointer to a DAGService, which is uses to resolve nodes. +// TODO: now that this is more modular, try to unify this code with the +// the resolvers in namesys type Resolver struct { - DAG merkledag.DAGService + DAG dag.DAGService + + ResolveOnce func(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) +} + +func NewBasicResolver(ds dag.DAGService) *Resolver { + return &Resolver{ + DAG: ds, + ResolveOnce: ResolveSingle, + } } // SplitAbsPath clean up and split fpath. It extracts the first component (which @@ -53,7 +64,9 @@ func SplitAbsPath(fpath Path) (*cid.Cid, []string, error) { } c, err := cid.Decode(parts[0]) + // first element in the path is a cid if err != nil { + log.Debug("given path element is not a cid.\n") return nil, nil, err } @@ -75,6 +88,11 @@ func (s *Resolver) ResolvePath(ctx context.Context, fpath Path) (node.Node, erro return nodes[len(nodes)-1], err } +func ResolveSingle(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { + lnk, _, err := nd.Resolve([]string{name}) + return lnk, err +} + // ResolvePathComponents fetches the nodes for each segment of the given path. // It uses the first path component as a hash (key) of the first node, then // resolves all other components walking the links, with ResolveLinks. @@ -107,26 +125,24 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd node.Node, names []stri nd := ndd // dup arg workaround // for each of the path components - for len(names) > 0 { + for _, name := range names { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, time.Minute) defer cancel() - lnk, rest, err := nd.Resolve(names) - if err == merkledag.ErrLinkNotFound { - n := nd.Cid() - return result, ErrNoLink{Name: names[0], Node: n} + lnk, err := s.ResolveOnce(ctx, s.DAG, nd, name) + if err == dag.ErrLinkNotFound { + return result, ErrNoLink{Name: name, Node: nd.Cid()} } else if err != nil { return result, err } - nextnode, err := s.DAG.Get(ctx, lnk.Cid) + nextnode, err := lnk.GetNode(ctx, s.DAG) if err != nil { return result, err } nd = nextnode - names = rest result = append(result, nextnode) } return result, nil diff --git a/path/resolver_test.go b/path/resolver_test.go index 652f38796535..cf6ef92145d5 100644 --- a/path/resolver_test.go +++ b/path/resolver_test.go @@ -55,7 +55,7 @@ func TestRecurivePathResolution(t *testing.T) { t.Fatal(err) } - resolver := &path.Resolver{DAG: dagService} + resolver := path.NewBasicResolver(dagService) node, err := resolver.ResolvePath(ctx, p) if err != nil { t.Fatal(err) diff --git a/unixfs/io/resolve.go b/unixfs/io/resolve.go new file mode 100644 index 000000000000..d1b3a16bd482 --- /dev/null +++ b/unixfs/io/resolve.go @@ -0,0 +1,46 @@ +package io + +import ( + "context" + + dag "github.com/ipfs/go-ipfs/merkledag" + ft "github.com/ipfs/go-ipfs/unixfs" + + node "gx/ipfs/QmZx42H5khbVQhV5odp66TApShV4XCujYazcvYduZ4TroB/go-ipld-node" +) + +func ResolveUnixfsOnce(ctx context.Context, ds dag.DAGService, nd node.Node, name string) (*node.Link, error) { + pbnd, ok := nd.(*dag.ProtoNode) + if !ok { + lnk, _, err := nd.Resolve([]string{name}) + return lnk, err + } + + upb, err := ft.FromBytes(pbnd.Data()) + if err != nil { + // Not a unixfs node, use standard object traversal code + lnk, _, err := nd.Resolve([]string{name}) + return lnk, err + } + + switch upb.GetType() { + /* + case ft.THAMTShard: + s, err := hamt.NewHamtFromDag(ds, nd) + if err != nil { + return nil, err + } + + // TODO: optimized routine on HAMT for returning a dag.Link to avoid extra disk hits + out, err := s.Find(ctx, name) + if err != nil { + return nil, err + } + + return dag.MakeLink(out) + */ + default: + lnk, _, err := nd.Resolve([]string{name}) + return lnk, err + } +}