pathfs.go 14.8 KB
Newer Older
1
package fuse
2

3
import (
4
	"fmt"
5
	"log"
6
	"path/filepath"
7
	"sync"
8 9 10 11
)

var _ = log.Println

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
12
// A parent pointer: node should be reachable as parent.children[name]
13 14 15 16 17 18
type clientInodePath struct {
	parent *pathInode
	name   string
	node   *pathInode
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
19 20 21 22 23 24 25 26
// PathNodeFs is the file system that can translate an inode back to a
// path.  The path name is then used to call into an object that has
// the FileSystem interface.
//
// Lookups (ie. FileSystem.GetAttr) may return a inode number in its
// return value. The inode number ("clientInode") is used to indicate
// linked files. The clientInode is never exported back to the kernel;
// it is only used to maintain a list of all names of an inode.
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
27
type PathNodeFs struct {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
28 29 30
	Debug     bool
	fs        FileSystem
	root      *pathInode
31
	connector *FileSystemConnector
32

33
	// protects clientInodeMap and pathInode.Parent pointers
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
34
	pathLock sync.RWMutex
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
35 36 37

	// This map lists all the parent links known for a given
	// nodeId.
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
38
	clientInodeMap map[uint64][]*clientInodePath
39 40 41 42

	options *PathNodeFsOptions
}

43
func (fs *PathNodeFs) Mount(path string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
44
	dir, name := filepath.Split(path)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
45 46 47
	if dir != "" {
		dir = filepath.Clean(dir)
	}
48
	parent := fs.LookupNode(dir)
49 50 51
	if parent == nil {
		return ENOENT
	}
52
	return fs.connector.Mount(parent, name, nodeFs, opts)
53 54
}

55
// Forgets all known information on client inodes.
56 57
func (fs *PathNodeFs) ForgetClientInodes() {
	if !fs.options.ClientInodes {
58 59
		return
	}
60 61 62 63
	fs.pathLock.Lock()
	defer fs.pathLock.Unlock()
	fs.clientInodeMap = map[uint64][]*clientInodePath{}
	fs.root.forgetClientInodes()
64 65 66
}

// Rereads all inode numbers for all known files.
67 68
func (fs *PathNodeFs) RereadClientInodes() {
	if !fs.options.ClientInodes {
69 70
		return
	}
71 72
	fs.ForgetClientInodes()
	fs.root.updateClientInodes()
73 74
}

75 76
func (fs *PathNodeFs) UnmountNode(node *Inode) Status {
	return fs.connector.Unmount(node)
77 78
}

79 80
func (fs *PathNodeFs) Unmount(path string) Status {
	node := fs.Node(path)
81 82 83
	if node == nil {
		return ENOENT
	}
84
	return fs.connector.Unmount(node)
85 86
}

87
func (fs *PathNodeFs) OnUnmount() {
88 89
}

90 91
func (fs *PathNodeFs) String() string {
	return fmt.Sprintf("PathNodeFs(%v)", fs.fs)
92 93
}

94 95 96
func (fs *PathNodeFs) OnMount(conn *FileSystemConnector) {
	fs.connector = conn
	fs.fs.OnMount(fs)
97 98
}

99 100
func (fs *PathNodeFs) Node(name string) *Inode {
	n, rest := fs.LastNode(name)
101 102 103 104 105 106
	if len(rest) > 0 {
		return nil
	}
	return n
}

107
// Like node, but use Lookup to discover inodes we may not have yet.
108 109
func (fs *PathNodeFs) LookupNode(name string) *Inode {
	return fs.connector.LookupNode(fs.Root().Inode(), name)
110 111
}

112
func (fs *PathNodeFs) Path(node *Inode) string {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
113 114 115 116
	pNode := node.FsNode().(*pathInode)
	return pNode.GetPath()
}

117 118
func (fs *PathNodeFs) LastNode(name string) (*Inode, []string) {
	return fs.connector.Node(fs.Root().Inode(), name)
119 120
}

121 122
func (fs *PathNodeFs) FileNotify(path string, off int64, length int64) Status {
	node, r := fs.connector.Node(fs.root.Inode(), path)
123
	if len(r) > 0 {
124 125
		return ENOENT
	}
126
	return fs.connector.FileNotify(node, off, length)
127 128
}

129 130
func (fs *PathNodeFs) EntryNotify(dir string, name string) Status {
	node, rest := fs.connector.Node(fs.root.Inode(), dir)
131
	if len(rest) > 0 {
132 133
		return ENOENT
	}
134
	return fs.connector.EntryNotify(node, name)
135 136
}

137 138
func (fs *PathNodeFs) Notify(path string) Status {
	node, rest := fs.connector.Node(fs.root.Inode(), path)
139
	if len(rest) > 0 {
140
		return fs.connector.EntryNotify(node, rest[0])
141
	}
142
	return fs.connector.FileNotify(node, 0, 0)
143 144
}

145 146
func (fs *PathNodeFs) AllFiles(name string, mask uint32) []WithFlags {
	n := fs.Node(name)
147 148 149 150 151 152
	if n == nil {
		return nil
	}
	return n.Files(mask)
}

153
func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
154
	root := new(pathInode)
155
	root.fs = fs
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
156

157 158 159 160
	if opts == nil {
		opts = &PathNodeFsOptions{}
	}

161
	pfs := &PathNodeFs{
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
162 163
		fs:             fs,
		root:           root,
164
		clientInodeMap: map[uint64][]*clientInodePath{},
165
		options:        opts,
166
	}
167 168
	root.pathFs = pfs
	return pfs
169 170
}

