github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/ipnsfs/dir.go (about)

     1  package ipnsfs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  
     9  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    10  
    11  	dag "github.com/ipfs/go-ipfs/merkledag"
    12  	ft "github.com/ipfs/go-ipfs/unixfs"
    13  	ufspb "github.com/ipfs/go-ipfs/unixfs/pb"
    14  )
    15  
    16  var ErrNotYetImplemented = errors.New("not yet implemented")
    17  var ErrInvalidChild = errors.New("invalid child node")
    18  
    19  type Directory struct {
    20  	fs     *Filesystem
    21  	parent childCloser
    22  
    23  	childDirs map[string]*Directory
    24  	files     map[string]*File
    25  
    26  	lock sync.Mutex
    27  	node *dag.Node
    28  	ctx  context.Context
    29  
    30  	name string
    31  }
    32  
    33  func NewDirectory(ctx context.Context, name string, node *dag.Node, parent childCloser, fs *Filesystem) *Directory {
    34  	return &Directory{
    35  		ctx:       ctx,
    36  		fs:        fs,
    37  		name:      name,
    38  		node:      node,
    39  		parent:    parent,
    40  		childDirs: make(map[string]*Directory),
    41  		files:     make(map[string]*File),
    42  	}
    43  }
    44  
    45  // closeChild updates the child by the given name to the dag node 'nd'
    46  // and changes its own dag node, then propogates the changes upward
    47  func (d *Directory) closeChild(name string, nd *dag.Node) error {
    48  	_, err := d.fs.dserv.Add(nd)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	d.lock.Lock()
    54  	defer d.lock.Unlock()
    55  	err = d.node.RemoveNodeLink(name)
    56  	if err != nil && err != dag.ErrNotFound {
    57  		return err
    58  	}
    59  
    60  	err = d.node.AddNodeLinkClean(name, nd)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	return d.parent.closeChild(d.name, d.node)
    66  }
    67  
    68  func (d *Directory) Type() NodeType {
    69  	return TDir
    70  }
    71  
    72  // childFile returns a file under this directory by the given name if it exists
    73  func (d *Directory) childFile(name string) (*File, error) {
    74  	fi, ok := d.files[name]
    75  	if ok {
    76  		return fi, nil
    77  	}
    78  
    79  	nd, err := d.childFromDag(name)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	i, err := ft.FromBytes(nd.Data)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	switch i.GetType() {
    89  	case ufspb.Data_Directory:
    90  		return nil, ErrIsDirectory
    91  	case ufspb.Data_File:
    92  		nfi, err := NewFile(name, nd, d, d.fs)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		d.files[name] = nfi
    97  		return nfi, nil
    98  	case ufspb.Data_Metadata:
    99  		return nil, ErrNotYetImplemented
   100  	default:
   101  		return nil, ErrInvalidChild
   102  	}
   103  }
   104  
   105  // childDir returns a directory under this directory by the given name if it
   106  // exists.
   107  func (d *Directory) childDir(name string) (*Directory, error) {
   108  	dir, ok := d.childDirs[name]
   109  	if ok {
   110  		return dir, nil
   111  	}
   112  
   113  	nd, err := d.childFromDag(name)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	i, err := ft.FromBytes(nd.Data)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	switch i.GetType() {
   124  	case ufspb.Data_Directory:
   125  		ndir := NewDirectory(d.ctx, name, nd, d, d.fs)
   126  		d.childDirs[name] = ndir
   127  		return ndir, nil
   128  	case ufspb.Data_File:
   129  		return nil, fmt.Errorf("%s is not a directory", name)
   130  	case ufspb.Data_Metadata:
   131  		return nil, ErrNotYetImplemented
   132  	default:
   133  		return nil, ErrInvalidChild
   134  	}
   135  }
   136  
   137  // childFromDag searches through this directories dag node for a child link
   138  // with the given name
   139  func (d *Directory) childFromDag(name string) (*dag.Node, error) {
   140  	for _, lnk := range d.node.Links {
   141  		if lnk.Name == name {
   142  			return lnk.GetNode(d.ctx, d.fs.dserv)
   143  		}
   144  	}
   145  
   146  	return nil, os.ErrNotExist
   147  }
   148  
   149  // Child returns the child of this directory by the given name
   150  func (d *Directory) Child(name string) (FSNode, error) {
   151  	d.lock.Lock()
   152  	defer d.lock.Unlock()
   153  	return d.childUnsync(name)
   154  }
   155  
   156  // childUnsync returns the child under this directory by the given name
   157  // without locking, useful for operations which already hold a lock
   158  func (d *Directory) childUnsync(name string) (FSNode, error) {
   159  	dir, err := d.childDir(name)
   160  	if err == nil {
   161  		return dir, nil
   162  	}
   163  	fi, err := d.childFile(name)
   164  	if err == nil {
   165  		return fi, nil
   166  	}
   167  
   168  	return nil, os.ErrNotExist
   169  }
   170  
   171  func (d *Directory) List() []string {
   172  	d.lock.Lock()
   173  	defer d.lock.Unlock()
   174  
   175  	var out []string
   176  	for _, lnk := range d.node.Links {
   177  		out = append(out, lnk.Name)
   178  	}
   179  	return out
   180  }
   181  
   182  func (d *Directory) Mkdir(name string) (*Directory, error) {
   183  	d.lock.Lock()
   184  	defer d.lock.Unlock()
   185  
   186  	_, err := d.childDir(name)
   187  	if err == nil {
   188  		return nil, os.ErrExist
   189  	}
   190  	_, err = d.childFile(name)
   191  	if err == nil {
   192  		return nil, os.ErrExist
   193  	}
   194  
   195  	ndir := &dag.Node{Data: ft.FolderPBData()}
   196  	err = d.node.AddNodeLinkClean(name, ndir)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	err = d.parent.closeChild(d.name, d.node)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return d.childDir(name)
   207  }
   208  
   209  func (d *Directory) Unlink(name string) error {
   210  	d.lock.Lock()
   211  	defer d.lock.Unlock()
   212  
   213  	delete(d.childDirs, name)
   214  	delete(d.files, name)
   215  
   216  	err := d.node.RemoveNodeLink(name)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	return d.parent.closeChild(d.name, d.node)
   222  }
   223  
   224  // AddChild adds the node 'nd' under this directory giving it the name 'name'
   225  func (d *Directory) AddChild(name string, nd *dag.Node) error {
   226  	d.Lock()
   227  	defer d.Unlock()
   228  	pbn, err := ft.FromBytes(nd.Data)
   229  	if err != nil {
   230  		return err
   231  	}
   232  
   233  	_, err = d.childUnsync(name)
   234  	if err == nil {
   235  		return errors.New("directory already has entry by that name")
   236  	}
   237  
   238  	err = d.node.AddNodeLinkClean(name, nd)
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	switch pbn.GetType() {
   244  	case ft.TDirectory:
   245  		d.childDirs[name] = NewDirectory(d.ctx, name, nd, d, d.fs)
   246  	case ft.TFile, ft.TMetadata, ft.TRaw:
   247  		nfi, err := NewFile(name, nd, d, d.fs)
   248  		if err != nil {
   249  			return err
   250  		}
   251  		d.files[name] = nfi
   252  	default:
   253  		return ErrInvalidChild
   254  	}
   255  	return d.parent.closeChild(d.name, d.node)
   256  }
   257  
   258  func (d *Directory) GetNode() (*dag.Node, error) {
   259  	return d.node, nil
   260  }
   261  
   262  func (d *Directory) Lock() {
   263  	d.lock.Lock()
   264  }
   265  
   266  func (d *Directory) Unlock() {
   267  	d.lock.Unlock()
   268  }