ipns_unix.go 14.3 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 14
	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"
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15

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

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

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

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

Jeromy's avatar
Jeromy committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
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
	}

	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
52
// FileSystem is the readwrite IPNS Fuse Filesystem.
53 54 55 56 57 58
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
59
func NewFileSystem(ipfs *core.IpfsNode, sk ci.PrivKey, ipfspath string) (*FileSystem, error) {
60
	root, err := CreateRoot(ipfs, []ci.PrivKey{sk}, ipfspath)
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	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
86
			log.Errorf("Read Root Error: %s", err)
87 88 89 90 91
			return nil, err
		}
		root.LocalLink = &Link{u.Key(hash).Pretty()}
	}

92 93 94 95 96 97 98 99 100 101
	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
102
		nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
103 104

		go nd.repub.Run()
105 106 107

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

114
		if !u.IsValidHash(pointsTo) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115
			log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo)
116 117 118
			return nil, nil
		}

119 120 121 122 123 124 125 126 127 128
		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
	}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	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
156
	log.Debugf("ipns: Root Lookup: '%s'", name)
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
	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
175
	log.Debugf("ipns: Falling back to resolution for [%s].", name)
176 177
	resolved, err := s.Ipfs.Namesys.Resolve(name)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178
		log.Warningf("ipns: namesys resolve error: %s", err)
179 180 181
		return nil, fuse.ENOENT
	}

Jeromy's avatar
Jeromy committed
182
	return &Link{s.IpfsRoot + "/" + resolved}, nil
183 184 185 186
}

// ReadDir reads a particular directory. Disallowed for root.
func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
187
	log.Debug("Read Root.")
188 189 190 191 192 193 194 195 196 197
	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
198
			log.Errorf("Read Root Error: %s", err)
199 200 201 202 203 204 205 206 207 208 209 210 211
			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
212
	root   *Root
213
	nsRoot *Node
Jeromy's avatar
Jeromy committed
214
	parent *Node
215

216 217
	repub *Republisher

Jeromy's avatar
Jeromy committed
218 219 220
	// 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)
221
	name string
222 223

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

228 229
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
230
	dagMod *uio.DagModifier
231
	cached *ftpb.Data
232 233 234
}

func (s *Node) loadData() error {
235
	s.cached = new(ftpb.Data)
236 237 238 239 240 241
	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
242 243
		err := s.loadData()
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244
			log.Errorf("Error loading PBData for file: '%s'", s.name)
Jeromy's avatar
Jeromy committed
245
		}
246 247
	}
	switch s.cached.GetType() {
248
	case ftpb.Data_Directory:
249
		return fuse.Attr{Mode: os.ModeDir | 0555}
250
	case ftpb.Data_File, ftpb.Data_Raw:
251
		size, err := ft.DataSize(s.Nd.Data)
Jeromy's avatar
Jeromy committed
252
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
253
			log.Errorf("Error getting size of file: %s", err)
Jeromy's avatar
Jeromy committed
254 255
			size = 0
		}
Jeromy's avatar
Jeromy committed
256 257 258
		if size == 0 {
			size = s.dagMod.Size()
		}
Jeromy's avatar
Jeromy committed
259 260 261 262 263 264

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

265
		return fuse.Attr{
Jeromy's avatar
Jeromy committed
266
			Mode:   mode,
Jeromy's avatar
Jeromy committed
267
			Size:   size,
268 269 270
			Blocks: uint64(len(s.Nd.Links)),
		}
	default:
271
		log.Error("Invalid data type.")
272 273 274 275 276 277
		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
278
	log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name)
279 280 281 282 283 284
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

285 286 287 288
	return s.makeChild(name, nd), nil
}

func (n *Node) makeChild(name string, node *mdag.Node) *Node {
289
	child := &Node{
Jeromy's avatar
Jeromy committed
290 291 292 293
		Ipfs:   n.Ipfs,
		Nd:     node,
		name:   name,
		nsRoot: n.nsRoot,
Jeromy's avatar
Jeromy committed
294
		parent: n,
295 296
	}

Jeromy's avatar
Jeromy committed
297
	// Always ensure that each child knows where the root is
298 299
	if n.nsRoot == nil {
		child.nsRoot = n
300
	} else {
301
		child.nsRoot = n.nsRoot
302 303
	}

304
	return child
305 306 307 308
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
309
	log.Debug("Node ReadDir")
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	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
327
	log.Debugf("ipns: ReadAll [%s]", s.name)
328
	r, err := uio.NewDagReader(s.Nd, s.Ipfs.DAG)
329 330 331 332 333
	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
334 335
	b, err := ioutil.ReadAll(r)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
336
		log.Errorf("[%s] Readall error: %s", s.name, err)
Jeromy's avatar
Jeromy committed
337 338 339
		return nil, err
	}
	return b, nil
