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

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

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

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

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

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

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

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

31
	name string
32 33
}

34
func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, dserv dag.DAGService) *Directory {
35
	return &Directory{
36
		dserv:     dserv,
37
		ctx:       ctx,
38 39 40 41
		name:      name,
		node:      node,
		parent:    parent,
		childDirs: make(map[string]*Directory),
42
		files:     make(map[string]*File),
43 44 45
	}
}

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

	d.lock.Lock()
Jeromy's avatar
Jeromy committed
55
	defer d.lock.Unlock()
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	err = d.node.RemoveNodeLink(name)
	if err != nil && err != dag.ErrNotFound {
		return err
	}

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

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

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

Jeromy's avatar
Jeromy committed
73
// childFile returns a file under this directory by the given name if it exists
74
func (d *Directory) childFile(name string) (*File, error) {
75 76 77 78 79
	fi, ok := d.files[name]
	if ok {
		return fi, nil
	}

Jeromy's avatar
Jeromy committed
80 81 82 83 84 85 86 87
	nd, err := d.childFromDag(name)
	if err != nil {
		return nil, err
	}
	i, err := ft.FromBytes(nd.Data)
	if err != nil {
		return nil, err
	}
88

Jeromy's avatar
Jeromy committed
89 90 91 92
	switch i.GetType() {
	case ufspb.Data_Directory:
		return nil, ErrIsDirectory
	case ufspb.Data_File:
93
		nfi, err := NewFile(name, nd, d, d.dserv)
Jeromy's avatar
Jeromy committed
94 95
		if err != nil {
			return nil, err
96
		}
Jeromy's avatar
Jeromy committed
97 98 99 100 101 102
		d.files[name] = nfi
		return nfi, nil
	case ufspb.Data_Metadata:
		return nil, ErrNotYetImplemented
	default:
		return nil, ErrInvalidChild
103 104 105
	}
}

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

Jeromy's avatar
Jeromy committed
114 115 116 117 118 119 120 121 122 123 124 125
	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:
126
		ndir := NewDirectory(d.ctx, name, nd, d, d.dserv)
Jeromy's avatar
Jeromy committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140
		d.childDirs[name] = ndir
		return ndir, nil
	case ufspb.Data_File:
		return nil, fmt.Errorf("%s is not a directory", name)
	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) {
141 142
	for _, lnk := range d.node.Links {
		if lnk.Name == name {
143
			return lnk.GetNode(d.ctx, d.dserv)
144 145 146
		}
	}

Jeromy's avatar
Jeromy committed
147
	return nil, os.ErrNotExist
148 149
}

Jeromy's avatar
Jeromy committed
150
// Child returns the child of this directory by the given name
151 152 153
func (d *Directory) Child(name string) (FSNode, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
Jeromy's avatar
Jeromy committed
154 155 156
	return d.childUnsync(name)
}

Jeromy's avatar
Jeromy committed
157 158
// 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
159
func (d *Directory) childUnsync(name string) (FSNode, error) {
160

161 162 163 164 165 166 167 168 169
	dir, err := d.childDir(name)
	if err == nil {
		return dir, nil
	}
	fi, err := d.childFile(name)
	if err == nil {
		return fi, nil
	}

Jeromy's avatar
Jeromy committed
170
	return nil, os.ErrNotExist
171 172
}

173 174 175 176 177 178 179 180
type NodeListing struct {
	Name string
	Type int
	Size int64
	Hash string
}

func (d *Directory) List() ([]NodeListing, error) {
181 182 183
	d.lock.Lock()
	defer d.lock.Unlock()

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
	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)
215
	}
216 217

	return out, nil
218 219 220 221
}

func (d *Directory) Mkdir(name string) (*Directory, error) {
	d.lock.Lock()
Jeromy's avatar
Jeromy committed
222
	defer d.lock.Unlock()
223 224 225

	_, err := d.childDir(name)
	if err == nil {
Jeromy's avatar
Jeromy committed
226
		return nil, os.ErrExist
227 228 229
	}
	_, err = d.childFile(name)
	if err == nil {
Jeromy's avatar
Jeromy committed
230
		return nil, os.ErrExist
231 232 233
	}

	ndir := &dag.Node{Data: ft.FolderPBData()}
234 235 236 237 238 239

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

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	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
	}

	return d.childDir(name)
}

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

257 258 259 260 261 262 263 264 265 266 267
	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
268
// AddChild adds the node 'nd' under this directory giving it the name 'name'
269
func (d *Directory) AddChild(name string, nd *dag.Node) error {
Jeromy's avatar
Jeromy committed
270 271
	d.Lock()
	defer d.Unlock()
272

273 274 275 276 277
	pbn, err := ft.FromBytes(nd.Data)
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
278
	_, err = d.childUnsync(name)
279
	if err == nil {
280
		return ErrDirExists
281 282
	}

Jeromy's avatar
Jeromy committed
283 284 285 286 287
	_, err = d.dserv.Add(nd)
	if err != nil {
		return err
	}

288 289 290 291 292 293 294
	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		return err
	}

	switch pbn.GetType() {
	case ft.TDirectory:
295
		d.childDirs[name] = NewDirectory(d.ctx, name, nd, d, d.dserv)
296
	case ft.TFile, ft.TMetadata, ft.TRaw:
297
		nfi, err := NewFile(name, nd, d, d.dserv)
298 299 300 301 302
		if err != nil {
			return err
		}
		d.files[name] = nfi
	default:
Jeromy's avatar
Jeromy committed
303
		return ErrInvalidChild
304 305 306 307 308 309 310 311
	}
	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) GetNode() (*dag.Node, error) {
	return d.node, nil
}

312 313
func (d *Directory) Lock() {
	d.lock.Lock()
314 315
}

316 317
func (d *Directory) Unlock() {
	d.lock.Unlock()
318
}