dir.go 8.22 KB
Newer Older
1
package mfs
2 3

import (
4
	"context"
5 6 7
	"errors"
	"fmt"
	"os"
Jeromy's avatar
Jeromy committed
8
	"path"
Jeromy's avatar
Jeromy committed
9
	"sort"
10
	"sync"
Jeromy's avatar
Jeromy committed
11
	"time"
Jeromy's avatar
Jeromy committed
12

13 14
	dag "github.com/ipfs/go-ipfs/merkledag"
	ft "github.com/ipfs/go-ipfs/unixfs"
15
	uio "github.com/ipfs/go-ipfs/unixfs/io"
16
	ufspb "github.com/ipfs/go-ipfs/unixfs/pb"
17

18
	cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid"
19
	node "gx/ipfs/Qmb3Hm9QDFmfYuET4pu7Kyg8JV78jFa1nvZx5vnCZsK4ck/go-ipld-format"
20 21
)

Jeromy's avatar
Jeromy committed
22 23
var ErrNotYetImplemented = errors.New("not yet implemented")
var ErrInvalidChild = errors.New("invalid child node")
24
var ErrDirExists = errors.New("directory already has entry by that name")
Jeromy's avatar
Jeromy committed
25

26
type Directory struct {
27
	dserv  dag.DAGService
28 29
	parent childCloser

30
	childDirs map[string]*Directory
31
	files     map[string]*File
32 33

	lock sync.Mutex
34
	ctx  context.Context
35

36 37
	dirbuilder *uio.Directory

Jeromy's avatar
Jeromy committed
38 39
	modTime time.Time

40
	name string
41 42
}

43 44 45 46
func NewDirectory(ctx context.Context, name string, node node.Node, parent childCloser, dserv dag.DAGService) (*Directory, error) {
	db, err := uio.NewDirectoryFromNode(dserv, node)
	if err != nil {
		return nil, err
47
	}
48 49 50 51 52 53 54 55 56 57 58

	return &Directory{
		dserv:      dserv,
		ctx:        ctx,
		name:       name,
		dirbuilder: db,
		parent:     parent,
		childDirs:  make(map[string]*Directory),
		files:      make(map[string]*File),
		modTime:    time.Now(),
	}, nil
59 60
}

Kevin Atkinson's avatar
Kevin Atkinson committed
61
// SetPrefix sets the CID prefix
62
func (d *Directory) SetPrefix(prefix *cid.Prefix) {
63 64 65
	d.dirbuilder.SetPrefix(prefix)
}

Jeromy's avatar
Jeromy committed
66
// closeChild updates the child by the given name to the dag node 'nd'
Jeromy's avatar
Jeromy committed
67
// and changes its own dag node
Jeromy's avatar
Jeromy committed
68
func (d *Directory) closeChild(name string, nd node.Node, sync bool) error {
69
	mynd, err := d.closeChildUpdate(name, nd, sync)
70 71 72 73
	if err != nil {
		return err
	}

74 75 76 77
	if sync {
		return d.parent.closeChild(d.name, mynd, true)
	}
	return nil
78 79 80
}

// closeChildUpdate is the portion of closeChild that needs to be locked around
Jeromy's avatar
Jeromy committed
81
func (d *Directory) closeChildUpdate(name string, nd node.Node, sync bool) (*dag.ProtoNode, error) {
82
	d.lock.Lock()
Jeromy's avatar
Jeromy committed
83
	defer d.lock.Unlock()
84 85

	err := d.updateChild(name, nd)
86
	if err != nil {
87
		return nil, err
88 89
	}

90 91 92 93
	if sync {
		return d.flushCurrentNode()
	}
	return nil, nil
94 95
}

96
func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) {
97
	nd, err := d.dirbuilder.GetNode()
98 99 100 101
	if err != nil {
		return nil, err
	}

102 103 104 105
	_, err = d.dserv.Add(nd)
	if err != nil {
		return nil, err
	}
106

107 108 109
	pbnd, ok := nd.(*dag.ProtoNode)
	if !ok {
		return nil, dag.ErrNotProtobuf
110 111
	}

112 113 114 115 116
	return pbnd.Copy().(*dag.ProtoNode), nil
}

func (d *Directory) updateChild(name string, nd node.Node) error {
	err := d.dirbuilder.AddChild(d.ctx, name, nd)
117 118 119 120
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
121 122
	d.modTime = time.Now()

123
	return nil
124 125 126 127 128 129
}

func (d *Directory) Type() NodeType {
	return TDir
}

