ipns_unix.go 13 KB
Newer Older
1 2 3
package ipns

import (
4
	"errors"
5 6 7 8 9
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11 12
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13

14 15
	"github.com/jbenet/go-ipfs/core"
	ci "github.com/jbenet/go-ipfs/crypto"
16
	"github.com/jbenet/go-ipfs/importer/chunk"
17
	mdag "github.com/jbenet/go-ipfs/merkledag"
18 19
	ft "github.com/jbenet/go-ipfs/unixfs"
	uio "github.com/jbenet/go-ipfs/unixfs/io"
20 21 22
	u "github.com/jbenet/go-ipfs/util"
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23
var log = u.Logger("ipns")
24

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25 26 27 28 29
var (
	shortRepublishTimeout = time.Millisecond * 5
	longRepublishTimeout  = time.Millisecond * 500
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30
// FileSystem is the readwrite IPNS Fuse Filesystem.
31 32 33 34 35 36 37
type FileSystem struct {
	Ipfs     *core.IpfsNode
	RootNode *Root
}

// NewFileSystem constructs new fs using given core.IpfsNode instance.
func NewIpns(ipfs *core.IpfsNode, ipfspath string) (*FileSystem, error) {
38
	root, err := CreateRoot(ipfs, []ci.PrivKey{ipfs.Identity.PrivKey()}, ipfspath)
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	if err != nil {
		return nil, err
	}
	return &FileSystem{Ipfs: ipfs, RootNode: root}, nil
}

func CreateRoot(n *core.IpfsNode, keys []ci.PrivKey, ipfsroot string) (*Root, error) {
	root := new(Root)
	root.LocalDirs = make(map[string]*Node)
	root.Ipfs = n
	abspath, err := filepath.Abs(ipfsroot)
	if err != nil {
		return nil, err
	}
	root.IpfsRoot = abspath

	root.Keys = keys

	if len(keys) == 0 {
		log.Warning("No keys given for ipns root creation")
	} else {
		k := keys[0]
		pub := k.GetPublic()
		hash, err := pub.Hash()
		if err != nil {
			log.Error("Read Root Error: %s", err)
			return nil, err
		}
		root.LocalLink = &Link{u.Key(hash).Pretty()}
	}

70 71 72 73 74 75 76 77 78 79
	for _, k := range keys {
		hash, err := k.GetPublic().Hash()
		if err != nil {
			log.Error("failed to hash public key.")
			continue
		}
		name := u.Key(hash).Pretty()
		nd := new(Node)
		nd.Ipfs = n
		nd.key = k
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80
		nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
81 82

		go nd.repub.Run()
83 84 85

		pointsTo, err := n.Namesys.Resolve(name)
		if err != nil {
Jeromy's avatar
Jeromy committed
86
			log.Warning("Could not resolve value for local ipns entry, providing empty dir")
87
			nd.Nd = &mdag.Node{Data: ft.FolderPBData()}
Jeromy's avatar
Jeromy committed
88
			root.LocalDirs[name] = nd
89 90 91
			continue
		}

92 93 94 95 96
		if !u.IsValidHash(pointsTo) {
			log.Critical("Got back bad data from namesys resolve! [%s]", pointsTo)
			return nil, nil
		}

97 98 99 100 101 102 103 104 105 106
		node, err := n.Resolver.ResolvePath(pointsTo)
		if err != nil {
			log.Warning("Failed to resolve value from ipns entry in ipfs")
			continue
		}

		nd.Nd = node
		root.LocalDirs[name] = nd
	}

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	return root, nil
}

// Root constructs the Root of the filesystem, a Root object.
func (f FileSystem) Root() (fs.Node, fuse.Error) {
	return f.RootNode, nil
}

// Root is the root object of the filesystem tree.
type Root struct {
	Ipfs *core.IpfsNode
	Keys []ci.PrivKey

	// Used for symlinking into ipfs
	IpfsRoot  string
	LocalDirs map[string]*Node

	LocalLink *Link
}

// Attr returns file attributes.
func (*Root) Attr() fuse.Attr {
	return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
}

// Lookup performs a lookup under this node.
func (s *Root) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
134
	log.Debug("ipns: Root Lookup: '%s'", name)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	switch name {
	case "mach_kernel", ".hidden", "._.":
		// Just quiet some log noise on OS X.
		return nil, fuse.ENOENT
	}

	if name == "local" {
		if s.LocalLink == nil {
			return nil, fuse.ENOENT
		}
		return s.LocalLink, nil
	}

	nd, ok := s.LocalDirs[name]
	if ok {
		return nd, nil
	}

153
	log.Debug("ipns: Falling back to resolution for [%s].", name)
154 155
	resolved, err := s.Ipfs.Namesys.Resolve(name)
	if err != nil {
Jeromy's avatar
Jeromy committed
156
		log.Warning("ipns: namesys resolve error: %s", err)
157 158 159
		return nil, fuse.ENOENT
	}

Jeromy's avatar
Jeromy committed
160
	return &Link{s.IpfsRoot + "/" + resolved}, nil
161 162 163 164
}

// ReadDir reads a particular directory. Disallowed for root.
func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
165
	log.Debug("Read Root.")
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	listing := []fuse.Dirent{
		fuse.Dirent{
			Name: "local",
			Type: fuse.DT_Link,
		},
	}
	for _, k := range r.Keys {
		pub := k.GetPublic()
		hash, err := pub.Hash()
		if err != nil {
			log.Error("Read Root Error: %s", err)
			continue
		}
		ent := fuse.Dirent{
			Name: u.Key(hash).Pretty(),
			Type: fuse.DT_Dir,
		}
		listing = append(listing, ent)
	}
	return listing, nil
}

