dir.go 8.33 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

Steven Allen's avatar
Steven Allen committed
18 19
	node "gx/ipfs/QmNwUEK7QbwSqyKBu3mMtToo8SUc6wQJ7gdZq4gGGJqfnf/go-ipld-format"
	cid "gx/ipfs/QmeSrf6pzut73u6zLQkRFQ3ygt3k6XFT2kjdYP8Tnkwwyg/go-cid"
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
}

61 62 63 64 65
// GetPrefix gets the CID prefix of the root node
func (d *Directory) GetPrefix() *cid.Prefix {
	return d.dirbuilder.GetPrefix()
}

Kevin Atkinson's avatar
Kevin Atkinson committed
66
// SetPrefix sets the CID prefix
67
func (d *Directory) SetPrefix(prefix *cid.Prefix) {
68 69 70
	d.dirbuilder.SetPrefix(prefix)
}

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

79 80 81 82
	if sync {
		return d.parent.closeChild(d.name, mynd, true)
	}
	return nil
83 84 85
}

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

	err := d.updateChild(name, nd)
91
	if err != nil {
92
		return nil, err
93 94
	}

95 96 97 98
	if sync {
		return d.flushCurrentNode()
	}
	return nil, nil
99 100
}

101
func (d *Directory) flushCurrentNode() (*dag.ProtoNode, error) {
102
	nd, err := d.dirbuilder.GetNode()
103 104 105 106
	if err != nil {
		return nil, err
	}

107 108 109 110
	_, err = d.dserv.Add(nd)
	if err != nil {
		return nil, err
	}
111

112 113 114
	pbnd, ok := nd.(*dag.ProtoNode)
	if !ok {
		return nil, dag.ErrNotProtobuf
115 116
	}

117 118 119 120 121
	return pbnd.Copy().(*dag.ProtoNode), nil
}

func (d *Directory) updateChild(name string, nd node.Node) error {
	err := d.dirbuilder.AddChild(d.ctx, name, nd)
122 123 124 125
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
126 127
	d.modTime = time.Now()

128
	return nil
129 130 131 132 133 134
}

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

135 136 137
// 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
138 139 140 141 142
	nd, err := d.childFromDag(name)
	if err != nil {
		return nil, err
	}

143 144 145 146
	return d.cacheNode(name, nd)
}

// cacheNode caches a node into d.childDirs or d.files and returns the FSNode.
Jeromy's avatar
Jeromy committed
147 148 149 150 151 152 153
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
154

Jeromy's avatar
Jeromy committed
155
		switch i.GetType() {
156 157 158 159 160 161
		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
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
			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:
177 178 179 180 181 182
		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
183
	default:
Jeromy's avatar
Jeromy committed
184
		return nil, fmt.Errorf("unrecognized node type in cache node")
Jeromy's avatar
Jeromy committed
185 186 187
	}
}

Jeromy's avatar
Jeromy committed
188 189 190 191 192 193 194
// 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)
}

195 196 197 198 199 200 201
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
202 203
// childFromDag searches through this directories dag node for a child link
// with the given name
Jeromy's avatar
Jeromy committed
204
func (d *Directory) childFromDag(name string) (node.Node, error) {
205
	return d.dirbuilder.Find(d.ctx, name)
206 207
}

Jeromy's avatar
Jeromy committed
208 209
// 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
210
func (d *Directory) childUnsync(name string) (FSNode, error) {
211 212 213
	cdir, ok := d.childDirs[name]
	if ok {
		return cdir, nil
214
	}
215 216 217 218

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

221
	return d.childNode(name)
222 223
}

224 225 226 227 228 229 230
type NodeListing struct {
	Name string
	Type int
	Size int64
	Hash string
}

Jeromy's avatar
Jeromy committed
231
func (d *Directory) ListNames(ctx context.Context) ([]string, error) {
232 233
	d.lock.Lock()
	defer d.lock.Unlock()
Jeromy's avatar
Jeromy committed
234

235
	var out []string
Jeromy's avatar
Jeromy committed
236
	err := d.dirbuilder.ForEachLink(ctx, func(l *node.Link) error {
237 238 239
		out = append(out, l.Name)
		return nil
	})
240 241 242 243
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
244
	sort.Strings(out)
Jeromy's avatar
Jeromy committed
245

246
	return out, nil
Jeromy's avatar
Jeromy committed
247 248
}

Jeromy's avatar
Jeromy committed
249
func (d *Directory) List(ctx context.Context) ([]NodeListing, error) {
250
	var out []NodeListing
Jeromy's avatar
Jeromy committed
251
	err := d.ForEachEntry(ctx, func(nl NodeListing) error {
252 253 254 255 256
		out = append(out, nl)
		return nil
	})
	return out, err
}
257

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

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

		child := NodeListing{
			Name: l.Name,
			Type: int(c.Type()),
			Hash: nd.Cid().String(),
276 277 278 279 280
		}

		if c, ok := c.(*File); ok {
			size, err := c.Size()
			if err != nil {
281
				return err
282 283 284 285
			}
			child.Size = size
		}

286 287
		return f(child)
	})
288 289 290 291
}

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

Jeromy's avatar
Jeromy committed
294
	fsn, err := d.childUnsync(name)
295
	if err == nil {
Jeromy's avatar
Jeromy committed
296 297 298 299 300 301 302 303
		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)
		}
304 305
	}

306
	ndir := ft.EmptyDirNode()
307
	ndir.SetPrefix(d.GetPrefix())
308 309 310 311 312 313

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

314 315 316 317 318 319
	err = d.dirbuilder.AddChild(d.ctx, name, ndir)
	if err != nil {
		return nil, err
	}

	dirobj, err := NewDirectory(d.ctx, name, ndir, d, d.dserv)
320 321 322 323
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
324 325
	d.childDirs[name] = dirobj
	return dirobj, nil
326 327 328 329
}

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

332 333 334
	delete(d.childDirs, name)
	delete(d.files, name)

335
	return d.dirbuilder.RemoveChild(d.ctx, name)
336 337
}

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

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

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

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

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

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

Jeromy's avatar
Jeromy committed
367
	d.modTime = time.Now()
Jeromy's avatar
Jeromy committed
368
	return nil
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 398
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
399 400 401 402 403 404 405 406 407 408
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
409
func (d *Directory) GetNode() (node.Node, error) {
410 411
	d.lock.Lock()
	defer d.lock.Unlock()
412 413 414 415 416 417

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

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

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

428
	return nd.Copy(), err
429
}