Skip to content

Commit 13ad385

Browse files
committed
Add notification messages framework
Now we only impl the NotifyInvalInode,NotifyInvalEntry, NotifyDelete. Notify funcs allow the filesystem to invalidate VFS caches. The statement that 'In a networked fuse file-system, the local fuse instance may receive changes from a remote system that do not pass through the local kernel vfs. When those changes are made the local fuse instance must notify the local kernel of the change so as to keep the local kernel's cache in sync. This is accomplished via notify delete or notify invalidate. In other words, these notifications only apply if fuse file-system makes changes to the file-system that are not initiated by the local kernel: changes initiated by the local kernel must not involve notify delete or notify invalidate (will deadlock), and are not required.' is quoted from Jhon Muir. About fuse notify func history ,please ref link http://lkml.iu.edu/hypermail/linux/kernel/0906.0/01120.html .
1 parent 659cc51 commit 13ad385

8 files changed

Lines changed: 321 additions & 2 deletions

File tree

connection.go

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,19 @@ func (c *Connection) beginOp(
240240
// should not record any state keyed on their ID.
241241
//
242242
// Cf. https://github.com/osxfuse/osxfuse/issues/208
243-
if opCode != fusekernel.OpForget {
243+
244+
// Special case: For notify ops ,it is issued by the userspace filesystem to
245+
// fuse kernel. The ops's kernel opCode is 0 in kernel' viewpoint or kernel not
246+
// care whether what the iopcode is .But the ops have the notifycode from 1
247+
// to 6 now. For adapt the notfiy ops to the BeginOP ,we fake an opcode as
248+
// notfiycode + 100. Now opcode range of ops from kernel including origin
249+
// from userspace and just from kernel is < 100. The notify ops's opcode
250+
// range is [101-106].
251+
// Then we can process all ops consistently.
252+
// Cf. func (c *Connection) SetNotifyContext(op interface{})
253+
// (context.Context, error) {
254+
255+
if opCode != fusekernel.OpForget && opCode < 100 {
244256
var cancel func()
245257
ctx, cancel = context.WithCancel(ctx)
246258
c.recordCancelFunc(fuseID, cancel)
@@ -410,6 +422,38 @@ func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) {
410422
}
411423
}
412424

425+
// The SetNotifyContext set context according with value of the notify op's
426+
// fuseops.Notify*Op.
427+
func (c *Connection) SetNotifyContext(op interface{}) (context.Context, error) {
428+
429+
outMsg := c.getOutMessage()
430+
431+
err := c.buildNotify(outMsg, op)
432+
if err != nil {
433+
return nil, err
434+
}
435+
436+
ctx := context.Background()
437+
438+
switch op.(type) {
439+
case *fuseops.NotifyInvalInodeOp:
440+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeInvalInode), 0)
441+
442+
case *fuseops.NotifyInvalEntryOp:
443+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeInvalEntry), 0)
444+
445+
case *fuseops.NotifyDeleteOp:
446+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeDelete), 0)
447+
448+
default:
449+
panic(fmt.Sprintf("Unexpected op: %#v", op))
450+
}
451+
452+
ctx = context.WithValue(ctx, contextKey, opState{nil, outMsg, op})
453+
return ctx, nil
454+
455+
}
456+
413457
// Skip errors that happen as a matter of course, since they spook users.
414458
func (c *Connection) shouldLogError(
415459
op interface{},
@@ -497,6 +541,29 @@ func (c *Connection) Reply(ctx context.Context, opErr error) {
497541
}
498542
}
499543