171 172
func (fs *PathNodeFs) Root() FsNode {
	return fs.root
173 174
}

175 176
// This is a combination of dentry (entry in the file/directory and
// the inode). This structure is used to implement glue for FSes where
177
// there is a one-to-one mapping of paths and inodes.
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
178
type pathInode struct {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
179 180 181
	pathFs *PathNodeFs
	fs     FileSystem
	Name   string
182 183

	// This is nil at the root of the mount.
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
184
	Parent *pathInode
185

186 187 188
	// This is to correctly resolve hardlinks of the underlying
	// real filesystem.
	clientInode uint64
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
189

190
	DefaultFsNode
191 192
}

193
// Drop all known client inodes. Must have the treeLock.
194 195 196
func (n *pathInode) forgetClientInodes() {
	n.clientInode = 0
	for _, ch := range n.Inode().FsChildren() {
197 198 199 200 201
		ch.FsNode().(*pathInode).forgetClientInodes()
	}
}

// Reread all client nodes below this node.  Must run outside the treeLock.
202 203 204
func (n *pathInode) updateClientInodes() {
	n.GetAttr(nil, nil)
	for _, ch := range n.Inode().FsChildren() {
205 206 207 208
		ch.FsNode().(*pathInode).updateClientInodes()
	}
}

209 210 211
func (n *pathInode) LockTree() func() {
	n.pathFs.pathLock.Lock()
	return func() { n.pathFs.pathLock.Unlock() }
212 213
}

214 215 216
func (n *pathInode) RLockTree() func() {
	n.pathFs.pathLock.RLock()
	return func() { n.pathFs.pathLock.RUnlock() }
217 218
}

219 220
func (n *pathInode) fillNewChildAttr(path string, child *pathInode, c *Context) (fi *Attr, code Status) {
	fi, _ = n.fs.GetAttr(path, c)
221 222 223
	if fi != nil && fi.Ino > 0 {
		child.clientInode = fi.Ino
	}
224 225 226 227 228 229

	if fi == nil {
		log.Println("fillNewChildAttr found nil FileInfo", path)
		return nil, ENOENT
	}
	return fi, OK
230 231
}

