dir.go 6.82 KB
Newer Older
1
package mfs
2 3 4 5 6 7

import (
	"errors"
	"fmt"
	"os"
	"sync"
Jeromy's avatar
Jeromy committed
8
	"time"
Jeromy's avatar
Jeromy committed
9 10

	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
11

12 13 14
	dag "github.com/ipfs/go-ipfs/merkledag"
	ft "github.com/ipfs/go-ipfs/unixfs"
	ufspb "github.com/ipfs/go-ipfs/unixfs/pb"
15 16
)

Jeromy's avatar
Jeromy committed
17 18
var ErrNotYetImplemented = errors.New("not yet implemented")
var ErrInvalidChild = errors.New("invalid child node")
19
var ErrDirExists = errors.New("directory already has entry by that name")
Jeromy's avatar
Jeromy committed
20

21
type Directory struct {
22
	dserv  dag.DAGService
23 24
	parent childCloser

25
	childDirs map[string]*Directory
26
	files     map[string]*File
27 28

	lock sync.Mutex
29
	node *dag.Node
30
	ctx  context.Context
31

Jeromy's avatar
Jeromy committed
32 33
	modTime time.Time

34
	name string
35 36
}

37
func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, dserv dag.DAGService) *Directory {
38
	return &Directory{
39
		dserv:     dserv,
40
		ctx:       ctx,
41 42 43 44
		name:      name,
		node:      node,
		parent:    parent,
		childDirs: make(map[string]*Directory),
45
		files:     make(map[string]*File),
Jeromy's avatar
Jeromy committed
46
		modTime:   time.Now(),
47 48 49
	}
}

Jeromy's avatar
Jeromy committed
50 51
// closeChild updates the child by the given name to the dag node 'nd'
// and changes its own dag node, then propogates the changes upward
52
func (d *Directory) closeChild(name string, nd *dag.Node) error {
53
	_, err := d.dserv.Add(nd)
54 55 56 57 58
	if err != nil {
		return err
	}

	d.lock.Lock()
Jeromy's avatar
Jeromy committed
59
	defer d.lock.Unlock()
60 61 62 63 64 65 66 67 68 69
	err = d.updateChild(name, nd)
	if err != nil {
		return err
	}

	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) updateChild(name string, nd *dag.Node) error {
	err := d.node.RemoveNodeLink(name)
70 71 72 73 74 75 76 77 78
	if err != nil && err != dag.ErrNotFound {
		return err
	}

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

Jeromy's avatar
Jeromy committed
79 80
	d.modTime = time.Now()

81
	return nil
82 83 84 85 86 87
}

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

Jeromy's avatar
Jeromy committed
88
// childFile returns a file under this directory by the given name if it exists
89
func (d *Directory) childFile(name string) (*File, error) {
90 91 92 93 94
	fi, ok := d.files[name]
	if ok {
		return fi, nil
	}

95
	fsn, err := d.childNode(name)
Jeromy's avatar
Jeromy committed
96 97 98
	if err != nil {
		return nil, err
	}
99

100 101
	if fi, ok := fsn.(*File); ok {
		return fi, nil
102
	}
103 104

	return nil, fmt.Errorf("%s is not a file", name)
105 106
}

Jeromy's avatar
Jeromy committed
107 108
// childDir returns a directory under this directory by the given name if it
// exists.
109 110 111 112 113 114
func (d *Directory) childDir(name string) (*Directory, error) {
	dir, ok := d.childDirs[name]
	if ok {
		return dir, nil
	}

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
	fsn, err := d.childNode(name)
	if err != nil {
		return nil, err
	}

	if dir, ok := fsn.(*Directory); ok {
		return dir, nil
	}

	return nil, fmt.Errorf("%s is not a directory", name)
}

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

	i, err := ft.FromBytes(nd.Data)
	if err != nil {
		return nil, err
	}

	switch i.GetType() {
	case ufspb.Data_Directory:
142
		ndir := NewDirectory(d.ctx, name, nd, d, d.dserv)
Jeromy's avatar
Jeromy committed
143 144 145
		d.childDirs[name] = ndir
		return ndir, nil
	case ufspb.Data_File:
146 147 148 149 150 151
		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
152 153 154 155 156 157 158 159 160 161
	case ufspb.Data_Metadata:
		return nil, ErrNotYetImplemented
	default:
		return nil, ErrInvalidChild
	}
}

// childFromDag searches through this directories dag node for a child link
// with the given name
func (d *Directory) childFromDag(name string) (*dag.Node, error) {
162 163
	for _, lnk := range d.node.Links {
		if lnk.Name == name {
164
			return lnk.GetNode(d.ctx, d.dserv)
165 166 167
		}
	}

Jeromy's avatar
Jeromy committed
168
	return nil, os.ErrNotExist
169 170
}