// Node is the core object representing a filesystem tree node.
type Node struct {
Jeromy's avatar
Jeromy committed
190
	root   *Root
191
	nsRoot *Node
Jeromy's avatar
Jeromy committed
192
	parent *Node
193

194 195
	repub *Republisher

Jeromy's avatar
Jeromy committed
196 197 198
	// This nodes name in its parent dir.
	// NOTE: this strategy wont work well if we allow hard links
	// (im all for murdering the thought of hard links)
199
	name string
200 201

	// Private keys held by nodes at the root of a keyspace
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
202 203
	// WARNING(security): the PrivKey interface is currently insecure
	// (holds the raw key). It will be secured later.
204 205
	key ci.PrivKey

206 207
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
208
	dagMod *uio.DagModifier
209
	cached *ft.PBData
210 211 212
}

func (s *Node) loadData() error {
213
	s.cached = new(ft.PBData)
214 215 216 217 218 219
	return proto.Unmarshal(s.Nd.Data, s.cached)
}

// Attr returns the attributes of a given node.
func (s *Node) Attr() fuse.Attr {
	if s.cached == nil {
Jeromy's avatar
Jeromy committed
220 221 222 223
		err := s.loadData()
		if err != nil {
			log.Error("Error loading PBData for file: '%s'", s.name)
		}
224 225
	}
	switch s.cached.GetType() {
226
	case ft.PBData_Directory:
227
		return fuse.Attr{Mode: os.ModeDir | 0555}
228 229
	case ft.PBData_File, ft.PBData_Raw:
		size, err := ft.DataSize(s.Nd.Data)
Jeromy's avatar
Jeromy committed
230 231 232 233
		if err != nil {
			log.Error("Error getting size of file: %s", err)
			size = 0
		}
234
		return fuse.Attr{
235
			Mode:   0666,
Jeromy's avatar
Jeromy committed
236
			Size:   size,
237 238 239
			Blocks: uint64(len(s.Nd.Links)),
		}
	default:
240
		log.Error("Invalid data type.")
241 242 243 244 245 246
		return fuse.Attr{}
	}
}

// Lookup performs a lookup under this node.
func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
247
	log.Debug("ipns: node[%s] Lookup '%s'", s.name, name)
