Skip to content

Commit c796534

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 88e3bc5 commit c796534

8 files changed

Lines changed: 267 additions & 2 deletions

File tree

connection.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ func (c *Connection) beginOp(
241241
// should not record any state keyed on their ID.
242242
//
243243
// Cf. https://github.com/osxfuse/osxfuse/issues/208
244-
if opCode != fusekernel.OpForget {
244+
if opCode != fusekernel.OpForget && opCode < 100 {
245245
var cancel func()
246246
ctx, cancel = context.WithCancel(ctx)
247247
c.recordCancelFunc(fuseID, cancel)
@@ -411,6 +411,36 @@ func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) {
411411
}
412412
}
413413

414+
func (c *Connection) SetNotifyContext(op interface{}) (context.Context, error) {
415+
416+
outMsg := c.getOutMessage()
417+
418+
err := c.buildNotify(outMsg, op)
419+
if err != nil {
420+
return nil, err
421+
}
422+
423+
ctx := context.Background()
424+
425+
switch op.(type) {
426+
case *fuseops.NotifyInvalInodeOp:
427+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeInvalInode), 0)
428+
429+
case *fuseops.NotifyInvalEntryOp:
430+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeInvalEntry), 0)
431+
432+
case *fuseops.NotifyDeleteOp:
433+
ctx = c.beginOp(100+uint32(fusekernel.NotifyCodeDelete), 0)
434+
435+
default:
436+
panic(fmt.Sprintf("Unexpected op: %#v", op))
437+
}
438+
439+
ctx = context.WithValue(ctx, contextKey, opState{nil, outMsg, op})
440+
return ctx, nil
441+
442+
}
443+
414444
// Skip errors that happen as a matter of course, since they spook users.
415445
func (c *Connection) shouldLogError(
416446
op interface{},
@@ -498,6 +528,27 @@ func (c *Connection) Reply(ctx context.Context, opErr error) {
498528
}
499529
}
500530