232 233
// GetPath returns the path relative to the mount governing this
// inode.  It returns nil for mount if the file was deleted or the
234
// filesystem unmounted.
235 236
func (n *pathInode) GetPath() (path string) {
	defer n.RLockTree()()
237 238

	rev_components := make([]string, 0, 10)
239 240 241
	p := n
	for ; p.Parent != nil; p = p.Parent {
		rev_components = append(rev_components, p.Name)
242
	}
243
	if p != p.pathFs.root {
244
		return ".deleted"
245
	}
246 247 248
	path = ReverseJoin(rev_components, "/")
	if n.pathFs.Debug {
		log.Printf("Inode %d = %q (%s)", n.Inode().nodeId, path, n.fs.String())
249
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
250

251
	return path
252 253
}

254 255 256
func (n *pathInode) addChild(name string, child *pathInode) {
	n.Inode().AddChild(name, child.Inode())
	child.Parent = n
257
	child.Name = name
258

259 260 261
	if child.clientInode > 0 && n.pathFs.options.ClientInodes {
		defer n.LockTree()()
		m := n.pathFs.clientInodeMap[child.clientInode]
262
		e := &clientInodePath{
263
			n, name, child,
264 265
		}
		m = append(m, e)
266
		n.pathFs.clientInodeMap[child.clientInode] = m
267
	}
268 269
}

270 271
func (n *pathInode) rmChild(name string) *pathInode {
	childInode := n.Inode().RmChild(name)
272 273 274 275
	if childInode == nil {
		return nil
	}
	ch := childInode.FsNode().(*pathInode)
276

277 278 279
	if ch.clientInode > 0 && n.pathFs.options.ClientInodes {
		defer n.LockTree()()
		m := n.pathFs.clientInodeMap[ch.clientInode]
280 281 282

		idx := -1
		for i, v := range m {
283
			if v.parent == n && v.name == name {
284 285 286 287 288 289 290 291 292 293 294
				idx = i
				break
			}
		}
		if idx >= 0 {
			m[idx] = m[len(m)-1]
			m = m[:len(m)-1]
		}
		if len(m) > 0 {
			ch.Parent = m[0].parent
			ch.Name = m[0].name
295
			return ch
296
		} else {
297
			delete(n.pathFs.clientInodeMap, ch.clientInode)
298
		}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
299 300
	}

301 302
	ch.Name = ".deleted"
	ch.Parent = nil
303 304

	return ch
305
}
306

307 308
// Handle a change in clientInode number for an other wise unchanged
// pathInode.
309 310
func (n *pathInode) setClientInode(ino uint64) {
	if ino == n.clientInode || !n.pathFs.options.ClientInodes {
311 312
		return
	}
313 314 315
	defer n.LockTree()()
	if n.clientInode != 0 {
		delete(n.pathFs.clientInodeMap, n.clientInode)
316
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
317

318 319
	n.clientInode = ino
	if n.Parent != nil {
320
		e := &clientInodePath{
321
			n.Parent, n.Name, n,
322
		}
323
		n.pathFs.clientInodeMap[ino] = append(n.pathFs.clientInodeMap[ino], e)
324 325 326
	}
}

327 328
func (n *pathInode) OnForget() {
	if n.clientInode == 0 || !n.pathFs.options.ClientInodes {
329 330
		return
	}
331 332
	defer n.LockTree()()
	delete(n.pathFs.clientInodeMap, n.clientInode)
333 334
}

335
////////////////////////////////////////////////////////////////
336 337
// FS operations

338 339
func (n *pathInode) StatFs() *StatfsOut {
	return n.fs.StatFs(n.GetPath())
340
}
341

342 343
func (n *pathInode) Readlink(c *Context) ([]byte, Status) {
	path := n.GetPath()
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
344

345
	val, err := n.fs.Readlink(path, c)
346 347 348
	return []byte(val), err
}

349 350 351
func (n *pathInode) Access(mode uint32, context *Context) (code Status) {
	p := n.GetPath()
	return n.fs.Access(p, mode, context)
352 353
}

354 355
func (n *pathInode) GetXAttr(attribute string, context *Context) (data []byte, code Status) {
	return n.fs.GetXAttr(n.GetPath(), attribute, context)
356 357
}

358 359 360
func (n *pathInode) RemoveXAttr(attr string, context *Context) Status {
	p := n.GetPath()
	return n.fs.RemoveXAttr(p, attr, context)
361 362
}

363 364
func (n *pathInode) SetXAttr(attr string, data []byte, flags int, context *Context) Status {
	return n.fs.SetXAttr(n.GetPath(), attr, data, flags, context)
365 366
}

367 368
func (n *pathInode) ListXAttr(context *Context) (attrs []string, code Status) {
	return n.fs.ListXAttr(n.GetPath(), context)
369 370
}

371
func (n *pathInode) Flush(file File, openFlags uint32, context *Context) (code Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
372
	return file.Flush()
373 374
}

375
func (n *pathInode) OpenDir(context *Context) ([]DirEntry, Status) {
376
	return n.fs.OpenDir(n.GetPath(), context)
377 378
}

379 380 381
func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *Context) (fi *Attr, newNode FsNode, code Status) {
	fullPath := filepath.Join(n.GetPath(), name)
	code = n.fs.Mknod(fullPath, mode, dev, context)
382
	if code.Ok() {
383
		pNode := n.createChild(false)
384
		newNode = pNode
385 386
		fi, code = n.fillNewChildAttr(fullPath, pNode, context)
		n.addChild(name, pNode)
387 388
	}
	return
389
}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
390

391 392 393
func (n *pathInode) Mkdir(name string, mode uint32, context *Context) (fi *Attr, newNode FsNode, code Status) {
	fullPath := filepath.Join(n.GetPath(), name)
	code = n.fs.Mkdir(fullPath, mode, context)
394
	if code.Ok() {
395
		pNode := n.createChild(true)
396
		newNode = pNode
397 398
		fi, code = n.fillNewChildAttr(fullPath, pNode, context)
		n.addChild(name, pNode)
399 400
	}
	return
401 402
}

403 404
func (n *pathInode) Unlink(name string, context *Context) (code Status) {
	code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context)
405
	if code.Ok() {
406
		n.rmChild(name)
407 408
	}
	return code
409 410
}

