36533f7c3f
Fixes for new geth version
289 lines
8.1 KiB
Go
289 lines
8.1 KiB
Go
package io
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
mdag "github.com/ipfs/go-merkledag"
|
|
|
|
format "github.com/ipfs/go-unixfs"
|
|
hamt "github.com/ipfs/go-unixfs/hamt"
|
|
|
|
cid "github.com/ipfs/go-cid"
|
|
ipld "github.com/ipfs/go-ipld-format"
|
|
)
|
|
|
|
// UseHAMTSharding is a global flag that signifies whether or not to use the
|
|
// HAMT sharding scheme for directory creation
|
|
var UseHAMTSharding = false
|
|
|
|
// DefaultShardWidth is the default value used for hamt sharding width.
|
|
var DefaultShardWidth = 256
|
|
|
|
// Directory defines a UnixFS directory. It is used for creating, reading and
|
|
// editing directories. It allows to work with different directory schemes,
|
|
// like the basic or the HAMT implementation.
|
|
//
|
|
// It just allows to perform explicit edits on a single directory, working with
|
|
// directory trees is out of its scope, they are managed by the MFS layer
|
|
// (which is the main consumer of this interface).
|
|
type Directory interface {
|
|
|
|
// SetCidBuilder sets the CID Builder of the root node.
|
|
SetCidBuilder(cid.Builder)
|
|
|
|
// AddChild adds a (name, key) pair to the root node.
|
|
AddChild(context.Context, string, ipld.Node) error
|
|
|
|
// ForEachLink applies the given function to Links in the directory.
|
|
ForEachLink(context.Context, func(*ipld.Link) error) error
|
|
|
|
// EnumLinksAsync returns a channel which will receive Links in the directory
|
|
// as they are enumerated, where order is not gauranteed
|
|
EnumLinksAsync(context.Context) <-chan format.LinkResult
|
|
|
|
// Links returns the all the links in the directory node.
|
|
Links(context.Context) ([]*ipld.Link, error)
|
|
|
|
// Find returns the root node of the file named 'name' within this directory.
|
|
// In the case of HAMT-directories, it will traverse the tree.
|
|
Find(context.Context, string) (ipld.Node, error)
|
|
|
|
// RemoveChild removes the child with the given name.
|
|
RemoveChild(context.Context, string) error
|
|
|
|
// GetNode returns the root of this directory.
|
|
GetNode() (ipld.Node, error)
|
|
|
|
// GetCidBuilder returns the CID Builder used.
|
|
GetCidBuilder() cid.Builder
|
|
}
|
|
|
|
// TODO: Evaluate removing `dserv` from this layer and providing it in MFS.
|
|
// (The functions should in that case add a `DAGService` argument.)
|
|
|
|
// BasicDirectory is the basic implementation of `Directory`. All the entries
|
|
// are stored in a single node.
|
|
type BasicDirectory struct {
|
|
node *mdag.ProtoNode
|
|
dserv ipld.DAGService
|
|
}
|
|
|
|
// HAMTDirectory is the HAMT implementation of `Directory`.
|
|
// (See package `hamt` for more information.)
|
|
type HAMTDirectory struct {
|
|
shard *hamt.Shard
|
|
dserv ipld.DAGService
|
|
}
|
|
|
|
// NewDirectory returns a Directory. It needs a `DAGService` to add the children.
|
|
func NewDirectory(dserv ipld.DAGService) Directory {
|
|
if UseHAMTSharding {
|
|
dir := new(HAMTDirectory)
|
|
s, err := hamt.NewShard(dserv, DefaultShardWidth)
|
|
if err != nil {
|
|
panic(err) // will only panic if DefaultShardWidth is a bad value
|
|
}
|
|
dir.shard = s
|
|
dir.dserv = dserv
|
|
return dir
|
|
}
|
|
|
|
dir := new(BasicDirectory)
|
|
dir.node = format.EmptyDirNode()
|
|
dir.dserv = dserv
|
|
return dir
|
|
}
|
|
|
|
// ErrNotADir implies that the given node was not a unixfs directory
|
|
var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard")
|
|
|
|
// NewDirectoryFromNode loads a unixfs directory from the given IPLD node and
|
|
// DAGService.
|
|
func NewDirectoryFromNode(dserv ipld.DAGService, node ipld.Node) (Directory, error) {
|
|
protoBufNode, ok := node.(*mdag.ProtoNode)
|
|
if !ok {
|
|
return nil, ErrNotADir
|
|
}
|
|
|
|
fsNode, err := format.FSNodeFromBytes(protoBufNode.Data())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch fsNode.Type() {
|
|
case format.TDirectory:
|
|
return &BasicDirectory{
|
|
dserv: dserv,
|
|
node: protoBufNode.Copy().(*mdag.ProtoNode),
|
|
}, nil
|
|
case format.THAMTShard:
|
|
shard, err := hamt.NewHamtFromDag(dserv, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &HAMTDirectory{
|
|
dserv: dserv,
|
|
shard: shard,
|
|
}, nil
|
|
}
|
|
|
|
return nil, ErrNotADir
|
|
}
|
|
|
|
// SetCidBuilder implements the `Directory` interface.
|
|
func (d *BasicDirectory) SetCidBuilder(builder cid.Builder) {
|
|
d.node.SetCidBuilder(builder)
|
|
}
|
|
|
|
// AddChild implements the `Directory` interface. It adds (or replaces)
|
|
// a link to the given `node` under `name`.
|
|
func (d *BasicDirectory) AddChild(ctx context.Context, name string, node ipld.Node) error {
|
|
d.node.RemoveNodeLink(name)
|
|
// Remove old link (if it existed), don't check a potential `ErrNotFound`.
|
|
|
|
return d.node.AddNodeLink(name, node)
|
|
}
|
|
|
|
// EnumLinksAsync returns a channel which will receive Links in the directory
|
|
// as they are enumerated, where order is not gauranteed
|
|
func (d *BasicDirectory) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult {
|
|
linkResults := make(chan format.LinkResult)
|
|
go func() {
|
|
defer close(linkResults)
|
|
for _, l := range d.node.Links() {
|
|
select {
|
|
case linkResults <- format.LinkResult{
|
|
Link: l,
|
|
Err: nil,
|
|
}:
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return linkResults
|
|
}
|
|
|
|
// ForEachLink implements the `Directory` interface.
|
|
func (d *BasicDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error {
|
|
for _, l := range d.node.Links() {
|
|
if err := f(l); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Links implements the `Directory` interface.
|
|
func (d *BasicDirectory) Links(ctx context.Context) ([]*ipld.Link, error) {
|
|
return d.node.Links(), nil
|
|
}
|
|
|
|
// Find implements the `Directory` interface.
|
|
func (d *BasicDirectory) Find(ctx context.Context, name string) (ipld.Node, error) {
|
|
lnk, err := d.node.GetNodeLink(name)
|
|
if err == mdag.ErrLinkNotFound {
|
|
err = os.ErrNotExist
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return d.dserv.Get(ctx, lnk.Cid)
|
|
}
|
|
|
|
// RemoveChild implements the `Directory` interface.
|
|
func (d *BasicDirectory) RemoveChild(ctx context.Context, name string) error {
|
|
return d.node.RemoveNodeLink(name)
|
|
}
|
|
|
|
// GetNode implements the `Directory` interface.
|
|
func (d *BasicDirectory) GetNode() (ipld.Node, error) {
|
|
return d.node, nil
|
|
}
|
|
|
|
// GetCidBuilder implements the `Directory` interface.
|
|
func (d *BasicDirectory) GetCidBuilder() cid.Builder {
|
|
return d.node.CidBuilder()
|
|
}
|
|
|
|
// SwitchToSharding returns a HAMT implementation of this directory.
|
|
func (d *BasicDirectory) SwitchToSharding(ctx context.Context) (Directory, error) {
|
|
hamtDir := new(HAMTDirectory)
|
|
hamtDir.dserv = d.dserv
|
|
|
|
shard, err := hamt.NewShard(d.dserv, DefaultShardWidth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
shard.SetCidBuilder(d.node.CidBuilder())
|
|
hamtDir.shard = shard
|
|
|
|
for _, lnk := range d.node.Links() {
|
|
node, err := d.dserv.Get(ctx, lnk.Cid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = hamtDir.shard.Set(ctx, lnk.Name, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return hamtDir, nil
|
|
}
|
|
|
|
// SetCidBuilder implements the `Directory` interface.
|
|
func (d *HAMTDirectory) SetCidBuilder(builder cid.Builder) {
|
|
d.shard.SetCidBuilder(builder)
|
|
}
|
|
|
|
// AddChild implements the `Directory` interface.
|
|
func (d *HAMTDirectory) AddChild(ctx context.Context, name string, nd ipld.Node) error {
|
|
return d.shard.Set(ctx, name, nd)
|
|
}
|
|
|
|
// ForEachLink implements the `Directory` interface.
|
|
func (d *HAMTDirectory) ForEachLink(ctx context.Context, f func(*ipld.Link) error) error {
|
|
return d.shard.ForEachLink(ctx, f)
|
|
}
|
|
|
|
// EnumLinksAsync returns a channel which will receive Links in the directory
|
|
// as they are enumerated, where order is not gauranteed
|
|
func (d *HAMTDirectory) EnumLinksAsync(ctx context.Context) <-chan format.LinkResult {
|
|
return d.shard.EnumLinksAsync(ctx)
|
|
}
|
|
|
|
// Links implements the `Directory` interface.
|
|
func (d *HAMTDirectory) Links(ctx context.Context) ([]*ipld.Link, error) {
|
|
return d.shard.EnumLinks(ctx)
|
|
}
|
|
|
|
// Find implements the `Directory` interface. It will traverse the tree.
|
|
func (d *HAMTDirectory) Find(ctx context.Context, name string) (ipld.Node, error) {
|
|
lnk, err := d.shard.Find(ctx, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return lnk.GetNode(ctx, d.dserv)
|
|
}
|
|
|
|
// RemoveChild implements the `Directory` interface.
|
|
func (d *HAMTDirectory) RemoveChild(ctx context.Context, name string) error {
|
|
return d.shard.Remove(ctx, name)
|
|
}
|
|
|
|
// GetNode implements the `Directory` interface.
|
|
func (d *HAMTDirectory) GetNode() (ipld.Node, error) {
|
|
return d.shard.Node()
|
|
}
|
|
|
|
// GetCidBuilder implements the `Directory` interface.
|
|
func (d *HAMTDirectory) GetCidBuilder() cid.Builder {
|
|
return d.shard.CidBuilder()
|
|
}
|