248 249 250 251 252 253
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

254 255 256 257
	return s.makeChild(name, nd), nil
}

func (n *Node) makeChild(name string, node *mdag.Node) *Node {
258
	child := &Node{
Jeromy's avatar
Jeromy committed
259 260 261 262
		Ipfs:   n.Ipfs,
		Nd:     node,
		name:   name,
		nsRoot: n.nsRoot,
Jeromy's avatar
Jeromy committed
263
		parent: n,
264 265
	}

Jeromy's avatar
Jeromy committed
266
	// Always ensure that each child knows where the root is
267 268
	if n.nsRoot == nil {
		child.nsRoot = n
269
	} else {
270
		child.nsRoot = n.nsRoot
271 272
	}

273
	return child
274 275 276 277
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
278
	log.Debug("Node ReadDir")
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	entries := make([]fuse.Dirent, len(s.Nd.Links))
	for i, link := range s.Nd.Links {
		n := link.Name
		if len(n) == 0 {
			n = link.Hash.B58String()
		}
		entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
	}

	if len(entries) > 0 {
		return entries, nil
	}
	return nil, fuse.ENOENT
}

// ReadAll reads the object data as file data
func (s *Node) ReadAll(intr fs.Intr) ([]byte, fuse.Error) {
Jeromy's avatar
Jeromy committed
296
	log.Debug("ipns: ReadAll [%s]", s.name)
297
	r, err := uio.NewDagReader(s.Nd, s.Ipfs.DAG)
298 299 300 301 302
	if err != nil {
		return nil, err
	}
	// this is a terrible function... 'ReadAll'?
	// what if i have a 6TB file? GG RAM.
Jeromy's avatar
Jeromy committed
303 304 305 306 307 308
	b, err := ioutil.ReadAll(r)
	if err != nil {
		log.Error("[%s] Readall error: %s", s.name, err)
		return nil, err
	}
	return b, nil
309 310
}

311
func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
Jeromy's avatar
Jeromy committed
312
	log.Debug("ipns: Node Write [%s]: flags = %s, offset = %d, size = %d", n.name, req.Flags.String(), req.Offset, len(req.Data))
Jeromy's avatar
Jeromy committed
313

Jeromy's avatar
Jeromy committed
314
	if n.dagMod == nil {
Jeromy's avatar
Jeromy committed
315
		// Create a DagModifier to allow us to change the existing dag node
316
		dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter)
Jeromy's avatar
Jeromy committed
317 318 319 320 321
		if err != nil {
			log.Error("Error creating dag modifier: %s", err)
			return err
		}
		n.dagMod = dmod
322
	}
Jeromy's avatar
Jeromy committed
323
	wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset))
Jeromy's avatar
Jeromy committed
324 325
	if err != nil {
		return err
326
	}
Jeromy's avatar
Jeromy committed
327
	resp.Size = wrote
328 329 330 331
	return nil
}

