ipns_unix.go 14 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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19
	ci "github.com/jbenet/go-ipfs/p2p/crypto"
20 21
	ft "github.com/jbenet/go-ipfs/unixfs"
	uio "github.com/jbenet/go-ipfs/unixfs/io"
22
	ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
23 24 25
	u "github.com/jbenet/go-ipfs/util"
)

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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35
// FileSystem is the readwrite IPNS Fuse Filesystem.
36 37 38 39 40 41
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
42
func NewFileSystem(ipfs *core.IpfsNode, sk ci.PrivKey, ipfspath string) (*FileSystem, error) {
43
	root, err := CreateRoot(ipfs, []ci.PrivKey{sk}, ipfspath)
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
	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
69
			log.Errorf("Read Root Error: %s", err)
70 71 72 73 74
			return nil, err
		}
		root.LocalLink = &Link{u.Key(hash).Pretty()}
	}

75 76 77 78 79 80 81 82 83 84
	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
85
		nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
86 87

		go nd.repub.Run()
88 89 90

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

97
		if !u.IsValidHash(pointsTo) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98
			log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo)
99 100 101
			return nil, nil
		}

102 103 104 105 106 107 108 109 110 111
		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
	}

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	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
139
	log.Debugf("ipns: Root Lookup: '%s'", name)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	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
158
	log.Debugf("ipns: Falling back to resolution for [%s].", name)
159 160
	resolved, err := s.Ipfs.Namesys.Resolve(name)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
161
		log.Warningf("ipns: namesys resolve error: %s", err)
162 163 164
		return nil, fuse.ENOENT
	}

Jeromy's avatar
Jeromy committed
165
	return &Link{s.IpfsRoot + "/" + resolved}, nil
166 167 168 169
}

// ReadDir reads a particular directory. Disallowed for root.
func (r *Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
170
	log.Debug("Read Root.")
171 172 173 174 175 176 177 178 179 180
	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
181
			log.Errorf("Read Root Error: %s", err)
182 183 184 185 186 187 188 189 190 191 192 193 194
			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
195
	root   *Root
196
	nsRoot *Node
Jeromy's avatar
Jeromy committed
197
	parent *Node
198

199 200
	repub *Republisher

Jeromy's avatar
Jeromy committed
201 202 203
	// 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)
204
	name string
205 206

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

211 212
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
213
	dagMod *uio.DagModifier
214
	cached *ftpb.Data
215 216 217
}

func (s *Node) loadData() error {
218
	s.cached = new(ftpb.Data)
219 220 221 222 223 224
	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
225 226
		err := s.loadData()
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
227
			log.Errorf("Error loading PBData for file: '%s'", s.name)
Jeromy's avatar
Jeromy committed
228
		}
229 230
	}
	switch s.cached.GetType() {
231
	case ftpb.Data_Directory:
232
		return fuse.Attr{Mode: os.ModeDir | 0555}
233
	case ftpb.Data_File, ftpb.Data_Raw:
234
		size, err := ft.DataSize(s.Nd.Data)
Jeromy's avatar
Jeromy committed
235
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
236
			log.Errorf("Error getting size of file: %s", err)
Jeromy's avatar
Jeromy committed
237 238
			size = 0
		}
Jeromy's avatar
Jeromy committed
239 240 241
		if size == 0 {
			size = s.dagMod.Size()
		}
Jeromy's avatar
Jeromy committed
242 243 244 245 246 247

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

248
		return fuse.Attr{
Jeromy's avatar
Jeromy committed
249
			Mode:   mode,
Jeromy's avatar
Jeromy committed
250
			Size:   size,
251 252 253
			Blocks: uint64(len(s.Nd.Links)),
		}
	default:
254
		log.Error("Invalid data type.")
255 256 257 258 259 260
		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
261
	log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name)
262 263 264 265 266 267
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

268 269 270 271
	return s.makeChild(name, nd), nil
}

func (n *Node) makeChild(name string, node *mdag.Node) *Node {
272
	child := &Node{
Jeromy's avatar
Jeromy committed
273 274 275 276
		Ipfs:   n.Ipfs,
		Nd:     node,
		name:   name,
		nsRoot: n.nsRoot,
Jeromy's avatar
Jeromy committed
277
		parent: n,
278 279
	}

Jeromy's avatar
Jeromy committed
280
	// Always ensure that each child knows where the root is
281 282
	if n.nsRoot == nil {
		child.nsRoot = n
283
	} else {
284
		child.nsRoot = n.nsRoot
285 286
	}

287
	return child
288 289 290 291
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
Jeromy's avatar
Jeromy committed
292
	log.Debug("Node ReadDir")
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	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
310
	log.Debugf("ipns: ReadAll [%s]", s.name)
311
	r, err := uio.NewDagReader(s.Nd, s.Ipfs.DAG)
312 313 314 315 316
	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
317 318
	b, err := ioutil.ReadAll(r)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
319
		log.Errorf("[%s] Readall error: %s", s.name, err)
Jeromy's avatar
Jeromy committed
320 321 322
		return nil, err
	}
	return b, nil
