File: notifier.go

package info (click to toggle)
golang-github-jacobsa-fuse 0.0~git20250829.cbc61fa-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 812 kB
  • sloc: makefile: 4
file content (132 lines) | stat: -rw-r--r-- 3,938 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
70
71
72
73
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
99
100
101
102
103
104
105
106
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
package fuse

import (
	"unsafe"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/fusekernel"
)

// Notifier coordinates low-level notifications from the fuse daemon to the
// kernel. A Notifier may be used by the ServeOps implementation of a Server. In
// order to deliver notifications, wrap the server with NewServerWithNotifier.
type Notifier struct {
	inodeInvalidations  chan invalidateInodeCommand
	dentryInvalidations chan invalidateEntryCommand
}

func NewNotifier() *Notifier {
	return &Notifier{
		inodeInvalidations:  make(chan invalidateInodeCommand),
		dentryInvalidations: make(chan invalidateEntryCommand),
	}
}

type invalidateInodeCommand struct {
	inode  fuseops.InodeID
	offset int64
	length int64
	done   chan<- error
}

type invalidateEntryCommand struct {
	parent fuseops.InodeID
	name   string
	// If fusekernel.NotifyInvalEntryOut is updated to use its padding as flags,
	// we can support the expire flag in this command as well.
	done chan<- error
}

// InvalidateInode notifies the kernel to invalidate an inode cache entry. See
// the libfuse documentation at
// https://libfuse.github.io/doxygen/fuse__lowlevel_8h.html#a9cb974af9745294ff446d11cba2422f1
// for more details.
//
// InvalidateInode blocks until the kernel write completes, and returns the
// error from the kernel, if any. ENOSYS indicates that the kernel does not
// support inode invalidations.
func (n *Notifier) InvalidateInode(inode fuseops.InodeID, offset, length int64) error {
	done := make(chan error)
	n.inodeInvalidations <- invalidateInodeCommand{inode, offset, length, done}
	return <-done
}

// InvalidateEntry notifies to the kernel to invalidate a dentry cache entry.
// See the libfuse documentation at
// https://libfuse.github.io/doxygen/fuse__lowlevel_8h.html#ab14032b74b0a57a2b3155dd6ba8d6095
// for more details.
//
// InvalidateEntry blocks until the kernel write completes, and returns the
// error from the kernel, if any. ENOSYS indicates that the kernel does not
// support dentry invalidations.
func (n *Notifier) InvalidateEntry(parent fuseops.InodeID, name string) error {
	done := make(chan error)
	n.dentryInvalidations <- invalidateEntryCommand{parent, name, done}
	return <-done
}

func serviceInodeInvalidation(c *Connection, inode fuseops.InodeID, offset, length int64) error {
	outMsg := c.getOutMessage()
	defer c.putOutMessage(outMsg)

	cmd := fusekernel.NotifyInvalInodeOut{
		Ino: uint64(inode),
		Off: offset,
		Len: length,
	}
	outMsg.Append(unsafe.Slice((*byte)(unsafe.Pointer(&cmd)), int(unsafe.Sizeof(cmd))))

	outMsg.OutHeader().Error = fusekernel.NotifyCodeInvalInode
	outMsg.OutHeader().Len = uint32(outMsg.Len())

	return c.writeOutMessage(outMsg)
}

func serviceEntryInval(c *Connection, parent fuseops.InodeID, name string) error {
	outMsg := c.getOutMessage()
	defer c.putOutMessage(outMsg)

	cmd := fusekernel.NotifyInvalEntryOut{
		Parent:  uint64(parent),
		Namelen: uint32(len(name)),
	}
	outMsg.Append(unsafe.Slice((*byte)(unsafe.Pointer(&cmd)), int(unsafe.Sizeof(cmd))))

	// The name must be represented as a C string with a null-terminator.
	outMsg.AppendString(name)
	outMsg.Append([]byte{0})

	outMsg.OutHeader().Error = fusekernel.NotifyCodeInvalEntry
	outMsg.OutHeader().Len = uint32(outMsg.Len())
	return c.writeOutMessage(outMsg)
}

func (n *Notifier) notify(c *Connection, terminate <-chan struct{}) {
	for {
		select {
		case i := <-n.inodeInvalidations:
			i.done <- serviceInodeInvalidation(c, i.inode, i.offset, i.length)
		case e := <-n.dentryInvalidations:
			e.done <- serviceEntryInval(c, e.parent, e.name)
		case <-terminate:
			return
		}
	}
}

type notifierServer struct {
	n *Notifier
	s Server
}

func (s *notifierServer) ServeOps(c *Connection) {
	terminate := make(chan struct{})

	go s.n.notify(c, terminate)
	s.s.ServeOps(c)
	close(terminate)
}

func NewServerWithNotifier(n *Notifier, s Server) Server {
	return &notifierServer{n, s}
}