func (n *Node) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error {
Jeromy's avatar
Jeromy committed
332
	log.Debug("Got flush request [%s]!", n.name)
333

Jeromy's avatar
Jeromy committed
334
	// If a write has happened
Jeromy's avatar
Jeromy committed
335 336
	if n.dagMod != nil {
		newNode, err := n.dagMod.GetNode()
337
		if err != nil {
Jeromy's avatar
Jeromy committed
338
			log.Error("Error getting dag node from dagMod: %s", err)
Jeromy's avatar
Jeromy committed
339
			return err
340
		}
Jeromy's avatar
Jeromy committed
341

Jeromy's avatar
Jeromy committed
342
		if n.parent != nil {
Jeromy's avatar
Jeromy committed
343
			log.Debug("updating self in parent!")
Jeromy's avatar
Jeromy committed
344 345 346 347
			err := n.parent.update(n.name, newNode)
			if err != nil {
				log.Critical("error in updating ipns dag tree: %s", err)
				// return fuse.ETHISISPRETTYBAD
Jeromy's avatar
Jeromy committed
348
				return err
Jeromy's avatar
Jeromy committed
349 350 351
			}
		}
		n.Nd = newNode
352

353
		/*/TEMP
Jeromy's avatar
Jeromy committed
354 355 356 357 358 359 360 361 362 363 364
		dr, err := mdag.NewDagReader(n.Nd, n.Ipfs.DAG)
		if err != nil {
			log.Critical("Verification read failed.")
		}
		b, err := ioutil.ReadAll(dr)
		if err != nil {
			log.Critical("Verification read failed.")
		}
		fmt.Println("VERIFICATION READ")
		fmt.Printf("READ %d BYTES\n", len(b))
		fmt.Println(string(b))
Jeromy's avatar
Jeromy committed
365
		fmt.Println(b)
366
		//*/
Jeromy's avatar
Jeromy committed
367

Jeromy's avatar
Jeromy committed
368
		n.dagMod = nil
Jeromy's avatar
Jeromy committed
369

Jeromy's avatar
Jeromy committed
370
		n.wasChanged()
371 372 373
	}
	return nil
}
374

Jeromy's avatar
Jeromy committed
375
// Signal that a node in this tree was changed so the root can republish
Jeromy's avatar
Jeromy committed
376
func (n *Node) wasChanged() {
377 378 379 380 381 382 383 384
	root := n.nsRoot
	if root == nil {
		root = n
	}

	root.repub.Publish <- struct{}{}
}

Jeromy's avatar
Jeromy committed
385 386
func (n *Node) republishRoot() error {
	log.Debug("Republish root")
Jeromy's avatar
Jeromy committed
387 388

	// We should already be the root, this is just a sanity check
389 390 391 392 393 394
	var root *Node
	if n.nsRoot != nil {
		root = n.nsRoot
	} else {
		root = n
	}
395

Jeromy's avatar
Jeromy committed
396 397
	// Add any nodes that may be new to the DAG service
	err := n.Ipfs.DAG.AddRecursive(root.Nd)
398 399 400 401
	if err != nil {
		log.Critical("ipns: Dag Add Error: %s", err)
		return err
	}
402

403 404 405 406 407 408
	ndkey, err := root.Nd.Key()
	if err != nil {
		log.Error("getKey error: %s", err)
		return err
	}
	log.Debug("Publishing changes!")
409

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
410
	err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty())
411 412 413
	if err != nil {
		log.Error("ipns: Publish Failed: %s", err)
		return err
414 415 416 417 418 419 420 421 422
	}
	return nil
}

func (n *Node) Fsync(req *fuse.FsyncRequest, intr fs.Intr) fuse.Error {
	log.Debug("Got fsync request!")
	return nil
}

423 424
func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mkdir request!")
425
	dagnd := &mdag.Node{Data: ft.FolderPBData()}
Jeromy's avatar
Jeromy committed
426 427
	nnode := n.Nd.Copy()
	nnode.AddNodeLink(req.Name, dagnd)
428 429 430 431

	child := &Node{
		Ipfs: n.Ipfs,
		Nd:   dagnd,
Jeromy's avatar
Jeromy committed
432
		name: req.Name,
433 434 435 436 437 438 439 440
	}

	if n.nsRoot == nil {
		child.nsRoot = n
	} else {
		child.nsRoot = n.nsRoot
	}

Jeromy's avatar
Jeromy committed
441 442 443 444
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
			log.Critical("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
445
			return nil, err
Jeromy's avatar
Jeromy committed
446 447 448 449 450
		}
	}
	n.Nd = nnode

	n.wasChanged()
451

452 453 454 455
	return child, nil
}

func (n *Node) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) {
Jeromy's avatar
Jeromy committed
456
	//log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
457
	//TODO: check open flags and truncate if necessary
458 459
	if req.Flags&fuse.OpenTruncate != 0 {
		log.Warning("Need to truncate file!")
Jeromy's avatar
Jeromy committed
460 461 462
		n.cached = nil
		n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
	} else if req.Flags&fuse.OpenAppend != 0 {
463 464
		log.Warning("Need to append to file!")
	}
465 466 467
	return n, nil
}

