ipns_unix.go 14.7 KB
Newer Older
1 2
// package fuse/ipns implements a fuse filesystem that interfaces
// with ipns, the naming system for ipfs.
3 4 5
package ipns

import (
6
	"errors"
7 8 9 10 11
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

12 13
	fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
	fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
Jeromy's avatar
Jeromy committed
14
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
15
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16

17 18
	core "github.com/jbenet/go-ipfs/core"
	chunk "github.com/jbenet/go-ipfs/importer/chunk"
19
	mdag "github.com/jbenet/go-ipfs/merkledag"
Jeromy's avatar
Jeromy committed
20
	nsys "github.com/jbenet/go-ipfs/namesys"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21
	ci "github.com/jbenet/go-ipfs/p2p/crypto"
22 23
	ft "github.com/jbenet/go-ipfs/unixfs"
	uio "github.com/jbenet/go-ipfs/unixfs/io"
24
	ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
25 26 27
	u "github.com/jbenet/go-ipfs/util"
)

Jeromy's avatar
Jeromy committed
28 29
const IpnsReadonly = true

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32 33 34 35 36
var (
	shortRepublishTimeout = time.Millisecond * 5
	longRepublishTimeout  = time.Millisecond * 500
)

Jeromy's avatar
Jeromy committed
37 38
// InitializeKeyspace sets the ipns record for the given key to
// point to an empty directory.
Jeromy's avatar
Jeromy committed
39 40 41 42 43 44 45
func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
	emptyDir := &mdag.Node{Data: ft.FolderPBData()}
	k, err := n.DAG.Add(emptyDir)
	if err != nil {
		return err
	}

46 47 48 49 50 51 52 53 54 55
	err = n.Pinning.Pin(emptyDir, false)
	if err != nil {
		return err
	}

	err = n.Pinning.Flush()
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
56 57 58 59 60 61 62 63 64
	pub := nsys.NewRoutingPublisher(n.Routing)
	err = pub.Publish(key, k.B58String())
	if err != nil {
		return err
	}

	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
65
// FileSystem is the readwrite IPNS Fuse Filesystem.
66 67 68 69 70 71
type FileSystem struct {
	Ipfs     *core.IpfsNode
	RootNode *Root
}

// NewFileSystem constructs new fs using given core.IpfsNode instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72
func NewFileSystem(ipfs *core.IpfsNode, sk ci.PrivKey, ipfspath string) (*FileSystem, error) {
73
	root, err := CreateRoot(ipfs, []ci.PrivKey{sk}, ipfspath)
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	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 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
			log.Errorf("Read Root Error: %s", err)
100 101 102 103 104
			return nil, err
		}
		root.LocalLink = &Link{u.Key(hash).Pretty()}
	}

105 106 107 108 109 110 111 112 113 114
	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
115
		nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
116 117

		go nd.repub.Run()
118 119 120

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

127
		if !u.IsValidHash(pointsTo) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128
			log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo)
129 130 131
			return nil, nil
		}

132 133 134 135 136 137 138 139 140 141
		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
	}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	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) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
169
	log.Debugf("ipns: Root Lookup: '%s'", name)
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	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
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188
	log.Debugf("ipns: Falling back to resolution for [%s].", name)
189 190
	resolved, err := s.Ipfs.Namesys.Resolve(name)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
191
		log.Warningf("ipns: namesys resolve error: %s", err)
192 193 194
		return nil, fuse.ENOENT
	}

Jeromy's avatar
Jeromy committed
195
	return &Link{s.IpfsRoot + "/" + resolved}, nil
196 197 198 199
}

// ReadDir reads a particular directory. Disallowed for root.
func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
200
	log.Debug("Read Root.")
201 202 203 204 205 206 207 208 209 210
	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 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211
			log.Errorf("Read Root Error: %s", err)
212 213 214 215 216 217 218 219 220 221 222 223 224
			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
225
	root   *Root
226
	nsRoot *Node
Jeromy's avatar
Jeromy committed
227
	parent *Node
