Skip to content
Snippets Groups Projects
Select Git revision
  • fd4c4122d0a9cfab582b878759d1f5062d0f9982
  • main default protected
2 results

dir.go

Blame
  • user avatar
    Jeromy Johnson authored
    License: MIT
    Signed-off-by: default avatarJeromy <jeromyj@gmail.com>
    fd4c4122
    History
    dir.go 7.39 KiB
    package mfs
    
    import (
    	"errors"
    	"fmt"
    	"os"
    	"path"
    	"sync"
    	"time"
    
    	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    
    	dag "github.com/ipfs/go-ipfs/merkledag"
    	ft "github.com/ipfs/go-ipfs/unixfs"
    	ufspb "github.com/ipfs/go-ipfs/unixfs/pb"
    )
    
    var ErrNotYetImplemented = errors.New("not yet implemented")
    var ErrInvalidChild = errors.New("invalid child node")
    var ErrDirExists = errors.New("directory already has entry by that name")
    
    type Directory struct {
    	dserv  dag.DAGService
    	parent childCloser
    
    	childDirs map[string]*Directory
    	files     map[string]*File
    
    	lock sync.Mutex
    	node *dag.Node
    	ctx  context.Context
    
    	modTime time.Time
    
    	name string
    }
    
    func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, dserv dag.DAGService) *Directory {
    	return &Directory{
    		dserv:     dserv,
    		ctx:       ctx,
    		name:      name,
    		node:      node,
    		parent:    parent,
    		childDirs: make(map[string]*Directory),
    		files:     make(map[string]*File),
    		modTime:   time.Now(),
    	}
    }
    
    // closeChild updates the child by the given name to the dag node 'nd'
    // and changes its own dag node
    func (d *Directory) closeChild(name string, nd *dag.Node) error {
    	mynd, err := d.closeChildUpdate(name, nd)
    	if err != nil {
    		return err
    	}
    
    	return d.parent.closeChild(d.name, mynd)
    }
    
    // closeChildUpdate is the portion of closeChild that needs to be locked around
    func (d *Directory) closeChildUpdate(name string, nd *dag.Node) (*dag.Node, error) {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	err := d.updateChild(name, nd)
    	if err != nil {
    		return nil, err
    	}
    
    	return d.flushCurrentNode()
    }
    
    func (d *Directory) flushCurrentNode() (*dag.Node, error) {
    	_, err := d.dserv.Add(d.node)
    	if err != nil {
    		return nil, err
    	}
    
    	return d.node.Copy(), nil
    }
    
    func (d *Directory) updateChild(name string, nd *dag.Node) error {
    	err := d.node.RemoveNodeLink(name)
    	if err != nil && err != dag.ErrNotFound {
    		return err
    	}
    
    	err = d.node.AddNodeLinkClean(name, nd)
    	if err != nil {
    		return err
    	}
    
    	d.modTime = time.Now()
    
    	return nil
    }
    
    func (d *Directory) Type() NodeType {
    	return TDir
    }
    
    // childFile returns a file under this directory by the given name if it exists
    func (d *Directory) childFile(name string) (*File, error) {
    	fi, ok := d.files[name]
    	if ok {
    		return fi, nil
    	}
    
    	fsn, err := d.childNode(name)
    	if err != nil {
    		return nil, err
    	}
    
    	if fi, ok := fsn.(*File); ok {
    		return fi, nil
    	}
    
    	return nil, fmt.Errorf("%s is not a file", name)
    }
    
    // childDir returns a directory under this directory by the given name if it
    // exists.
    func (d *Directory) childDir(name string) (*Directory, error) {
    	dir, ok := d.childDirs[name]
    	if ok {
    		return dir, nil
    	}
    
    	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) {
    	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:
    		ndir := NewDirectory(d.ctx, name, nd, d, d.dserv)
    		d.childDirs[name] = ndir
    		return ndir, nil
    	case ufspb.Data_File:
    		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
    	}
    }
    
    // childFromDag searches through this directories dag node for a child link
    // with the given name
    func (d *Directory) childFromDag(name string) (*dag.Node, error) {
    	for _, lnk := range d.node.Links {
    		if lnk.Name == name {
    			return lnk.GetNode(d.ctx, d.dserv)
    		}
    	}
    
    	return nil, os.ErrNotExist
    }
    
    // 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)
    }
    
    // childUnsync returns the child under this directory by the given name
    // without locking, useful for operations which already hold a lock
    func (d *Directory) childUnsync(name string) (FSNode, error) {
    	cdir, ok := d.childDirs[name]
    	if ok {
    		return cdir, nil
    	}
    
    	cfile, ok := d.files[name]
    	if ok {
    		return cfile, nil
    	}
    
    	return d.childNode(name)
    }
    
    type NodeListing struct {
    	Name string
    	Type int
    	Size int64
    	Hash string
    }
    
    func (d *Directory) List() ([]NodeListing, error) {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	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)
    	}
    
    	return out, nil
    }
    
    func (d *Directory) Mkdir(name string) (*Directory, error) {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	_, err := d.childDir(name)
    	if err == nil {
    		return nil, os.ErrExist
    	}
    	_, err = d.childFile(name)
    	if err == nil {
    		return nil, os.ErrExist
    	}
    
    	ndir := &dag.Node{Data: ft.FolderPBData()}
    
    	_, err = d.dserv.Add(ndir)
    	if err != nil {
    		return nil, err
    	}
    
    	err = d.node.AddNodeLinkClean(name, ndir)
    	if err != nil {
    		return nil, err
    	}
    
    	dirobj := NewDirectory(d.ctx, name, ndir, d, d.dserv)
    	d.childDirs[name] = dirobj
    	return dirobj, nil
    }
    
    func (d *Directory) Unlink(name string) error {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	delete(d.childDirs, name)
    	delete(d.files, name)
    
    	err := d.node.RemoveNodeLink(name)
    	if err != nil {
    		return err
    	}
    
    	_, err = d.dserv.Add(d.node)
    	if err != nil {
    		return err
    	}
    
    	return nil
    }
    
    func (d *Directory) Flush() error {
    	nd, err := d.flushCurrentNode()
    	if err != nil {
    		return err
    	}
    
    	return d.parent.closeChild(d.name, nd)
    }
    
    // AddChild adds the node 'nd' under this directory giving it the name 'name'
    func (d *Directory) AddChild(name string, nd *dag.Node) error {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	_, err := d.childUnsync(name)
    	if err == nil {
    		return ErrDirExists
    	}
    
    	_, err = d.dserv.Add(nd)
    	if err != nil {
    		return err
    	}
    
    	err = d.node.AddNodeLinkClean(name, nd)
    	if err != nil {
    		return err
    	}
    
    	d.modTime = time.Now()
    
    	return nil
    }
    
    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
    }
    
    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
    }
    
    func (d *Directory) GetNode() (*dag.Node, error) {
    	d.lock.Lock()
    	defer d.lock.Unlock()
    
    	err := d.sync()
    	if err != nil {
    		return nil, err
    	}
    
    	return d.node, nil
    }
    
    func (d *Directory) Lock() {
    	d.lock.Lock()
    }
    
    func (d *Directory) Unlock() {
    	d.lock.Unlock()
    }