468 469 470 471 472
func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mknod request!")
	return nil, nil
}

473
func (n *Node) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, intr fs.Intr) (fs.Node, fs.Handle, fuse.Error) {
474
	log.Debug("Got create request: %s", req.Name)
Jeromy's avatar
Jeromy committed
475 476

	// New 'empty' file
477
	nd := &mdag.Node{Data: ft.FilePBData(nil, 0)}
478 479
	child := n.makeChild(req.Name, nd)

Jeromy's avatar
Jeromy committed
480 481 482
	nnode := n.Nd.Copy()

	err := nnode.AddNodeLink(req.Name, nd)
483 484
	if err != nil {
		log.Error("Error adding child to node: %s", err)
Jeromy's avatar
Jeromy committed
485
		return nil, nil, err
486
	}
Jeromy's avatar
Jeromy committed
487 488 489 490 491
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
			log.Critical("Error updating node: %s", err)
			// Can we panic, please?
Jeromy's avatar
Jeromy committed
492
			return nil, nil, err
Jeromy's avatar
Jeromy committed
493 494 495 496 497
		}
	}
	n.Nd = nnode
	n.wasChanged()

498
	return child, child, nil
499 500 501
}

func (n *Node) Remove(req *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
502
	log.Debug("[%s] Got Remove request: %s", n.name, req.Name)
Jeromy's avatar
Jeromy committed
503 504
	nnode := n.Nd.Copy()
	err := nnode.RemoveNodeLink(req.Name)
505 506 507 508
	if err != nil {
		log.Error("Remove: No such file.")
		return fuse.ENOENT
	}
Jeromy's avatar
Jeromy committed
509 510 511 512 513

	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
			log.Critical("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
514
			return err
Jeromy's avatar
Jeromy committed
515 516 517 518
		}
	}
	n.Nd = nnode
	n.wasChanged()
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
	return nil
}

func (n *Node) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error {
	log.Debug("Got Rename request '%s' -> '%s'", req.OldName, req.NewName)
	var mdn *mdag.Node
	for _, l := range n.Nd.Links {
		if l.Name == req.OldName {
			mdn = l.Node
		}
	}
	if mdn == nil {
		log.Critical("nil Link found on rename!")
		return fuse.ENOENT
	}
	n.Nd.RemoveNodeLink(req.OldName)

	switch newDir := newDir.(type) {
	case *Node:
		err := newDir.Nd.AddNodeLink(req.NewName, mdn)
		if err != nil {
			log.Error("Error adding node to new dir on rename: %s", err)
Jeromy's avatar
Jeromy committed
541
			return err
542 543 544
		}
	default:
		log.Critical("Unknown node type for rename target dir!")
545
		return errors.New("Unknown fs node type!")
546 547
	}
	return nil
548
}
Jeromy's avatar
Jeromy committed
549

Jeromy's avatar
Jeromy committed
550
// Updates the child of this node, specified by name to the given newnode
Jeromy's avatar
Jeromy committed
551
func (n *Node) update(name string, newnode *mdag.Node) error {
Jeromy's avatar
Jeromy committed
552
	log.Debug("update '%s' in '%s'", name, n.name)
Jeromy's avatar
Jeromy committed
553 554 555 556 557 558 559 560
	nnode := n.Nd.Copy()
	err := nnode.RemoveNodeLink(name)
	if err != nil {
		return err
	}
	nnode.AddNodeLink(name, newnode)

	if n.parent != nil {
Jeromy's avatar
Jeromy committed
561
		err := n.parent.update(n.name, nnode)
Jeromy's avatar
Jeromy committed
562 563 564 565 566 567 568
		if err != nil {
			return err
		}
	}
	n.Nd = nnode
	return nil
}