130 131 132
// childNode returns a FSNode under this directory by the given name if it exists.
// it does *not* check the cached dirs and files
func (d *Directory) childNode(name string) (FSNode, error) {
Jeromy's avatar
Jeromy committed
133 134 135 136 137
	nd, err := d.childFromDag(name)
	if err != nil {
		return nil, err
	}

138 139 140 141
	return d.cacheNode(name, nd)
}

// cacheNode caches a node into d.childDirs or d.files and returns the FSNode.
Jeromy's avatar
Jeromy committed
142 143 144 145 146 147 148
func (d *Directory) cacheNode(name string, nd node.Node) (FSNode, error) {
	switch nd := nd.(type) {
	case *dag.ProtoNode:
		i, err := ft.FromBytes(nd.Data())
		if err != nil {
			return nil, err
		}
Jeromy's avatar
Jeromy committed
149

Jeromy's avatar
Jeromy committed
150
		switch i.GetType() {
151 152 153 154 155 156
		case ufspb.Data_Directory, ufspb.Data_HAMTShard:
			ndir, err := NewDirectory(d.ctx, name, nd, d, d.dserv)
			if err != nil {
				return nil, err
			}

Jeromy's avatar
Jeromy committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
			d.childDirs[name] = ndir
			return ndir, nil
		case ufspb.Data_File, ufspb.Data_Raw, ufspb.Data_Symlink:
			nfi, err := NewFile(name, nd, d, d.dserv)
			if err != nil {
				return nil, err
			}
			d.files[name] = nfi
			return nfi, nil
		case ufspb.Data_Metadata:
			return nil, ErrNotYetImplemented
		default:
			return nil, ErrInvalidChild
		}
	case *dag.RawNode:
172 173 174 175 176 177
		nfi, err := NewFile(name, nd, d, d.dserv)
		if err != nil {
			return nil, err
		}
		d.files[name] = nfi
		return nfi, nil
Jeromy's avatar
Jeromy committed
178
	default:
Jeromy's avatar
Jeromy committed
179
		return nil, fmt.Errorf("unrecognized node type in cache node")
Jeromy's avatar
Jeromy committed
180 181 182
	}
}

Jeromy's avatar
Jeromy committed
183 184 185 186 187 188 189
// Child returns the child of this directory by the given name
func (d *Directory) Child(name string) (FSNode, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	return d.childUnsync(name)
}

190 191 192 193 194 195 196
func (d *Directory) Uncache(name string) {
	d.lock.Lock()
	defer d.lock.Unlock()
	delete(d.files, name)
	delete(d.childDirs, name)
}

Jeromy's avatar
Jeromy committed
197 198
// childFromDag searches through this directories dag node for a child link
// with the given name
Jeromy's avatar
Jeromy committed
199
func (d *Directory) childFromDag(name string) (node.Node, error) {
200
	return d.dirbuilder.Find(d.ctx, name)
201 202
}

Jeromy's avatar
Jeromy committed
203 204
// childUnsync returns the child under this directory by the given name
// without locking, useful for operations which already hold a lock
Jeromy's avatar
Jeromy committed
205
func (d *Directory) childUnsync(name string) (FSNode, error) {
206 207 208
	cdir, ok := d.childDirs[name]
	if ok {
		return cdir, nil
209
	}
210 211 212 213

	cfile, ok := d.files[name]
	if ok {
		return cfile, nil
214 215
	}

216
	return d.childNode(name)
217 218
}

219 220 221 222 223 224 225
type NodeListing struct {
	Name string
	Type int
	Size int64
	Hash string
}

Jeromy's avatar
Jeromy committed
226
func (d *Directory) ListNames(ctx context.Context) ([]string, error) {
227 228
	d.lock.Lock()
	defer d.lock.Unlock()
Jeromy's avatar
Jeromy committed
229

230
	var out []string
Jeromy's avatar
Jeromy committed
231
	err := d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error {
232 233 234
		out = append(out, l.Name)
		return nil
	})
235 236 237 238
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
239
	sort.Strings(out)
Jeromy's avatar
Jeromy committed
240

241
	return out, nil
Jeromy's avatar
Jeromy committed
242 243
}

Jeromy's avatar
Jeromy committed
244
func (d *Directory) List(ctx context.Context) ([]NodeListing, error) {
245
	var out []NodeListing
Jeromy's avatar
Jeromy committed
246
	err := d.ForEachEntry(ctx, func(nl NodeListing) error {
247 248 249 250 251
		out = append(out, nl)
		return nil
	})
	return out, err
}
252