323 324
}

325
func (n *Node) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
326
	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
327 328 329 330
	if IpnsReadonly {
		log.Error("Attempted to write on readonly ipns filesystem.")
		return fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
331

Jeromy's avatar
Jeromy committed
332
	if n.dagMod == nil {
Jeromy's avatar
Jeromy committed
333
		// Create a DagModifier to allow us to change the existing dag node
334
		dmod, err := uio.NewDagModifier(n.Nd, n.Ipfs.DAG, chunk.DefaultSplitter)
Jeromy's avatar
Jeromy committed
335
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
336
			log.Errorf("Error creating dag modifier: %s", err)
Jeromy's avatar
Jeromy committed
337 338 339
			return err
		}
		n.dagMod = dmod
340
	}
Jeromy's avatar
Jeromy committed
341
	wrote, err := n.dagMod.WriteAt(req.Data, uint64(req.Offset))
Jeromy's avatar
Jeromy committed
342 343
	if err != nil {
		return err
344
	}
Jeromy's avatar
Jeromy committed
345
	resp.Size = wrote
346 347 348 349
	return nil
}

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

Jeromy's avatar
Jeromy committed
355
	// If a write has happened
Jeromy's avatar
Jeromy committed
356 357
	if n.dagMod != nil {
		newNode, err := n.dagMod.GetNode()
358
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
359
			log.Errorf("Error getting dag node from dagMod: %s", err)
Jeromy's avatar
Jeromy committed
360
			return err
361
		}
Jeromy's avatar
Jeromy committed
362

Jeromy's avatar
Jeromy committed
363
		if n.parent != nil {
Jeromy's avatar
Jeromy committed
364
			log.Debug("updating self in parent!")
Jeromy's avatar
Jeromy committed
365 366
			err := n.parent.update(n.name, newNode)
			if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
367
				log.Criticalf("error in updating ipns dag tree: %s", err)
Jeromy's avatar
Jeromy committed
368
				// return fuse.ETHISISPRETTYBAD
Jeromy's avatar
Jeromy committed
369
				return err
Jeromy's avatar
Jeromy committed
370 371 372
			}
		}
		n.Nd = newNode
373

374
		/*/TEMP
Jeromy's avatar
Jeromy committed
375 376 377 378 379 380 381 382 383 384 385
		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
386
		fmt.Println(b)
387
		//*/
Jeromy's avatar
Jeromy committed
388

Jeromy's avatar
Jeromy committed
389
		n.dagMod = nil
Jeromy's avatar
Jeromy committed
390

Jeromy's avatar
Jeromy committed
391
		n.wasChanged()
392 393 394
	}
	return nil
}
395

Jeromy's avatar
Jeromy committed
396
// Signal that a node in this tree was changed so the root can republish
Jeromy's avatar
Jeromy committed
397
func (n *Node) wasChanged() {
Jeromy's avatar
Jeromy committed
398 399 400
	if IpnsReadonly {
		return
	}
401 402 403 404 405 406 407 408
	root := n.nsRoot
	if root == nil {
		root = n
	}

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

Jeromy's avatar
Jeromy committed
409 410
func (n *Node) republishRoot() error {
	log.Debug("Republish root")
Jeromy's avatar
Jeromy committed
411 412

	// We should already be the root, this is just a sanity check
413 414 415 416 417 418
	var root *Node
	if n.nsRoot != nil {
		root = n.nsRoot
	} else {
		root = n
	}
419

Jeromy's avatar
Jeromy committed
420 421
	// Add any nodes that may be new to the DAG service
	err := n.Ipfs.DAG.AddRecursive(root.Nd)
422
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
423
		log.Criticalf("ipns: Dag Add Error: %s", err)
424 425
		return err
	}
426

427 428
	ndkey, err := root.Nd.Key()
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
429
		log.Errorf("getKey error: %s", err)
430 431 432
		return err
	}
	log.Debug("Publishing changes!")
433

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
434
	err = n.Ipfs.Namesys.Publish(root.key, ndkey.Pretty())
435
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
436
		log.Errorf("ipns: Publish Failed: %s", err)
437
		return err
438 439 440 441 442 443 444 445 446
	}
	return nil
}

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