228

229 230
	repub *Republisher

Jeromy's avatar
Jeromy committed
231 232 233
	// 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)
234
	name string
235 236

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

241 242
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
243
	dagMod *uio.DagModifier
244
	cached *ftpb.Data
245 246 247
}

func (s *Node) loadData() error {
248
	s.cached = new(ftpb.Data)
249 250 251 252 253 254
	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
255 256
		err := s.loadData()
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
257
			log.Errorf("Error loading PBData for file: '%s'", s.name)
Jeromy's avatar
Jeromy committed
258
		}
259 260
	}
	switch s.cached.GetType() {
261
	case ftpb.Data_Directory:
262
		return fuse.Attr{Mode: os.ModeDir | 0555}
263
	case ftpb.Data_File, ftpb.Data_Raw:
264
		size, err := ft.DataSize(s.Nd.Data)
Jeromy's avatar
Jeromy committed
265
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
266
			log.Errorf("Error getting size of file: %s", err)
Jeromy's avatar
Jeromy committed
267 268
			size = 0
		}
Jeromy's avatar
Jeromy committed
269 270 271
		if size == 0 {
			size = s.dagMod.Size()
		}
Jeromy's avatar
Jeromy committed
272 273 274 275 276 277

		mode := os.FileMode(0666)
		if IpnsReadonly {
			mode = 0444
		}

278
		return fuse.Attr{
Jeromy's avatar
Jeromy committed
279
			Mode:   mode,
Jeromy's avatar
Jeromy committed
280
			Size:   size,
281 282 283
			Blocks: uint64(len(s.Nd.Links)),
		}
	default:
284
		log.Error("Invalid data type.")
285 286 287 288 289 290
		return fuse.Attr{}
	}
}

// Lookup performs a lookup under this node.
func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
291
	log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name)
292 293 294 295 296 297
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

298 299 300 301
	return s.makeChild(name, nd), nil
}

func (n *Node) makeChild(name string, node *mdag.Node) *Node {
302
	child := &Node{
Jeromy's avatar
Jeromy committed
303 304 305 306
		Ipfs:   n.Ipfs,
		Nd:     node,
		name:   name,
		nsRoot: n.nsRoot,
Jeromy's avatar
Jeromy committed
307
		parent: n,
308 309
	}

Jeromy's avatar
Jeromy committed
310
	// Always ensure that each child knows where the root is
311 312
	if n.nsRoot == nil {
		child.nsRoot = n
313
	} else {
314
		child.nsRoot = n.nsRoot
315 316
	}

317
	return child
318 319 320 321
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
322
	log.Debug("Node ReadDir")
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	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) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
340
	log.Debugf("ipns: ReadAll [%s]", s.name)
Jeromy's avatar
Jeromy committed
341
	r, err := uio.NewDagReader(context.TODO(), s.Nd, s.Ipfs.DAG)
342 343 344 345 346
	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
347 348
	b, err := ioutil.ReadAll(r)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
349
		log.Errorf("[%s] Readall error: %s", s.name, err)
Jeromy's avatar
Jeromy committed
350 351 352
		return nil, err
	}
	return b, nil
353 354
}

355
func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
356
	log.Debugf("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
357 358 359 360
	if IpnsReadonly {
		log.Error("Attempted to write on readonly ipns filesystem.")
		return fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
361

Jeromy's avatar
Jeromy committed
362
	if n.dagMod == nil {
Jeromy's avatar
Jeromy committed
363
		// Create a DagModifier to allow us to change the existing dag node
364
		dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter)
Jeromy's avatar
Jeromy committed
365
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
366
			log.Errorf("Error creating dag modifier: %s", err)
Jeromy's avatar
Jeromy committed
367 368 369
			return err
		}
		n.dagMod = dmod
370
	}
Jeromy's avatar
Jeromy committed
371
	wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset))
Jeromy's avatar
Jeromy committed
372 373
	if err != nil {
		return err
374
	}
Jeromy's avatar
Jeromy committed
375
	resp.Size = wrote