340 341
}

342
func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
343
	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
344 345 346 347
	if IpnsReadonly {
		log.Error("Attempted to write on readonly ipns filesystem.")
		return fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
348

Jeromy's avatar
Jeromy committed
349
	if n.dagMod == nil {
Jeromy's avatar
Jeromy committed
350
		// Create a DagModifier to allow us to change the existing dag node
351
		dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter)
Jeromy's avatar
Jeromy committed
352
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
353
			log.Errorf("Error creating dag modifier: %s", err)
Jeromy's avatar
Jeromy committed
354 355 356
			return err
		}
		n.dagMod = dmod
357
	}
Jeromy's avatar
Jeromy committed
358
	wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset))
Jeromy's avatar
Jeromy committed
359 360
	if err != nil {
		return err
361
	}
Jeromy's avatar
Jeromy committed
362
	resp.Size = wrote
363 364 365 366
	return nil
}

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

Jeromy's avatar
Jeromy committed
372
	// If a write has happened
Jeromy's avatar
Jeromy committed
373 374
	if n.dagMod != nil {
		newNode, err := n.dagMod.GetNode()
375
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
376
			log.Errorf("Error getting dag node from dagMod: %s", err)
Jeromy's avatar
Jeromy committed
377
			return err
378
		}
Jeromy's avatar
Jeromy committed
379

Jeromy's avatar
Jeromy committed
380
		if n.parent != nil {
Jeromy's avatar
Jeromy committed
381
			log.Debug("updating self in parent!")
Jeromy's avatar
Jeromy committed
382 383
			err := n.parent.update(n.name, newNode)
			if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
384
				log.Criticalf("error in updating ipns dag tree: %s", err)
Jeromy's avatar
Jeromy committed
385
				// return fuse.ETHISISPRETTYBAD
Jeromy's avatar
Jeromy committed
386
				return err
Jeromy's avatar
Jeromy committed
387 388 389
			}
		}
		n.Nd = newNode
390

391
		/*/TEMP
Jeromy's avatar
Jeromy committed
392 393 394 395 396 397 398 399 400 401 402
		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
403
		fmt.Println(b)
404
		//*/
Jeromy's avatar
Jeromy committed
405

Jeromy's avatar
Jeromy committed
406
		n.dagMod = nil
Jeromy's avatar
Jeromy committed
407

Jeromy's avatar
Jeromy committed
408
		n.wasChanged()
409 410 411
	}
	return nil
}
412

Jeromy's avatar
Jeromy committed
413
// Signal that a node in this tree was changed so the root can republish
Jeromy's avatar
Jeromy committed
414
func (n *Node) wasChanged() {
Jeromy's avatar
Jeromy committed
415 416 417
	if IpnsReadonly {
		return
	}
418 419 420 421 422 423 424 425
	root := n.nsRoot
	if root == nil {
		root = n
	}

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

Jeromy's avatar
Jeromy committed
426 427
func (n *Node) republishRoot() error {
	log.Debug("Republish root")
Jeromy's avatar
Jeromy committed
428 429

	// We should already be the root, this is just a sanity check
430 431 432 433 434 435
	var root *Node
	if n.nsRoot != nil {
		root = n.nsRoot
	} else {
		root = n
	}
436

Jeromy's avatar
Jeromy committed
437 438
	// Add any nodes that may be new to the DAG service
	err := n.Ipfs.DAG.AddRecursive(root.Nd)
439
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
440
		log.Criticalf("ipns: Dag Add Error: %s", err)
441 442
		return err
	}
443

444 445
	ndkey, err := root.Nd.Key()
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
446
		log.Errorf("getKey error: %s", err)
447 448 449
		return err
	}
	log.Debug("Publishing changes!")
450

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
451
	err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty())
452
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
453
		log.Errorf("ipns: Publish Failed: %s", err)
454
		return err