447 448
func (n *Node) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mkdir request!")
Jeromy's avatar
Jeromy committed
449 450 451 452
	if IpnsReadonly {
		log.Error("Attempted to call mkdir on readonly filesystem.")
		return nil, fuse.EPERM
	}
453
	dagnd := &mdag.Node{Data: ft.FolderPBData()}
Jeromy's avatar
Jeromy committed
454 455
	nnode := n.Nd.Copy()
	nnode.AddNodeLink(req.Name, dagnd)
456 457 458 459

	child := &Node{
		Ipfs: n.Ipfs,
		Nd:   dagnd,
Jeromy's avatar
Jeromy committed
460
		name: req.Name,
461 462 463 464 465 466 467 468
	}

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

Jeromy's avatar
Jeromy committed
469 470 471
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
472
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
473
			return nil, err
Jeromy's avatar
Jeromy committed
474 475 476 477 478
		}
	}
	n.Nd = nnode

	n.wasChanged()
479

480 481 482 483
	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
484
	//log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
485
	//TODO: check open flags and truncate if necessary
486 487
	if req.Flags&fuse.OpenTruncate != 0 {
		log.Warning("Need to truncate file!")
Jeromy's avatar
Jeromy committed
488 489 490
		n.cached = nil
		n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
	} else if req.Flags&fuse.OpenAppend != 0 {
491 492
		log.Warning("Need to append to file!")
	}
493 494 495
	return n, nil
}

496 497 498 499 500
func (n *Node) Mknod(req *fuse.MknodRequest, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debug("Got mknod request!")
	return nil, nil
}

501
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
502
	log.Debugf("Got create request: %s", req.Name)
Jeromy's avatar
Jeromy committed
503 504 505 506
	if IpnsReadonly {
		log.Error("Attempted to call Create on a readonly filesystem.")
		return nil, nil, fuse.EPERM
	}
Jeromy's avatar
Jeromy committed
507 508

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

Jeromy's avatar
Jeromy committed
512 513 514
	nnode := n.Nd.Copy()

	err := nnode.AddNodeLink(req.Name, nd)
515
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
516
		log.Errorf("Error adding child to node: %s", err)
Jeromy's avatar
Jeromy committed
517
		return nil, nil, err
518
	}
Jeromy's avatar
Jeromy committed
519 520 521
	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
522
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
523
			// Can we panic, please?
Jeromy's avatar
Jeromy committed
524
			return nil, nil, err
Jeromy's avatar
Jeromy committed
525 526 527 528 529
		}
	}
	n.Nd = nnode
	n.wasChanged()

530
	return child, child, nil
531 532 533
}

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

Jeromy's avatar
Jeromy committed
540 541
	nnode := n.Nd.Copy()
	err := nnode.RemoveNodeLink(req.Name)
542 543 544 545
	if err != nil {
		log.Error("Remove: No such file.")
		return fuse.ENOENT
	}
Jeromy's avatar
Jeromy committed
546 547 548 549

	if n.parent != nil {
		err := n.parent.update(n.name, nnode)
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
550
			log.Criticalf("Error updating node: %s", err)
Jeromy's avatar
Jeromy committed
551
			return err
Jeromy's avatar
Jeromy committed
552 553 554 555
		}
	}
	n.Nd = nnode
	n.wasChanged()
556 557 558 559
	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
560
	log.Debugf("Got Rename request '%s' -> '%s'", req.OldName, req.NewName)
Jeromy's avatar
Jeromy committed
561 562 563 564 565
	if IpnsReadonly {
		log.Error("Attempted to call Rename on a readonly filesystem.")
		return fuse.EPERM
	}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
	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
582
			log.Errorf("Error adding node to new dir on rename: %s", err)
Jeromy's avatar
Jeromy committed
583
			return err
584 585 586
		}
	default:
		log.Critical("Unknown node type for rename target dir!")
587
		return errors.New("Unknown fs node type!")
588 589
	}
	return nil
590
}
Jeromy's avatar
Jeromy committed
591

Jeromy's avatar
Jeromy committed
592
// Updates the child of this node, specified by name to the given newnode
Jeromy's avatar
Jeromy committed
593
func (n *Node) update(name string, newnode *mdag.Node) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
594
	log.Debugf("update '%s' in '%s'", name, n.name)
Jeromy's avatar
Jeromy committed
595 596 597 598 599 600 601 602
	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
603
		err := n.parent.update(n.name, nnode)
Jeromy's avatar
Jeromy committed
604 605 606 607 608 609 610
		if err != nil {
			return err
		}
	}
	n.Nd = nnode
	return nil
}