github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/fuse/ipns/ipns_unix.go (about)

     1  // package fuse/ipns implements a fuse filesystem that interfaces
     2  // with ipns, the naming system for ipfs.
     3  package ipns
     4  
     5  import (
     6  	"errors"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
    13  	fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
    14  	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
    15  
    16  	core "github.com/jbenet/go-ipfs/core"
    17  	ci "github.com/jbenet/go-ipfs/crypto"
    18  	chunk "github.com/jbenet/go-ipfs/importer/chunk"
    19  	mdag "github.com/jbenet/go-ipfs/merkledag"
    20  	ft "github.com/jbenet/go-ipfs/unixfs"
    21  	uio "github.com/jbenet/go-ipfs/unixfs/io"
    22  	ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
    23  	u "github.com/jbenet/go-ipfs/util"
    24  )
    25  
    26  var log = u.Logger("ipns")
    27  
    28  var (
    29  	shortRepublishTimeout = time.Millisecond * 5
    30  	longRepublishTimeout  = time.Millisecond * 500
    31  )
    32  
    33  // FileSystem is the readwrite IPNS Fuse Filesystem.
    34  type FileSystem struct {
    35  	Ipfs     *core.IpfsNode
    36  	RootNode *Root
    37  }
    38  
    39  // NewFileSystem constructs new fs using given core.IpfsNode instance.
    40  func NewIpns(ipfs *core.IpfsNode, ipfspath string) (*FileSystem, error) {
    41  	root, err := CreateRoot(ipfs, []ci.PrivKey{ipfs.Identity.PrivKey()}, ipfspath)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return &FileSystem{Ipfs: ipfs, RootNode: root}, nil
    46  }
    47  
    48  func CreateRoot(n *core.IpfsNode, keys []ci.PrivKey, ipfsroot string) (*Root, error) {
    49  	root := new(Root)
    50  	root.LocalDirs = make(map[string]*Node)
    51  	root.Ipfs = n
    52  	abspath, err := filepath.Abs(ipfsroot)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	root.IpfsRoot = abspath
    57  
    58  	root.Keys = keys
    59  
    60  	if len(keys) == 0 {
    61  		log.Warning("No keys given for ipns root creation")
    62  	} else {
    63  		k := keys[0]
    64  		pub := k.GetPublic()
    65  		hash, err := pub.Hash()
    66  		if err != nil {
    67  			log.Errorf("Read Root Error: %s", err)
    68  			return nil, err
    69  		}
    70  		root.LocalLink = &Link{u.Key(hash).Pretty()}
    71  	}
    72  
    73  	for _, k := range keys {
    74  		hash, err := k.GetPublic().Hash()
    75  		if err != nil {
    76  			log.Error("failed to hash public key.")
    77  			continue
    78  		}
    79  		name := u.Key(hash).Pretty()
    80  		nd := new(Node)
    81  		nd.Ipfs = n
    82  		nd.key = k
    83  		nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
    84  
    85  		go nd.repub.Run()
    86  
    87  		pointsTo, err := n.Namesys.Resolve(name)
    88  		if err != nil {
    89  			log.Warning("Could not resolve value for local ipns entry, providing empty dir")
    90  			nd.Nd = &mdag.Node{Data: ft.FolderPBData()}
    91  			root.LocalDirs[name] = nd
    92  			continue
    93  		}
    94  
    95  		if !u.IsValidHash(pointsTo) {
    96  			log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo)
    97  			return nil, nil
    98  		}
    99  
   100  		node, err := n.Resolver.ResolvePath(pointsTo)
   101  		if err != nil {
   102  			log.Warning("Failed to resolve value from ipns entry in ipfs")
   103  			continue
   104  		}
   105  
   106  		nd.Nd = node
   107  		root.LocalDirs[name] = nd
   108  	}
   109  
   110  	return root, nil
   111  }
   112  
   113  // Root constructs the Root of the filesystem, a Root object.
   114  func (f FileSystem) Root() (fs.Node, fuse.Error) {
   115  	return f.RootNode, nil
   116  }
   117  
   118  // Root is the root object of the filesystem tree.
   119  type Root struct {
   120  	Ipfs *core.IpfsNode
   121  	Keys []ci.PrivKey
   122  
   123  	// Used for symlinking into ipfs
   124  	IpfsRoot  string
   125  	LocalDirs map[string]*Node
   126  
   127  	LocalLink *Link
   128  }
   129  
   130  // Attr returns file attributes.
   131  func (*Root) Attr() fuse.Attr {
   132  	return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
   133  }
   134  
   135  // Lookup performs a lookup under this node.
   136  func (s *Root) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
   137  	log.Debugf("ipns: Root Lookup: '%s'", name)
   138  	switch name {
   139  	case "mach_kernel", ".hidden", "._.":
   140  		// Just quiet some log noise on OS X.
   141  		return nil, fuse.ENOENT
   142  	}
   143  
   144  	if name == "local" {
   145  		if s.LocalLink == nil {
   146  			return nil, fuse.ENOENT
   147  		}
   148  		return s.LocalLink, nil
   149  	}
   150  
   151  	nd, ok := s.LocalDirs[name]
   152  	if ok {
   153  		return nd, nil
   154  	}
   155  
   156  	log.Debugf("ipns: Falling back to resolution for [%s].", name)
   157  	resolved, err := s.Ipfs.Namesys.Resolve(name)
   158  	if err != nil {
   159  		log.Warningf("ipns: namesys resolve error: %s", err)
   160  		return nil, fuse.ENOENT
   161  	}
   162  
   163  	return &Link{s.IpfsRoot + "/" + resolved}, nil
   164  }
   165  
   166  // ReadDir reads a particular directory. Disallowed for root.
   167  func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
   168  	log.Debug("Read Root.")
   169  	listing := []fuse.Dirent{
   170  		fuse.Dirent{
   171  			Name: "local",
   172  			Type: fuse.DT_Link,
   173  		},
   174  	}
   175  	for _, k := range r.Keys {
   176  		pub := k.GetPublic()
   177  		hash, err := pub.Hash()
   178  		if err != nil {
   179  			log.Errorf("Read Root Error: %s", err)
   180  			continue
   181  		}
   182  		ent := fuse.Dirent{
   183  			Name: u.Key(hash).Pretty(),
   184  			Type: fuse.DT_Dir,
   185  		}
   186  		listing = append(listing, ent)
   187  	}
   188  	return listing, nil
   189  }
   190  
   191  // Node is the core object representing a filesystem tree node.
   192  type Node struct {
   193  	root   *Root
   194  	nsRoot *Node
   195  	parent *Node
   196  
   197  	repub *Republisher
   198  
   199  	// This nodes name in its parent dir.
   200  	// NOTE: this strategy wont work well if we allow hard links
   201  	// (im all for murdering the thought of hard links)
   202  	name string
   203  
   204  	// Private keys held by nodes at the root of a keyspace
   205  	// WARNING(security): the PrivKey interface is currently insecure
   206  	// (holds the raw key). It will be secured later.
   207  	key ci.PrivKey
   208  
   209  	Ipfs   *core.IpfsNode
   210  	Nd     *mdag.Node
   211  	dagMod *uio.DagModifier
   212  	cached *ftpb.Data
   213  }
   214  
   215  func (s *Node) loadData() error {
   216  	s.cached = new(ftpb.Data)
   217  	return proto.Unmarshal(s.Nd.Data, s.cached)
   218  }
   219  
   220  // Attr returns the attributes of a given node.
   221  func (s *Node) Attr() fuse.Attr {
   222  	if s.cached == nil {
   223  		err := s.loadData()
   224  		if err != nil {
   225  			log.Errorf("Error loading PBData for file: '%s'", s.name)
   226  		}
   227  	}
   228  	switch s.cached.GetType() {
   229  	case ftpb.Data_Directory:
   230  		return fuse.Attr{Mode: os.ModeDir | 0555}
   231  	case ftpb.Data_File, ftpb.Data_Raw:
   232  		size, err := ft.DataSize(s.Nd.Data)
   233  		if err != nil {
   234  			log.Errorf("Error getting size of file: %s", err)
   235  			size = 0
   236  		}
   237  		if size == 0 {
   238  			size = s.dagMod.Size()
   239  		}
   240  		return fuse.Attr{
   241  			Mode:   0666,
   242  			Size:   size,
   243  			Blocks: uint64(len(s.Nd.Links)),
   244  		}
   245  	default:
   246  		log.Error("Invalid data type.")
   247  		return fuse.Attr{}
   248  	}
   249  }
   250  
   251  // Lookup performs a lookup under this node.
   252  func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
   253  	log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name)
   254  	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
   255  	if err != nil {
   256  		// todo: make this error more versatile.
   257  		return nil, fuse.ENOENT
   258  	}
   259  
   260  	return s.makeChild(name, nd), nil
   261  }
   262  
   263  func (n *Node) makeChild(name string, node *mdag.Node) *Node {
   264  	child := &Node{
   265  		Ipfs:   n.Ipfs,
   266  		Nd:     node,
   267  		name:   name,
   268  		nsRoot: n.nsRoot,
   269  		parent: n,
   270  	}
   271  
   272  	// Always ensure that each child knows where the root is
   273  	if n.nsRoot == nil {
   274  		child.nsRoot = n
   275  	} else {
   276  		child.nsRoot = n.nsRoot
   277  	}
   278  
   279  	return child
   280  }
   281  
   282  // ReadDir reads the link structure as directory entries
   283  func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
   284  	log.Debug("Node ReadDir")
   285  	entries := make([]fuse.Dirent, len(s.Nd.Links))
   286  	for i, link := range s.Nd.Links {
   287  		n := link.Name
   288  		if len(n) == 0 {
   289  			n = link.Hash.B58String()
   290  		}
   291  		entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
   292  	}
   293  
   294  	if len(entries) > 0 {
   295  		return entries, nil
   296  	}
   297  	return nil, fuse.ENOENT
   298  }
   299  
   300  // ReadAll reads the object data as file data
   301  func (s *Node) ReadAll(intr fs.Intr) ([]byte, fuse.Error) {
   302  	log.Debugf("ipns: ReadAll [%s]", s.name)
   303  	r, err := uio.NewDagReader(s.Nd, s.Ipfs.DAG)
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  	// this is a terrible function... 'ReadAll'?
   308  	// what if i have a 6TB file? GG RAM.
   309  	b, err := ioutil.ReadAll(r)
   310  	if err != nil {
   311  		log.Errorf("[%s] Readall error: %s", s.name, err)
   312  		return nil, err
   313  	}
   314  	return b, nil
   315  }
   316  
   317  func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
   318  	log.Debugf("ipns: Node Write [%s]: flags = %s, offset = %d, size = %d", n.name, req.Flags.String(), req.Offset, len(req.Data))
   319  
   320  	if n.dagMod == nil {
   321  		// Create a DagModifier to allow us to change the existing dag node
   322  		dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter)
   323  		if err != nil {
   324  			log.Errorf("Error creating dag modifier: %s", err)
   325  			return err
   326  		}
   327  		n.dagMod = dmod
   328  	}
   329  	wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset))
   330  	if err != nil {
   331  		return err
   332  	}
   333  	resp.Size = wrote
   334  	return nil
   335  }
   336  
   337  func (n *Node) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error {
   338  	log.Debugf("Got flush request [%s]!", n.name)
   339  
   340  	// If a write has happened
   341  	if n.dagMod != nil {
   342  		newNode, err := n.dagMod.GetNode()
   343  		if err != nil {
   344  			log.Errorf("Error getting dag node from dagMod: %s", err)
   345  			return err
   346  		}
   347  
   348  		if n.parent != nil {
   349  			log.Debug("updating self in parent!")
   350  			err := n.parent.update(n.name, newNode)
   351  			if err != nil {
   352  				log.Criticalf("error in updating ipns dag tree: %s", err)
   353  				// return fuse.ETHISISPRETTYBAD
   354  				return err
   355  			}
   356  		}
   357  		n.Nd = newNode
   358  
   359  		/*/TEMP
   360  		dr, err := mdag.NewDagReader(n.Nd, n.Ipfs.DAG)
   361  		if err != nil {
   362  			log.Critical("Verification read failed.")
   363  		}
   364  		b, err := ioutil.ReadAll(dr)
   365  		if err != nil {
   366  			log.Critical("Verification read failed.")
   367  		}
   368  		fmt.Println("VERIFICATION READ")
   369  		fmt.Printf("READ %d BYTES\n", len(b))
   370  		fmt.Println(string(b))
   371  		fmt.Println(b)
   372  		//*/
   373  
   374  		n.dagMod = nil
   375  
   376  		n.wasChanged()
   377  	}
   378  	return nil
   379  }
   380  
   381  // Signal that a node in this tree was changed so the root can republish
   382  func (n *Node) wasChanged() {
   383  	root := n.nsRoot
   384  	if root == nil {
   385  		root = n
   386  	}
   387  
   388  	root.repub.Publish <- struct{}{}
   389  }
   390  
   391  func (n *Node) republishRoot() error {
   392  	log.Debug("Republish root")
   393  
   394  	// We should already be the root, this is just a sanity check
   395  	var root *Node
   396  	if n.nsRoot != nil {
   397  		root = n.nsRoot
   398  	} else {
   399  		root = n
   400  	}
   401  
   402  	// Add any nodes that may be new to the DAG service
   403  	err := n.Ipfs.DAG.AddRecursive(root.Nd)
   404  	if err != nil {
   405  		log.Criticalf("ipns: Dag Add Error: %s", err)
   406  		return err
   407  	}
   408  
   409  	ndkey, err := root.Nd.Key()
   410  	if err != nil {
   411  		log.Errorf("getKey error: %s", err)
   412  		return err
   413  	}
   414  	log.Debug("Publishing changes!")
   415  
   416  	err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty())
   417  	if err != nil {
   418  		log.Errorf("ipns: Publish Failed: %s", err)
   419  		return err
   420  	}
   421  	return nil
   422  }
   423  
   424  func (n *Node) Fsync(req *fuse.FsyncRequest, intr fs.Intr) fuse.Error {
   425  	log.Debug("Got fsync request!")
   426  	return nil
   427  }
   428  
   429  func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
   430  	log.Debug("Got mkdir request!")
   431  	dagnd := &mdag.Node{Data: ft.FolderPBData()}
   432  	nnode := n.Nd.Copy()
   433  	nnode.AddNodeLink(req.Name, dagnd)
   434  
   435  	child := &Node{
   436  		Ipfs: n.Ipfs,
   437  		Nd:   dagnd,
   438  		name: req.Name,
   439  	}
   440  
   441  	if n.nsRoot == nil {
   442  		child.nsRoot = n
   443  	} else {
   444  		child.nsRoot = n.nsRoot
   445  	}
   446  
   447  	if n.parent != nil {
   448  		err := n.parent.update(n.name, nnode)
   449  		if err != nil {
   450  			log.Criticalf("Error updating node: %s", err)
   451  			return nil, err
   452  		}
   453  	}
   454  	n.Nd = nnode
   455  
   456  	n.wasChanged()
   457  
   458  	return child, nil
   459  }
   460  
   461  func (n *Node) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) {
   462  	//log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
   463  	//TODO: check open flags and truncate if necessary
   464  	if req.Flags&fuse.OpenTruncate != 0 {
   465  		log.Warning("Need to truncate file!")
   466  		n.cached = nil
   467  		n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
   468  	} else if req.Flags&fuse.OpenAppend != 0 {
   469  		log.Warning("Need to append to file!")
   470  	}
   471  	return n, nil
   472  }
   473  
   474  func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) {
   475  	log.Debug("Got mknod request!")
   476  	return nil, nil
   477  }
   478  
   479  func (n *Node) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, intr fs.Intr) (fs.Node, fs.Handle, fuse.Error) {
   480  	log.Debugf("Got create request: %s", req.Name)
   481  
   482  	// New 'empty' file
   483  	nd := &mdag.Node{Data: ft.FilePBData(nil, 0)}
   484  	child := n.makeChild(req.Name, nd)
   485  
   486  	nnode := n.Nd.Copy()
   487  
   488  	err := nnode.AddNodeLink(req.Name, nd)
   489  	if err != nil {
   490  		log.Errorf("Error adding child to node: %s", err)
   491  		return nil, nil, err
   492  	}
   493  	if n.parent != nil {
   494  		err := n.parent.update(n.name, nnode)
   495  		if err != nil {
   496  			log.Criticalf("Error updating node: %s", err)
   497  			// Can we panic, please?
   498  			return nil, nil, err
   499  		}
   500  	}
   501  	n.Nd = nnode
   502  	n.wasChanged()
   503  
   504  	return child, child, nil
   505  }
   506  
   507  func (n *Node) Remove(req *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
   508  	log.Debugf("[%s] Got Remove request: %s", n.name, req.Name)
   509  	nnode := n.Nd.Copy()
   510  	err := nnode.RemoveNodeLink(req.Name)
   511  	if err != nil {
   512  		log.Error("Remove: No such file.")
   513  		return fuse.ENOENT
   514  	}
   515  
   516  	if n.parent != nil {
   517  		err := n.parent.update(n.name, nnode)
   518  		if err != nil {
   519  			log.Criticalf("Error updating node: %s", err)
   520  			return err
   521  		}
   522  	}
   523  	n.Nd = nnode
   524  	n.wasChanged()
   525  	return nil
   526  }
   527  
   528  func (n *Node) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error {
   529  	log.Debugf("Got Rename request '%s' -> '%s'", req.OldName, req.NewName)
   530  	var mdn *mdag.Node
   531  	for _, l := range n.Nd.Links {
   532  		if l.Name == req.OldName {
   533  			mdn = l.Node
   534  		}
   535  	}
   536  	if mdn == nil {
   537  		log.Critical("nil Link found on rename!")
   538  		return fuse.ENOENT
   539  	}
   540  	n.Nd.RemoveNodeLink(req.OldName)
   541  
   542  	switch newDir := newDir.(type) {
   543  	case *Node:
   544  		err := newDir.Nd.AddNodeLink(req.NewName, mdn)
   545  		if err != nil {
   546  			log.Errorf("Error adding node to new dir on rename: %s", err)
   547  			return err
   548  		}
   549  	default:
   550  		log.Critical("Unknown node type for rename target dir!")
   551  		return errors.New("Unknown fs node type!")
   552  	}
   553  	return nil
   554  }
   555  
   556  // Updates the child of this node, specified by name to the given newnode
   557  func (n *Node) update(name string, newnode *mdag.Node) error {
   558  	log.Debugf("update '%s' in '%s'", name, n.name)
   559  	nnode := n.Nd.Copy()
   560  	err := nnode.RemoveNodeLink(name)
   561  	if err != nil {
   562  		return err
   563  	}
   564  	nnode.AddNodeLink(name, newnode)
   565  
   566  	if n.parent != nil {
   567  		err := n.parent.update(n.name, nnode)
   568  		if err != nil {
   569  			return err
   570  		}
   571  	}
   572  	n.Nd = nnode
   573  	return nil
   574  }