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 ¬ifierServer{n, s}
}
|