531+
func (c *Connection) NotifyKernel(ctx context.Context) {
532+
533+
// we should get outmsg from context
534+
var key interface{} = contextKey
535+
foo := ctx.Value(key)
536+
state, ok := foo.(opState)
537+
if !ok {
538+
panic(fmt.Sprintf("Reply called with invalid context: %#v", ctx))
539+
}
540+
541+
outMsg := state.outMsg
542+
defer c.putOutMessage(outMsg)
543+
544+
c.debugLogger.Println("dev fd is:unique:notifycode ", c.dev.Fd(), outMsg.OutHeader().Unique, outMsg.OutHeader().Error)
545+
err := c.writeMessage(outMsg.Bytes())
546+
if err != nil && c.errorLogger != nil {
547+
c.errorLogger.Printf("writeMessage: %v %v", err, outMsg.Bytes())
548+
}
549+
550+
}
551+
501552
// Close the connection. Must not be called until operations that were read
502553
// from the connection have been responded to.
503554
func (c *Connection) close() (err error) {

conversions.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,59 @@ func (c *Connection) kernelResponse(
597597
return
598598
}
599599

600+
func (c *Connection) buildNotify(
601+
m *buffer.OutMessage,
602+
op interface{}) error {
603+
604+
h := m.OutHeader()
605+
h.Unique = 0
606+
// Create the appropriate output message
607+
switch o := op.(type) {
608+
case *fuseops.NotifyInvalInodeOp:
609+
h.Error = fusekernel.NotifyCodeInvalInode
610+
size := fusekernel.NotifyInvalInodeOutSize
611+
out := (*fusekernel.NotifyInvalInodeOut)(m.Grow(size))
612+
out.Ino = uint64(o.Ino)
613+
out.Off = int64(o.Off)
614+
out.Len = int64(o.Len)
615+
616+
case *fuseops.NotifyInvalEntryOp:
617+
err := checkName(o.Name)
618+
if err != nil {
619+
return err
620+
}
621+
h.Error = fusekernel.NotifyCodeInvalEntry
622+
size := fusekernel.NotifyInvalEntryOutSize
623+
out := (*fusekernel.NotifyInvalEntryOut)(m.Grow(size))
624+
out.Parent = uint64(o.Parent)
625+
out.Namelen = uint32(len(o.Name))
626+
m.Append([]byte(o.Name))
627+
b := []byte{'\x00'}
628+
m.Append(b)
629+
630+
case *fuseops.NotifyDeleteOp:
631+
err := checkName(o.Name)
632+
if err != nil {
633+
return err
634+
}
635+
h.Error = fusekernel.NotifyCodeDelete
636+
size := fusekernel.NotifyDeleteOutSize
637+
out := (*fusekernel.NotifyDeleteOut)(m.Grow(size))
638+
out.Parent = uint64(o.Parent)
639+
out.Child = uint64(o.Child)
640+
out.Namelen = uint32(len(o.Name))
641+
m.Append([]byte(o.Name))
642+
b := []byte{'\x00'}
643+
m.Append(b)
644+
645+
default:
646+
return errors.New("unexpectedop")
647+
}
648+
h.Len = uint32(m.Len())
649+
650+
return nil
651+
}
652+
600653
// Like kernelResponse, but assumes the user replied with a nil error to the
601654
// op.
602655
func (c *Connection) kernelResponseForOp(
@@ -888,3 +941,11 @@ func writeXattrSize(m *buffer.OutMessage, size uint32) {
888941
out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
889942
out.Size = size
890943
}
944+
func checkName(name string) error {
945+
const maxUint32 = ^uint32(0)
946+
if uint64(len(name)) > uint64(maxUint32) {
947+
// very unlikely, but we don't want to silently truncate
948+
return syscall.ENAMETOOLONG
949+
}
950+
return nil
951+
}

fuseops/ops.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,3 +845,24 @@ type SetXattrOp struct {
845845
// simply replace the value if the attribute exists.
846846
Flags uint32
847847
}
848+
type NotifyPollOp struct {
849+
}
850+
851+
type NotifyInvalInodeOp struct {
852+
Ino InodeID
853+
Off int64
854+
Len int64
855+
}
856+
type NotifyInvalEntryOp struct {
857+
Parent InodeID
858+
Name string
859+
}
860+
type NotifyStoreOp struct {
861+
}
862+
type NotifyRetrieveOp struct {
863+
}
864+
type NotifyDeleteOp struct {
865+
Parent InodeID
866+
Child InodeID
867+
Name string
868+
}

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,
@@ -216,3 +271,12 @@ func (s *fileSystemServer) handleOp(
216271

217272
c.Reply(ctx, err)
218273
}
274+
275+
func (s *fileSystemServer) GetMfs() *fuse.MountedFileSystem {
276+
return s.Mfs
277+
278+
}
279+
func (s *fileSystemServer) SetMfs(mfs *fuse.MountedFileSystem) {
280+
s.Mfs = mfs
281+
282+
}

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
"fmt"
1919
"os"
2020

21+
"github.com/jacobsa/fuse/fuseops"
2122
"golang.org/x/net/context"
2223
)
2324

@@ -28,6 +29,11 @@ type Server interface {
2829
// until all operations have been responded to. Must not be called more than
2930
// once.
3031
ServeOps(*Connection)
32+
InvalidateEntry(fuseops.InodeID, string) error
33+
InvalidateInode(fuseops.InodeID, int64, int64) error
34+
NotifyDelete(fuseops.InodeID, fuseops.InodeID, string) error
35+
SetMfs(*MountedFileSystem)
36+
GetMfs() *MountedFileSystem
3137
}
3238

3339
// Mount attempts to mount a file system on the given directory, using the
@@ -85,7 +91,10 @@ func Mount(
8591
return
8692
}
8793

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

mounted_file_system.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type MountedFileSystem struct {
2424
// The result to return from Join. Not valid until the channel is closed.
2525
joinStatus error
2626
joinStatusAvailable chan struct{}
27+
Conn interface{}
2728
}
2829

2930
// Dir returns the directory on which the file system is mounted (or where we

samples/forgetfs/forget_fs.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,23 @@ type ForgetFS struct {
9999
func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
100100
fs.server.ServeOps(c)
101101
}
102+
func (fs *ForgetFS) SetMfs(mfs *fuse.MountedFileSystem) {
103+
fs.server.SetMfs(mfs)
104+
}
105+
func (fs *ForgetFS) GetMfs() *fuse.MountedFileSystem {
106+
return fs.server.GetMfs()
107+
}
108+
func (fs *ForgetFS) InvalidateEntry(nodeid fuseops.InodeID, name string) error {
109+
return nil
110+
}
111+
112+
func (fs *ForgetFS) InvalidateInode(nodeid fuseops.InodeID, parent int64, nlookup int64) error {
113+
return nil
114+
}
115+
116+
func (fs *ForgetFS) NotifyDelete(nodeid fuseops.InodeID, parent fuseops.InodeID, name string) error {
117+
return nil
118+
}
102119

103120
// Panic if there are any inodes that have a non-zero reference count. For use
104121
// after unmounting.

0 commit comments

Comments
 (0)