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

     1  // package ipnsfs implements an in memory model of a mutable ipns filesystem,
     2  // to be used by the fuse filesystem.
     3  //
     4  // It consists of four main structs:
     5  // 1) The Filesystem
     6  //        The filesystem serves as a container and entry point for the ipns filesystem
     7  // 2) KeyRoots
     8  //        KeyRoots represent the root of the keyspace controlled by a given keypair
     9  // 3) Directories
    10  // 4) Files
    11  package ipnsfs
    12  
    13  import (
    14  	"errors"
    15  	"os"
    16  	"sync"
    17  	"time"
    18  
    19  	key "github.com/ipfs/go-ipfs/blocks/key"
    20  	dag "github.com/ipfs/go-ipfs/merkledag"
    21  	namesys "github.com/ipfs/go-ipfs/namesys"
    22  	ci "github.com/ipfs/go-ipfs/p2p/crypto"
    23  	path "github.com/ipfs/go-ipfs/path"
    24  	pin "github.com/ipfs/go-ipfs/pin"
    25  	ft "github.com/ipfs/go-ipfs/unixfs"
    26  
    27  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    28  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    29  )
    30  
    31  var log = eventlog.Logger("ipnsfs")
    32  
    33  var ErrIsDirectory = errors.New("error: is a directory")
    34  
    35  // Filesystem is the writeable fuse filesystem structure
    36  type Filesystem struct {
    37  	ctx context.Context
    38  
    39  	dserv dag.DAGService
    40  
    41  	nsys namesys.NameSystem
    42  
    43  	resolver *path.Resolver
    44  
    45  	pins pin.Pinner
    46  
    47  	roots map[string]*KeyRoot
    48  }
    49  
    50  // NewFilesystem instantiates an ipns filesystem using the given parameters and locally owned keys
    51  func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSystem, pins pin.Pinner, keys ...ci.PrivKey) (*Filesystem, error) {
    52  	roots := make(map[string]*KeyRoot)
    53  	fs := &Filesystem{
    54  		ctx:      ctx,
    55  		roots:    roots,
    56  		nsys:     nsys,
    57  		dserv:    ds,
    58  		pins:     pins,
    59  		resolver: &path.Resolver{DAG: ds},
    60  	}
    61  	for _, k := range keys {
    62  		pkh, err := k.GetPublic().Hash()
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  
    67  		root, err := fs.newKeyRoot(ctx, k)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		roots[key.Key(pkh).Pretty()] = root
    72  	}
    73  
    74  	return fs, nil
    75  }
    76  
    77  func (fs *Filesystem) Close() error {
    78  	wg := sync.WaitGroup{}
    79  	for _, r := range fs.roots {
    80  		wg.Add(1)
    81  		go func(r *KeyRoot) {
    82  			defer wg.Done()
    83  			err := r.Publish(fs.ctx)
    84  			if err != nil {
    85  				log.Info(err)
    86  				return
    87  			}
    88  		}(r)
    89  	}
    90  	wg.Wait()
    91  	return nil
    92  }
    93  
    94  // GetRoot returns the KeyRoot of the given name
    95  func (fs *Filesystem) GetRoot(name string) (*KeyRoot, error) {
    96  	r, ok := fs.roots[name]
    97  	if ok {
    98  		return r, nil
    99  	}
   100  	return nil, os.ErrNotExist
   101  }
   102  
   103  type childCloser interface {
   104  	closeChild(string, *dag.Node) error
   105  }
   106  
   107  type NodeType int
   108  
   109  const (
   110  	TFile NodeType = iota
   111  	TDir
   112  )
   113  
   114  // FSNode represents any node (directory, root, or file) in the ipns filesystem
   115  type FSNode interface {
   116  	GetNode() (*dag.Node, error)
   117  	Type() NodeType
   118  	Lock()
   119  	Unlock()
   120  }
   121  
   122  // KeyRoot represents the root of a filesystem tree pointed to by a given keypair
   123  type KeyRoot struct {
   124  	key  ci.PrivKey
   125  	name string
   126  
   127  	// node is the merkledag node pointed to by this keypair
   128  	node *dag.Node
   129  
   130  	// A pointer to the filesystem to access components
   131  	fs *Filesystem
   132  
   133  	// val represents the node pointed to by this key. It can either be a File or a Directory
   134  	val FSNode
   135  
   136  	repub *Republisher
   137  }
   138  
   139  // newKeyRoot creates a new KeyRoot for the given key, and starts up a republisher routine
   140  // for it
   141  func (fs *Filesystem) newKeyRoot(parent context.Context, k ci.PrivKey) (*KeyRoot, error) {
   142  	hash, err := k.GetPublic().Hash()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	name := "/ipns/" + key.Key(hash).String()
   148  
   149  	root := new(KeyRoot)
   150  	root.key = k
   151  	root.fs = fs
   152  	root.name = name
   153  
   154  	ctx, cancel := context.WithCancel(parent)
   155  	defer cancel()
   156  
   157  	pointsTo, err := fs.nsys.Resolve(ctx, name)
   158  	if err != nil {
   159  		err = namesys.InitializeKeyspace(ctx, fs.dserv, fs.nsys, fs.pins, k)
   160  		if err != nil {
   161  			return nil, err
   162  		}
   163  
   164  		pointsTo, err = fs.nsys.Resolve(ctx, name)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  	}
   169  
   170  	mnode, err := fs.resolver.ResolvePath(ctx, pointsTo)
   171  	if err != nil {
   172  		log.Errorf("Failed to retrieve value '%s' for ipns entry: %s\n", pointsTo, err)
   173  		return nil, err
   174  	}
   175  
   176  	root.node = mnode
   177  
   178  	root.repub = NewRepublisher(root, time.Millisecond*300, time.Second*3)
   179  	go root.repub.Run(parent)
   180  
   181  	pbn, err := ft.FromBytes(mnode.Data)
   182  	if err != nil {
   183  		log.Error("IPNS pointer was not unixfs node")
   184  		return nil, err
   185  	}
   186  
   187  	switch pbn.GetType() {
   188  	case ft.TDirectory:
   189  		root.val = NewDirectory(ctx, pointsTo.String(), mnode, root, fs)
   190  	case ft.TFile, ft.TMetadata, ft.TRaw:
   191  		fi, err := NewFile(pointsTo.String(), mnode, root, fs)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		root.val = fi
   196  	default:
   197  		panic("unrecognized! (NYI)")
   198  	}
   199  	return root, nil
   200  }
   201  
   202  func (kr *KeyRoot) GetValue() FSNode {
   203  	return kr.val
   204  }
   205  
   206  // closeChild implements the childCloser interface, and signals to the publisher that
   207  // there are changes ready to be published
   208  func (kr *KeyRoot) closeChild(name string, nd *dag.Node) error {
   209  	kr.repub.Touch()
   210  	return nil
   211  }
   212  
   213  // Publish publishes the ipns entry associated with this key
   214  func (kr *KeyRoot) Publish(ctx context.Context) error {
   215  	child, ok := kr.val.(FSNode)
   216  	if !ok {
   217  		return errors.New("child of key root not valid type")
   218  	}
   219  
   220  	nd, err := child.GetNode()
   221  	if err != nil {
   222  		return err
   223  	}
   224  
   225  	// Holding this lock so our child doesnt change out from under us
   226  	child.Lock()
   227  	k, err := kr.fs.dserv.Add(nd)
   228  	if err != nil {
   229  		child.Unlock()
   230  		return err
   231  	}
   232  	child.Unlock()
   233  	// Dont want to hold the lock while we publish
   234  	// otherwise we are holding the lock through a costly
   235  	// network operation
   236  
   237  	kp := path.FromKey(k)
   238  
   239  	ev := &eventlog.Metadata{"name": kr.name, "key": kp}
   240  	defer log.EventBegin(ctx, "ipnsfsPublishing", ev).Done()
   241  	log.Info("ipnsfs publishing %s -> %s", kr.name, kp)
   242  
   243  	return kr.fs.nsys.Publish(ctx, kr.key, kp)
   244  }
   245  
   246  // Republisher manages when to publish the ipns entry associated with a given key
   247  type Republisher struct {
   248  	TimeoutLong  time.Duration
   249  	TimeoutShort time.Duration
   250  	Publish      chan struct{}
   251  	root         *KeyRoot
   252  }
   253  
   254  // NewRepublisher creates a new Republisher object to republish the given keyroot
   255  // using the given short and long time intervals
   256  func NewRepublisher(root *KeyRoot, tshort, tlong time.Duration) *Republisher {
   257  	return &Republisher{
   258  		TimeoutShort: tshort,
   259  		TimeoutLong:  tlong,
   260  		Publish:      make(chan struct{}, 1),
   261  		root:         root,
   262  	}
   263  }
   264  
   265  // Touch signals that an update has occurred since the last publish.
   266  // Multiple consecutive touches may extend the time period before
   267  // the next Publish occurs in order to more efficiently batch updates
   268  func (np *Republisher) Touch() {
   269  	select {
   270  	case np.Publish <- struct{}{}:
   271  	default:
   272  	}
   273  }
   274  
   275  // Run is the main republisher loop
   276  func (np *Republisher) Run(ctx context.Context) {
   277  	for {
   278  		select {
   279  		case <-np.Publish:
   280  			quick := time.After(np.TimeoutShort)
   281  			longer := time.After(np.TimeoutLong)
   282  
   283  		wait:
   284  			select {
   285  			case <-ctx.Done():
   286  				return
   287  			case <-np.Publish:
   288  				quick = time.After(np.TimeoutShort)
   289  				goto wait
   290  			case <-quick:
   291  			case <-longer:
   292  			}
   293  
   294  			log.Info("Publishing Changes!")
   295  			err := np.root.Publish(ctx)
   296  			if err != nil {
   297  				log.Error("republishRoot error: %s", err)
   298  			}
   299  
   300  		case <-ctx.Done():
   301  			return
   302  		}
   303  	}
   304  }