544+
// The NotifyKernel is same as Reply func of Connection.But the diff is
545+
// that the func only send to kernel.
546+
func (c *Connection) NotifyKernel(ctx context.Context) {
547+
548+
// we should get outmsg from context
549+
var key interface{} = contextKey
550+
foo := ctx.Value(key)
551+
state, ok := foo.(opState)
552+
if !ok {
553+
panic(fmt.Sprintf("Reply called with invalid context: %#v", ctx))
554+
}
555+
556+
outMsg := state.outMsg
557+
defer c.putOutMessage(outMsg)
558+
559+
c.debugLogger.Println("dev fd is:unique:notifycode ", c.dev.Fd(), outMsg.OutHeader().Unique, outMsg.OutHeader().Error)
560+
err := c.writeMessage(outMsg.Bytes())
561+
if err != nil && c.errorLogger != nil {
562+
c.errorLogger.Printf("writeMessage: %v %v", err, outMsg.Bytes())
563+
}
564+
565+
}
566+
500567
// Close the connection. Must not be called until operations that were read
501568
// from the connection have been responded to.
502569
func (c *Connection) close() (err error) {

conversions.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,59 @@ func (c *Connection) kernelResponse(
623623
return
624624
}
625625

626+
func (c *Connection) buildNotify(
627+
m *buffer.OutMessage,
628+
op interface{}) error {
629+
630+
h := m.OutHeader()
631+
h.Unique = 0
632+
// Create the appropriate output message
633+
switch o := op.(type) {
634+
case *fuseops.NotifyInvalInodeOp:
635+
h.Error = fusekernel.NotifyCodeInvalInode
636+
size := fusekernel.NotifyInvalInodeOutSize
637+
out := (*fusekernel.NotifyInvalInodeOut)(m.Grow(size))
638+
out.Ino = uint64(o.Ino)
639+
out.Off = int64(o.Off)
640+
out.Len = int64(o.Len)
641+
642+
case *fuseops.NotifyInvalEntryOp:
643+
err := checkName(o.Name)
644+
if err != nil {
645+
return err
646+
}
647+
h.Error = fusekernel.NotifyCodeInvalEntry
648+
size := fusekernel.NotifyInvalEntryOutSize
649+
out := (*fusekernel.NotifyInvalEntryOut)(m.Grow(size))
650+
out.Parent = uint64(o.Parent)
651+
out.Namelen = uint32(len(o.Name))
652+
m.Append([]byte(o.Name))
653+
b := []byte{'\x00'}
654+
m.Append(b)
655+
656+
case *fuseops.NotifyDeleteOp:
657+
err := checkName(o.Name)
658+
if err != nil {
659+
return err
660+
}
661+
h.Error = fusekernel.NotifyCodeDelete
662+
size := fusekernel.NotifyDeleteOutSize
663+
out := (*fusekernel.NotifyDeleteOut)(m.Grow(size))
664+
out.Parent = uint64(o.Parent)
665+
out.Child = uint64(o.Child)
666+
out.Namelen = uint32(len(o.Name))
667+
m.Append([]byte(o.Name))
668+
b := []byte{'\x00'}
669+
m.Append(b)
670+
671+
default:
672+
return errors.New("unexpectedop")
673+
}
674+
h.Len = uint32(m.Len())
675+
676+
return nil
677+
}
678+
626679
// Like kernelResponse, but assumes the user replied with a nil error to the
627680
// op.
628681
func (c *Connection) kernelResponseForOp(
@@ -922,3 +975,11 @@ func writeXattrSize(m *buffer.OutMessage, size uint32) {
922975
out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
923976
out.Size = size
924977
}
978+
func checkName(name string) error {
979+
const maxUint32 = ^uint32(0)
980+
if uint64(len(name)) > uint64(maxUint32) {
981+
// very unlikely, but we don't want to silently truncate
982+
return syscall.ENAMETOOLONG
983+
}
984+
return nil
985+
}

fuseops/ops.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,3 +865,62 @@ type SetXattrOp struct {
865865
// simply replace the value if the attribute exists.
866866
Flags uint32
867867
}
868+
869+
// Notify IO readiness event
870+
type NotifyPollOp struct {
871+
}
872+
873+
// Notify to invalidate cache for an inode.
874+
// Added in FUSE protocol version 7.12. If the kernel does not support this
875+
// (or a newer) version, the op will return -ENOSYS and do nothing
876+
type NotifyInvalInodeOp struct {
877+
// inode to invalidatej
878+
Ino InodeID
879+
880+
// the offset in the inode where to start invalidating or negative to invalidate attributes only
881+
Off int64
882+
883+
// the amount of cache to invalidate or 0 for all
884+
Len int64
885+
}
886+
887+
// Notify to invalidate parent attributes and the dentry matching parent/name
888+
// Added in FUSE protocol version 7.12. If the kernel does not support this
889+
// (or a newer) version, the op will return -ENOSYS and do nothing
890+
type NotifyInvalEntryOp struct {
891+
// the inode number
892+
Parent InodeID
893+
894+
// the child entry file name
895+
Name string
896+
}
897+
898+
// Store data to the kernel buffers
899+
// Cf:http://libfuse.github.io/doxygen/fuse__lowlevel_8h.html#a9cb974af9745294ff446d11cba2422f1
900+
type NotifyStoreOp struct {
901+
}
902+
903+
// Retrieve data from the kernel buffers
904+
type NotifyRetrieveOp struct {
905+
}
906+
907+
// This op behaves like NotifyInvalEntryOp with the following additional
908+
// effect (at least as of Linux kernel 4.8):
909+
910+
// If the provided child inode matches the inode that is currently
911+
// associated with the cached dentry, and if there are any inotify
912+
// watches registered for the dentry, then the watchers are informed
913+
// that the dentry has been deleted.
914+
// Added in FUSE protocol version 7.18. If the kernel does not
915+
// support this (or a newer) version, op will return -ENOSYS and do nothing.
916+
917+
type NotifyDeleteOp struct {
918+
// the inode number
919+
Parent InodeID
920+
921+
// the child entry's inode
922+
Child InodeID
923+
924+
// the child entry file name
925+
Name string
926+
}

fuseutil/file_system.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ func NewFileSystemServer(fs FileSystem) fuse.Server {
9090
type fileSystemServer struct {
9191
fs FileSystem
9292
opsInFlight sync.WaitGroup
93+
//use set/get to use mfs not Mfs
94+
Mfs *fuse.MountedFileSystem
9395
}
9496

9597
func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
@@ -123,6 +125,59 @@ func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
123125
}
124126
}
125127

128+
func (s *fileSystemServer) InvalidateEntry(parent fuseops.InodeID, name string) error {
129+
c := s.GetMfs().Conn.(*fuse.Connection)
130+
131+
op := &fuseops.NotifyInvalEntryOp{
132+
Parent: fuseops.InodeID(parent),
133+
Name: string(name),
134+
}
135+
ctx, _ := c.SetNotifyContext(op)
136+
s.opsInFlight.Add(1)
137+
go func(ctx context.Context) {
138+
defer s.opsInFlight.Done()
139+
c.NotifyKernel(ctx)
140+
}(ctx)
141+
return nil
142+
}
143+
func (s *fileSystemServer) NotifyDelete(
144+
parent fuseops.InodeID,
145+
child fuseops.InodeID,
146+
name string) error {
147+
c := s.GetMfs().Conn.(*fuse.Connection)
148+
op := &fuseops.NotifyDeleteOp{
149+
Parent: fuseops.InodeID(parent),
150+
Child: fuseops.InodeID(child),
151+
Name: string(name),
152+
}
153+
ctx, _ := c.SetNotifyContext(op)
154+
s.opsInFlight.Add(1)
155+
go func(ctx context.Context) {
156+
defer s.opsInFlight.Done()
157+
c.NotifyKernel(ctx)
158+
}(ctx)
159+
return nil
160+
161+
}
162+
func (s *fileSystemServer) InvalidateInode(
163+
ino fuseops.InodeID,
164+
off int64,
165+
len int64) error {
166+
c := s.GetMfs().Conn.(*fuse.Connection)
167+
op := &fuseops.NotifyInvalInodeOp{
168+
Ino: fuseops.InodeID(ino),
169+
Off: off,
170+
Len: len,
171+
}
172+
ctx, _ := c.SetNotifyContext(op)
173+
s.opsInFlight.Add(1)
174+
go func(ctx context.Context) {
175+
defer s.opsInFlight.Done()
176+
c.NotifyKernel(ctx)
177+
}(ctx)
178+
return nil
179+
180+
}
126181
func (s *fileSystemServer) handleOp(
127182
c *fuse.Connection,
128183
ctx context.Context,
@@ -219,3 +274,12 @@ func (s *fileSystemServer) handleOp(
219274

220275
c.Reply(ctx, err)
221276
}
277+
278+
func (s *fileSystemServer) GetMfs() *fuse.MountedFileSystem {
279+
return s.Mfs
280+
281+
}
282+
func (s *fileSystemServer) SetMfs(mfs *fuse.MountedFileSystem) {
283+
s.Mfs = mfs
284+
285+
}

internal/fusekernel/fuse_kernel.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,9 @@ const (
756756
NotifyCodePoll int32 = 1
757757
NotifyCodeInvalInode int32 = 2
758758
NotifyCodeInvalEntry int32 = 3
759+
NotifyCodeStore int32 = 4
760+
NotifyCodeRetrieve int32 = 5
761+
NotifyCodeDelete int32 = 6
759762
)
760763

761764
type NotifyInvalInodeOut struct {
@@ -764,8 +767,46 @@ type NotifyInvalInodeOut struct {
764767
Len int64
765768
}
766769

770+
const NotifyInvalInodeOutSize = int(unsafe.Sizeof(NotifyInvalInodeOut{}))
771+
767772
type NotifyInvalEntryOut struct {
768773
Parent uint64
769774
Namelen uint32
770775
padding uint32
771776
}
777+
778+
const NotifyInvalEntryOutSize = int(unsafe.Sizeof(NotifyInvalEntryOut{}))
779+
780+
type NotifyDeleteOut struct {
781+
Parent uint64
782+
Child uint64
783+
Namelen uint32
784+
padding uint32
785+
}
786+
787+
const NotifyDeleteOutSize = int(unsafe.Sizeof(NotifyDeleteOut{}))
788+
789+
type NotifyStoreOut struct {
790+
Nodeid uint64
791+
Offset uint64
792+
Size uint32
793+
padding uint32
794+
}
795+
796+
type NotifyRetrieveOut struct {
797+
NotifyUnique uint64
798+
Nodeid uint64
799+
Offset uint64
800+
Size uint32
801+
padding uint32
802+
}
803+
804+
/* Matches the size of fuse_write_in */
805+
type NotifyRetrieveIn struct {
806+
Dummy1 uint64
807+
Offset uint64
808+
Size uint32
809+
Dummy2 uint32
810+
Dummy3 uint64
811+
Dummy4 uint64
812+
}

mount.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"os"
21+
"github.com/jacobsa/fuse/fuseops"
2122
)
2223

2324
// Server is an interface for any type that knows how to serve ops read from a
@@ -27,6 +28,11 @@ type Server interface {
2728
// until all operations have been responded to. Must not be called more than
2829
// once.
2930
ServeOps(*Connection)
31+
InvalidateEntry(fuseops.InodeID, string) error
32+
InvalidateInode(fuseops.InodeID, int64, int64) error
33+
NotifyDelete(fuseops.InodeID, fuseops.InodeID, string) error
34+
SetMfs(*MountedFileSystem)
35+
GetMfs() *MountedFileSystem
3036
}
3137

3238
// Mount attempts to mount a file system on the given directory, using the
@@ -84,7 +90,10 @@ func Mount(
8490
return
8591
}
8692

87-
// Serve the connection in the background. When done, set the join status.
93+
mfs.Conn = connection
94+
server.SetMfs(mfs)
95+
96+
// Serve the connection in the background. When done, set the join status
8897
go func() {
8998
server.ServeOps(connection)
9099
mfs.joinStatus = connection.close()

0 commit comments

Comments
 (0)