dirbuilder.go 4.32 KB
Newer Older
1 2 3
package io

import (
4
	"context"
5 6
	"fmt"
	"os"
Jeromy's avatar
Jeromy committed
7

8 9
	mdag "github.com/ipfs/go-ipfs/merkledag"
	format "github.com/ipfs/go-ipfs/unixfs"
10
	hamt "github.com/ipfs/go-ipfs/unixfs/hamt"
11
	cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
12

13
	node "gx/ipfs/QmPN7cwmpcc4DWXb4KTB9dNAJgjuPY69h3npsMfhRrQL9c/go-ipld-format"
14 15
)

16 17 18 19 20
// ShardSplitThreshold specifies how large of an unsharded directory
// the Directory code will generate. Adding entries over this value will
// result in the node being restructured into a sharded object.
var ShardSplitThreshold = 1000

Jeromy's avatar
Jeromy committed
21 22 23 24
// UseHAMTSharding is a global flag that signifies whether or not to use the
// HAMT sharding scheme for directory creation
var UseHAMTSharding = false

25 26 27 28
// DefaultShardWidth is the default value used for hamt sharding width.
var DefaultShardWidth = 256

type Directory struct {
29
	dserv   mdag.DAGService
30
	dirnode *mdag.ProtoNode
31

32
	shard *hamt.HamtShard
Henry's avatar
Henry committed
33 34
}

35 36 37
// NewDirectory returns a Directory. It needs a DAGService to add the Children
func NewDirectory(dserv mdag.DAGService) *Directory {
	db := new(Directory)
38
	db.dserv = dserv
Jeromy's avatar
Jeromy committed
39 40 41 42 43 44 45 46 47
	if UseHAMTSharding {
		s, err := hamt.NewHamtShard(dserv, DefaultShardWidth)
		if err != nil {
			panic(err) // will only panic if DefaultShardWidth is a bad value
		}
		db.shard = s
	} else {
		db.dirnode = format.EmptyDirNode()
	}
48 49 50
	return db
}

Jeromy's avatar
Jeromy committed
51
// ErrNotADir implies that the given node was not a unixfs directory
52 53
var ErrNotADir = fmt.Errorf("merkledag node was not a directory or shard")

54 55 56
func NewDirectoryFromNode(dserv mdag.DAGService, nd node.Node) (*Directory, error) {
	pbnd, ok := nd.(*mdag.ProtoNode)
	if !ok {
57
		return nil, ErrNotADir
58 59 60
	}

	pbd, err := format.FromBytes(pbnd.Data())
61
	if err != nil {
62
		return nil, err
63 64
	}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
	switch pbd.GetType() {
	case format.TDirectory:
		return &Directory{
			dserv:   dserv,
			dirnode: pbnd.Copy().(*mdag.ProtoNode),
		}, nil
	case format.THAMTShard:
		shard, err := hamt.NewHamtFromDag(dserv, nd)
		if err != nil {
			return nil, err
		}

		return &Directory{
			dserv: dserv,
			shard: shard,
		}, nil
	default:
82
		return nil, ErrNotADir
83
	}
84
}
85

86
// SetPrefix sets the prefix of the root node
87
func (d *Directory) SetPrefix(prefix *cid.Prefix) {
88 89 90
	if d.dirnode != nil {
		d.dirnode.SetPrefix(prefix)
	}
91 92 93
	if d.shard != nil {
		d.shard.SetPrefix(prefix)
	}
94 95
}

96 97 98
// AddChild adds a (name, key)-pair to the root node.
func (d *Directory) AddChild(ctx context.Context, name string, nd node.Node) error {
	if d.shard == nil {
Jeromy's avatar
Jeromy committed
99
		if !UseHAMTSharding {
100 101 102 103 104 105 106 107 108 109 110
			_ = d.dirnode.RemoveNodeLink(name)
			return d.dirnode.AddNodeLinkClean(name, nd)
		}

		err := d.switchToSharding(ctx)
		if err != nil {
			return err
		}
	}

	return d.shard.Set(ctx, name, nd)
111 112
}

113
func (d *Directory) switchToSharding(ctx context.Context) error {
114 115 116 117
	s, err := hamt.NewHamtShard(d.dserv, DefaultShardWidth)
	if err != nil {
		return err
	}
118
	s.SetPrefix(&d.dirnode.Prefix)
119 120

	d.shard = s
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
	for _, lnk := range d.dirnode.Links() {
		cnd, err := d.dserv.Get(ctx, lnk.Cid)
		if err != nil {
			return err
		}

		err = d.shard.Set(ctx, lnk.Name, cnd)
		if err != nil {
			return err
		}
	}

	d.dirnode = nil
	return nil
}

Jeromy's avatar
Jeromy committed
137
func (d *Directory) ForEachLink(ctx context.Context, f func(*node.Link) error) error {
138 139 140 141 142 143 144 145 146
	if d.shard == nil {
		for _, l := range d.dirnode.Links() {
			if err := f(l); err != nil {
				return err
			}
		}
		return nil
	}

Jeromy's avatar
Jeromy committed
147
	return d.shard.ForEachLink(ctx, f)
148 149
}

Jeromy's avatar
Jeromy committed
150
func (d *Directory) Links(ctx context.Context) ([]*node.Link, error) {
151 152 153 154
	if d.shard == nil {
		return d.dirnode.Links(), nil
	}

Jeromy's avatar
Jeromy committed
155
	return d.shard.EnumLinks(ctx)
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
}

func (d *Directory) Find(ctx context.Context, name string) (node.Node, error) {
	if d.shard == nil {
		lnk, err := d.dirnode.GetNodeLink(name)
		switch err {
		case mdag.ErrLinkNotFound:
			return nil, os.ErrNotExist
		default:
			return nil, err
		case nil:
		}

		return d.dserv.Get(ctx, lnk.Cid)
	}

Jeromy's avatar
Jeromy committed
172 173 174 175 176 177
	lnk, err := d.shard.Find(ctx, name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, d.dserv)
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
}

func (d *Directory) RemoveChild(ctx context.Context, name string) error {
	if d.shard == nil {
		return d.dirnode.RemoveNodeLink(name)
	}

	return d.shard.Remove(ctx, name)
}

// GetNode returns the root of this Directory
func (d *Directory) GetNode() (node.Node, error) {
	if d.shard == nil {
		return d.dirnode, nil
	}

	return d.shard.Node()
195
}
196 197 198 199 200 201 202 203

func (d *Directory) GetPrefix() *cid.Prefix {
	if d.shard == nil {
		return &d.dirnode.Prefix
	}

	return d.shard.Prefix()
}