253 254 255
func (d *Directory) ForEachEntry(ctx context.Context, f func(NodeListing) error) error {
	d.lock.Lock()
	defer d.lock.Unlock()
Jeromy's avatar
Jeromy committed
256
	return d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error {
257 258
		c, err := d.childUnsync(l.Name)
		if err != nil {
259 260 261 262 263 264 265 266 267 268 269 270
			return err
		}

		nd, err := c.GetNode()
		if err != nil {
			return err
		}

		child := NodeListing{
			Name: l.Name,
			Type: int(c.Type()),
			Hash: nd.Cid().String(),
271 272 273 274 275
		}

		if c, ok := c.(*File); ok {
			size, err := c.Size()
			if err != nil {
276
				return err
277 278 279 280
			}
			child.Size = size
		}

281 282
		return f(child)
	})
283 284 285 286
}

func (d *Directory) Mkdir(name string) (*Directory, error) {
	d.lock.Lock()
Jeromy's avatar
Jeromy committed
287
	defer d.lock.Unlock()
288

Jeromy's avatar
Jeromy committed
289
	fsn, err := d.childUnsync(name)
290
	if err == nil {
Jeromy's avatar
Jeromy committed
291 292 293 294 295 296 297 298
		switch fsn := fsn.(type) {
		case *Directory:
			return fsn, os.ErrExist
		case *File:
			return nil, os.ErrExist
		default:
			return nil, fmt.Errorf("unrecognized type: %#v", fsn)
		}
299 300
	}

301
	ndir := ft.EmptyDirNode()
302 303 304 305 306 307

	_, err = d.dserv.Add(ndir)
	if err != nil {
		return nil, err
	}

308 309 310 311 312 313
	err = d.dirbuilder.AddChild(d.ctx, name, ndir)
	if err != nil {
		return nil, err
	}

	dirobj, err := NewDirectory(d.ctx, name, ndir, d, d.dserv)
314 315 316 317
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
318 319
	d.childDirs[name] = dirobj
	return dirobj, nil
320 321 322 323
}

func (d *Directory) Unlink(name string) error {
	d.lock.Lock()
Jeromy's avatar
Jeromy committed
324 325
	defer d.lock.Unlock()

326 327 328
	delete(d.childDirs, name)
	delete(d.files, name)

329
	err := d.dirbuilder.RemoveChild(d.ctx, name)
330 331 332 333
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
334
	return nil
335 336
}

337
func (d *Directory) Flush() error {
Jeromy's avatar
Jeromy committed
338
	nd, err := d.GetNode()
339 340 341
	if err != nil {
		return err
	}
342

343
	return d.parent.closeChild(d.name, nd, true)
344 345
}

Jeromy's avatar
Jeromy committed
346
// AddChild adds the node 'nd' under this directory giving it the name 'name'
347
func (d *Directory) AddChild(name string, nd node.Node) error {
348 349
	d.lock.Lock()
	defer d.lock.Unlock()
350

Jeromy's avatar
Jeromy committed
351
	_, err := d.childUnsync(name)
352
	if err == nil {
353
		return ErrDirExists
354 355
	}

Jeromy's avatar
Jeromy committed
356 357 358 359 360
	_, err = d.dserv.Add(nd)
	if err != nil {
		return err
	}

361
	err = d.dirbuilder.AddChild(d.ctx, name, nd)
362 363 364 365
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
366
	d.modTime = time.Now()
Jeromy's avatar
Jeromy committed
367
	return nil
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
func (d *Directory) sync() error {
	for name, dir := range d.childDirs {
		nd, err := dir.GetNode()
		if err != nil {
			return err
		}

		err = d.updateChild(name, nd)
		if err != nil {
			return err
		}
	}

	for name, file := range d.files {
		nd, err := file.GetNode()
		if err != nil {
			return err
		}

		err = d.updateChild(name, nd)
		if err != nil {
			return err
		}
	}

	return nil
}

Jeromy's avatar
Jeromy committed
398 399 400 401 402 403 404 405 406 407
func (d *Directory) Path() string {
	cur := d
	var out string
	for cur != nil {
		out = path.Join(cur.name, out)
		cur = cur.parent.(*Directory)
	}
	return out
}

Jeromy's avatar
Jeromy committed
408
func (d *Directory) GetNode() (node.Node, error) {
409 410
	d.lock.Lock()
	defer d.lock.Unlock()
411 412 413 414 415 416

	err := d.sync()
	if err != nil {
		return nil, err
	}

417 418 419 420 421 422
	nd, err := d.dirbuilder.GetNode()
	if err != nil {
		return nil, err
	}

	_, err = d.dserv.Add(nd)
423 424 425 426
	if err != nil {
		return nil, err
	}

427
	return nd.Copy(), err
428
}