376 377 378 379
	return nil
}

func (n *Node) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
380
	log.Debugf("Got flush request [%s]!", n.name)
Jeromy's avatar
Jeromy committed
381 382 383
	if IpnsReadonly {
		return nil
	}
384

Jeromy's avatar
Jeromy committed
385
	// If a write has happened
Jeromy's avatar
Jeromy committed
386 387
	if n.dagMod != nil {
		newNode, err := n.dagMod.GetNode()
388
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
389
			log.Errorf("Error getting dag node from dagMod: %s", err)
Jeromy's avatar
Jeromy committed
390
			return err
391
		}
Jeromy's avatar
Jeromy committed
392

Jeromy's avatar
Jeromy committed
393
		if n.parent != nil {
Jeromy's avatar
Jeromy committed
394
			log.Debug("updating self in parent!")
Jeromy's avatar
Jeromy committed
395 396
			err := n.parent.update(n.name, newNode)
			if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
397
				log.Criticalf("error in updating ipns dag tree: %s", err)
Jeromy's avatar
Jeromy committed
398
				// return fuse.ETHISISPRETTYBAD
Jeromy's avatar
Jeromy committed
399
				return err
Jeromy's avatar
Jeromy committed
400 401 402
			}
		}
		n.Nd = newNode
403

404
		/*/TEMP
Jeromy's avatar
Jeromy committed
405 406 407 408 409 410 411 412 413 414 415
		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
416
		fmt.Println(b)
417
		//*/
Jeromy's avatar
Jeromy committed
418

Jeromy's avatar
Jeromy committed
419
		n.dagMod = nil
Jeromy's avatar
Jeromy committed
420

Jeromy's avatar
Jeromy committed
421
		n.wasChanged()
422 423 424
	}
	return nil
}
425

Jeromy's avatar
Jeromy committed
426
// Signal that a node in this tree was changed so the root can republish
Jeromy's avatar
Jeromy committed
427
func (n *Node) wasChanged() {
Jeromy's avatar
Jeromy committed
428 429 430
	if IpnsReadonly {
		return
	}
431 432 433 434 435 436 437 438
	root := n.nsRoot
	if root == nil {
		root = n
	}

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

Jeromy's avatar
Jeromy committed
439 440
func (n *Node) republishRoot() error {
	log.Debug("Republish root")
Jeromy's avatar
Jeromy committed
441 442

	// We should already be the root, this is just a sanity check
443 444 445 446 447 448
	var root *Node
	if n.nsRoot != nil {
		root = n.nsRoot
	} else {
		root = n
	}
449

Jeromy's avatar
Jeromy committed
450 451
	// Add any nodes that may be new to the DAG service
	err := n.Ipfs.DAG.AddRecursive(root.Nd)
452
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
453
		log.Criticalf("ipns: Dag Add Error: %s", err)
454 455
		return err
	}
456

457 458
	ndkey, err := root.Nd.Key()
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
459
		log.Errorf("getKey error: %s", err)
460 461 462
		return err
	}
	log.Debug("Publishing changes!")
463

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
464
	err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty())
465
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
466
		log.Errorf("ipns: Publish Failed: %s", err)
467
		return err
468 469 470 471 472 473 474 475 476
	}
	return nil
}

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

477 478
func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mkdir request!")
Jeromy's avatar
Jeromy committed
479 480 481 482
	if IpnsReadonly {
		log.Error("Attempted to call mkdir on readonly filesystem.")
		return nil, fuse.EPERM
	}
483
	dagnd := &mdag.Node{Data: ft.FolderPBData()}
Jeromy's avatar
Jeromy committed
484 485
	nnode := n.Nd.Copy()
	nnode.AddNodeLink(req.Name, dagnd)
486 487 488 489

	child := &Node{
		Ipfs: n.Ipfs,
		Nd:   dagnd,
Jeromy's avatar
Jeromy committed
490
		name: req.Name,
491 492 493 494 495 496 497 498
	}

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