Jeromy's avatar
Jeromy committed
171
// Child returns the child of this directory by the given name
172 173 174
func (d *Directory) Child(name string) (FSNode, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
Jeromy's avatar
Jeromy committed
175 176 177
	return d.childUnsync(name)
}

Jeromy's avatar
Jeromy committed
178 179
// 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
180
func (d *Directory) childUnsync(name string) (FSNode, error) {
181 182 183
	cdir, ok := d.childDirs[name]
	if ok {
		return cdir, nil
184
	}
185 186 187 188

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

191
	return d.childNode(name)
192 193
}

194 195 196 197 198 199 200 201
type NodeListing struct {
	Name string
	Type int
	Size int64
	Hash string
}

func (d *Directory) List() ([]NodeListing, error) {
202 203 204
	d.lock.Lock()
	defer d.lock.Unlock()

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
	var out []NodeListing
	for _, l := range d.node.Links {
		child := NodeListing{}
		child.Name = l.Name

		c, err := d.childUnsync(l.Name)
		if err != nil {
			return nil, err
		}

		child.Type = int(c.Type())
		if c, ok := c.(*File); ok {
			size, err := c.Size()
			if err != nil {
				return nil, err
			}
			child.Size = size
		}
		nd, err := c.GetNode()
		if err != nil {
			return nil, err
		}

		k, err := nd.Key()
		if err != nil {
			return nil, err
		}

		child.Hash = k.B58String()

		out = append(out, child)
236
	}
237 238

	return out, nil
239 240 241 242
}

func (d *Directory) Mkdir(name string) (*Directory, error) {
	d.lock.Lock()
Jeromy's avatar
Jeromy committed
243
	defer d.lock.Unlock()
244 245 246

	_, err := d.childDir(name)
	if err == nil {
Jeromy's avatar
Jeromy committed
247
		return nil, os.ErrExist
248 249 250
	}
	_, err = d.childFile(name)
	if err == nil {
Jeromy's avatar
Jeromy committed
251
		return nil, os.ErrExist
252 253 254
	}

	ndir := &dag.Node{Data: ft.FolderPBData()}
255 256 257 258 259 260

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

261 262 263 264 265 266 267 268 269 270
	err = d.node.AddNodeLinkClean(name, ndir)
	if err != nil {
		return nil, err
	}

	err = d.parent.closeChild(d.name, d.node)
	if err != nil {
		return nil, err
	}

Jeromy's avatar
Jeromy committed
271 272 273
	dirobj := NewDirectory(d.ctx, name, ndir, d, d.dserv)
	d.childDirs[name] = dirobj
	return dirobj, nil
274 275 276 277
}

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

280 281 282 283 284 285 286 287 288 289 290
	delete(d.childDirs, name)
	delete(d.files, name)

	err := d.node.RemoveNodeLink(name)
	if err != nil {
		return err
	}

	return d.parent.closeChild(d.name, d.node)
}

Jeromy's avatar
Jeromy committed
291
// AddChild adds the node 'nd' under this directory giving it the name 'name'
292
func (d *Directory) AddChild(name string, nd *dag.Node) error {
Jeromy's avatar
Jeromy committed
293 294
	d.Lock()
	defer d.Unlock()
295

Jeromy's avatar
Jeromy committed
296
	_, err := d.childUnsync(name)
297
	if err == nil {
298
		return ErrDirExists
299 300
	}

Jeromy's avatar
Jeromy committed
301 302 303 304 305
	_, err = d.dserv.Add(nd)
	if err != nil {
		return err
	}

306 307 308 309 310
	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
311 312
	d.modTime = time.Now()

Jeromy's avatar
Jeromy committed
313 314
	//return d.parent.closeChild(d.name, d.node)
	return nil
315 316
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
func (d *Directory) sync() error {
	for name, dir := range d.childDirs {
		nd, err := dir.GetNode()
		if err != nil {
			return err
		}

		_, err = d.dserv.Add(nd)
		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.dserv.Add(nd)
		if err != nil {
			return err
		}

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

	return nil
}

355
func (d *Directory) GetNode() (*dag.Node, error) {
356 357 358 359 360 361 362 363
	d.Lock()
	defer d.Unlock()

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

364 365 366
	return d.node, nil
}

367 368
func (d *Directory) Lock() {
	d.lock.Lock()
369 370
}

371 372
func (d *Directory) Unlock() {
	d.lock.Unlock()
373
}