411 412
func (n *pathInode) Rmdir(name string, context *Context) (code Status) {
	code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context)
413
	if code.Ok() {
414
		n.rmChild(name)
415 416
	}
	return code
417 418
}

419 420 421
func (n *pathInode) Symlink(name string, content string, context *Context) (fi *Attr, newNode FsNode, code Status) {
	fullPath := filepath.Join(n.GetPath(), name)
	code = n.fs.Symlink(content, fullPath, context)
422
	if code.Ok() {
423
		pNode := n.createChild(false)
424
		newNode = pNode
425 426
		fi, code = n.fillNewChildAttr(fullPath, pNode, context)
		n.addChild(name, pNode)
427 428
	}
	return
429 430
}

431
func (n *pathInode) Rename(oldName string, newParent FsNode, newName string, context *Context) (code Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
432
	p := newParent.(*pathInode)
433
	oldPath := filepath.Join(n.GetPath(), oldName)
434
	newPath := filepath.Join(p.GetPath(), newName)
435
	code = n.fs.Rename(oldPath, newPath, context)
436
	if code.Ok() {
437
		ch := n.rmChild(oldName)
438 439 440 441
		p.rmChild(newName)
		p.addChild(newName, ch)
	}
	return code
442 443
}

444 445
func (n *pathInode) Link(name string, existingFsnode FsNode, context *Context) (fi *Attr, newNode FsNode, code Status) {
	if !n.pathFs.options.ClientInodes {
446 447 448
		return nil, nil, ENOSYS
	}

449
	newPath := filepath.Join(n.GetPath(), name)
450 451
	existing := existingFsnode.(*pathInode)
	oldPath := existing.GetPath()
452
	code = n.fs.Link(oldPath, newPath, context)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
453
	if code.Ok() {
454
		fi, code = n.fs.GetAttr(newPath, context)
455 456 457 458
	}

	if code.Ok() {
		if existing.clientInode != 0 && existing.clientInode == fi.Ino {
459
			newNode = existing
460
			n.addChild(name, existing)
461
		} else {
462
			pNode := n.createChild(false)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
463
			newNode = pNode
464
			pNode.clientInode = fi.Ino
465
			n.addChild(name, pNode)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
466 467
		}
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
468
	return
469 470
}

471 472 473
func (n *pathInode) Create(name string, flags uint32, mode uint32, context *Context) (file File, fi *Attr, newNode FsNode, code Status) {
	fullPath := filepath.Join(n.GetPath(), name)
	file, code = n.fs.Create(fullPath, flags, mode, context)
474
	if code.Ok() {
475
		pNode := n.createChild(false)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
476
		newNode = pNode
477 478
		fi, code = n.fillNewChildAttr(fullPath, pNode, context)
		n.addChild(name, pNode)
479
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
480
	return
481 482
}

483
func (n *pathInode) createChild(isDir bool) *pathInode {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
484
	i := new(pathInode)
485 486
	i.fs = n.fs
	i.pathFs = n.pathFs
487

488
	n.Inode().New(isDir, i)
489
	return i
490 491
}

492 493 494
func (n *pathInode) Open(flags uint32, context *Context) (file File, code Status) {
	file, code = n.fs.Open(n.GetPath(), flags, context)
	if n.pathFs.Debug {
495
		file = &WithFlags{
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
496
			File:        file,
497
			Description: n.GetPath(),
498 499
		}
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
500
	return
501 502
}

503 504 505
func (n *pathInode) Lookup(name string, context *Context) (fi *Attr, node FsNode, code Status) {
	fullPath := filepath.Join(n.GetPath(), name)
	fi, code = n.fs.GetAttr(fullPath, context)
506
	if code.Ok() {
507
		node = n.findChild(fi, name, fullPath)
508 509 510
	}

	return
511 512
}

513
func (n *pathInode) findChild(fi *Attr, name string, fullPath string) (out *pathInode) {
514
	if fi.Ino > 0 {
515 516
		unlock := n.RLockTree()
		v := n.pathFs.clientInodeMap[fi.Ino]
517 518
		if len(v) > 0 {
			out = v[0].node
519 520 521 522

			if fi.Nlink == 1 {
				log.Println("Found linked inode, but Nlink == 1", fullPath)
			}
523
		}
524
		unlock()
525 526 527
	}

	if out == nil {
528
		out = n.createChild(fi.IsDir())
529
		out.clientInode = fi.Ino
530
		n.addChild(name, out)
531 532 533 534 535
	}

	return out
}

536
func (n *pathInode) GetAttr(file File, context *Context) (fi *Attr, code Status) {
537
	if file == nil {
538
		// called on a deleted files.
539
		file = n.inode.AnyFile()
540
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
541

542 543 544 545
	if file != nil {
		fi, code = file.GetAttr()
	}

546
	if file == nil || code == ENOSYS || code == EBADF {
547
		fi, code = n.fs.GetAttr(n.GetPath(), context)
548
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
549

550
	if fi != nil {
551
		n.setClientInode(fi.Ino)
552
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
553

554
	if fi != nil && !fi.IsDir() && fi.Nlink == 0 {
555 556 557
		fi.Nlink = 1
	}
	return fi, code
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
558
}
559

560 561
func (n *pathInode) Chmod(file File, perms uint32, context *Context) (code Status) {
	files := n.inode.Files(O_ANYWRITE)
562
	for _, f := range files {
563
		// TODO - pass context
564
		code = f.Chmod(perms)
565 566
		if code.Ok() {
			return
567 568
		}
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
569

570
	if len(files) == 0 || code == ENOSYS || code == EBADF {
571
		code = n.fs.Chmod(n.GetPath(), perms, context)
572 573 574 575
	}
	return code
}

576 577
func (n *pathInode) Chown(file File, uid uint32, gid uint32, context *Context) (code Status) {
	files := n.inode.Files(O_ANYWRITE)
578
	for _, f := range files {
579
		// TODO - pass context
580
		code = f.Chown(uid, gid)
581 582
		if code.Ok() {
			return code
583
		}
584
	}
585
	if len(files) == 0 || code == ENOSYS || code == EBADF {
586
		// TODO - can we get just FATTR_GID but not FATTR_UID ?
587
		code = n.fs.Chown(n.GetPath(), uid, gid, context)
588 589 590 591
	}
	return code
}

592 593
func (n *pathInode) Truncate(file File, size uint64, context *Context) (code Status) {
	files := n.inode.Files(O_ANYWRITE)
594 595 596
	for _, f := range files {
		// TODO - pass context
		code = f.Truncate(size)
597 598
		if code.Ok() {
			return code
599
		}
600
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
601
	if len(files) == 0 || code == ENOSYS || code == EBADF {
602
		code = n.fs.Truncate(n.GetPath(), size, context)
603 604 605 606
	}
	return code
}

607 608
func (n *pathInode) Utimens(file File, atime int64, mtime int64, context *Context) (code Status) {
	files := n.inode.Files(O_ANYWRITE)
609 610 611
	for _, f := range files {
		// TODO - pass context
		code = f.Utimens(atime, mtime)
612 613
		if code.Ok() {
			return code
614
		}
615
	}
616
	if len(files) == 0 || code == ENOSYS || code == EBADF {
617
		code = n.fs.Utimens(n.GetPath(), atime, mtime, context)
618 619 620
	}
	return code
}