455 456 457 458 459 460 461 462 463
	}
	return nil
}

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

464 465
func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mkdir request!")
Jeromy's avatar
Jeromy committed
466 467 468 469
	if IpnsReadonly {
		log.Error("Attempted to call mkdir on readonly filesystem.")
		return nil, fuse.EPERM
	}
470
	dagnd := &mdag.Node{Data: ft.FolderPBData()}
Jeromy's avatar
Jeromy committed
471 472
	nnode := n.Nd.Copy()
	nnode.AddNodeLink(req.Name, dagnd)
473 474 475 476

	child := &Node{
		Ipfs: n.Ipfs,
		Nd:   dagnd,
Jeromy's avatar
Jeromy committed
477
		name: req.Name,
478 479 480 481 482 483 484 485
	}

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

Jeromy's avatar
Jeromy committed
486 487 488
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
489
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
490
			return nil, err
Jeromy's avatar
Jeromy committed
491 492 493 494 495
		}
	}
	n.Nd = nnode

	n.wasChanged()
496

497 498 499 500
	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
501
	//log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
502
	//TODO: check open flags and truncate if necessary
503 504
	if req.Flags&fuse.OpenTruncate != 0 {
		log.Warning("Need to truncate file!")
Jeromy's avatar
Jeromy committed
505 506 507
		n.cached = nil
		n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
	} else if req.Flags&fuse.OpenAppend != 0 {
508 509
		log.Warning("Need to append to file!")
	}
510 511 512
	return n, nil
}

513 514 515 516 517
func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mknod request!")
	return nil, nil
}

518
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
519
	log.Debugf("Got create request: %s", req.Name)
Jeromy's avatar
Jeromy committed
520 521 522 523
	if IpnsReadonly {
		log.Error("Attempted to call Create on a readonly filesystem.")
		return nil, nil, fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
524 525

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

Jeromy's avatar
Jeromy committed
529 530 531
	nnode := n.Nd.Copy()

	err := nnode.AddNodeLink(req.Name, nd)
532
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
533
		log.Errorf("Error adding child to node: %s", err)
Jeromy's avatar
Jeromy committed
534
		return nil, nil, err
535
	}
Jeromy's avatar
Jeromy committed
536 537 538
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
539
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
540
			// Can we panic, please?
Jeromy's avatar
Jeromy committed
541
			return nil, nil, err
Jeromy's avatar
Jeromy committed
542 543 544 545 546
		}
	}
	n.Nd = nnode
	n.wasChanged()

547
	return child, child, nil
548 549 550
}

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

Jeromy's avatar
Jeromy committed
557 558
	nnode := n.Nd.Copy()
	err := nnode.RemoveNodeLink(req.Name)
559 560 561 562
	if err != nil {
		log.Error("Remove: No such file.")
		return fuse.ENOENT
	}
Jeromy's avatar
Jeromy committed
563 564 565 566

	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
567
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
568
			return err
Jeromy's avatar
Jeromy committed
569 570 571 572
		}
	}
	n.Nd = nnode
	n.wasChanged()
573 574 575 576
	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
577
	log.Debugf("Got Rename request '%s' -> '%s'", req.OldName, req.NewName)
Jeromy's avatar
Jeromy committed
578 579 580 581 582
	if IpnsReadonly {
		log.Error("Attempted to call Rename on a readonly filesystem.")
		return fuse.EPERM
	}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
	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
599
			log.Errorf("Error adding node to new dir on rename: %s", err)
Jeromy's avatar
Jeromy committed
600
			return err
601 602 603
		}
	default:
		log.Critical("Unknown node type for rename target dir!")
604
		return errors.New("Unknown fs node type!")
605 606
	}
	return nil
607
}
Jeromy's avatar
Jeromy committed
608

Jeromy's avatar
Jeromy committed
609
// Updates the child of this node, specified by name to the given newnode
Jeromy's avatar
Jeromy committed
610
func (n *Node) update(name string, newnode *mdag.Node) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
611
	log.Debugf("update '%s' in '%s'", name, n.name)
Jeromy's avatar
Jeromy committed
612 613 614 615 616 617 618 619
	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
620
		err := n.parent.update(n.name, nnode)
Jeromy's avatar
Jeromy committed
621 622 623 624 625 626 627
		if err != nil {
			return err
		}
	}
	n.Nd = nnode
	return nil
}