Jeromy's avatar
Jeromy committed
499 500 501
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
502
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
503
			return nil, err
Jeromy's avatar
Jeromy committed
504 505 506 507 508
		}
	}
	n.Nd = nnode

	n.wasChanged()
509

510 511 512 513
	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
514
	//log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
515
	//TODO: check open flags and truncate if necessary
516 517
	if req.Flags&fuse.OpenTruncate != 0 {
		log.Warning("Need to truncate file!")
Jeromy's avatar
Jeromy committed
518 519 520
		n.cached = nil
		n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
	} else if req.Flags&fuse.OpenAppend != 0 {
521 522
		log.Warning("Need to append to file!")
	}
523 524 525
	return n, nil
}

526 527 528 529 530
func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mknod request!")
	return nil, nil
}

531
func (n *Node) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, intr fs.Intr) (fs.Node, fs.Handle, fuse.Error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
532
	log.Debugf("Got create request: %s", req.Name)
Jeromy's avatar
Jeromy committed
533 534 535 536
	if IpnsReadonly {
		log.Error("Attempted to call Create on a readonly filesystem.")
		return nil, nil, fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
537 538

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

Jeromy's avatar
Jeromy committed
542 543 544
	nnode := n.Nd.Copy()

	err := nnode.AddNodeLink(req.Name, nd)
545
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
546
		log.Errorf("Error adding child to node: %s", err)
Jeromy's avatar
Jeromy committed
547
		return nil, nil, err
548
	}
Jeromy's avatar
Jeromy committed
549 550 551
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
552
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
553
			// Can we panic, please?
Jeromy's avatar
Jeromy committed
554
			return nil, nil, err
Jeromy's avatar
Jeromy committed
555 556 557 558 559
		}
	}
	n.Nd = nnode
	n.wasChanged()

560
	return child, child, nil
561 562 563
}

func (n *Node) Remove(req *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
564
	log.Debugf("[%s] Got Remove request: %s", n.name, req.Name)
Jeromy's avatar
Jeromy committed
565 566 567 568 569
	if IpnsReadonly {
		log.Error("Attempted to call Remove on a readonly filesystem.")
		return fuse.EPERM
	}

Jeromy's avatar
Jeromy committed
570 571
	nnode := n.Nd.Copy()
	err := nnode.RemoveNodeLink(req.Name)
572 573 574 575
	if err != nil {
		log.Error("Remove: No such file.")
		return fuse.ENOENT
	}
Jeromy's avatar
Jeromy committed
576 577 578 579

	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
580
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
581
			return err
Jeromy's avatar
Jeromy committed
582 583 584 585
		}
	}
	n.Nd = nnode
	n.wasChanged()
586 587 588 589
	return nil
}

func (n *Node) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
590
	log.Debugf("Got Rename request '%s' -> '%s'", req.OldName, req.NewName)
Jeromy's avatar
Jeromy committed
591 592 593 594 595
	if IpnsReadonly {
		log.Error("Attempted to call Rename on a readonly filesystem.")
		return fuse.EPERM
	}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
	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 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
612
			log.Errorf("Error adding node to new dir on rename: %s", err)
Jeromy's avatar
Jeromy committed
613
			return err
614 615 616
		}
	default:
		log.Critical("Unknown node type for rename target dir!")
617
		return errors.New("Unknown fs node type!")
618 619
	}
	return nil
620
}
Jeromy's avatar
Jeromy committed
621

Jeromy's avatar
Jeromy committed
622
// Updates the child of this node, specified by name to the given newnode
Jeromy's avatar
Jeromy committed
623
func (n *Node) update(name string, newnode *mdag.Node) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
624
	log.Debugf("update '%s' in '%s'", name, n.name)
Jeromy's avatar
Jeromy committed
625 626 627 628 629 630 631 632
	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
633
		err := n.parent.update(n.name, nnode)
Jeromy's avatar
Jeromy committed
634 635 636 637 638 639 640
		if err != nil {
			return err
		}
	}
	n.Nd = nnode
	return nil
}