From a9ddcb8a4b609500fc59c89ccc9ee05f00a5fefd Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 16 Mar 2018 18:07:08 +0100 Subject: [PATCH] handleMap: track per-handle generation number We used to hand out a new generation number even for already-known handles. This does not seem to cause problems on Linux, but osxfuse throws errors to userspace with osxfuse: vnode changed generation showing up in the kernel log. Introduce a per-handle generation number that stays constant until the handle is forgotten. Tested on Linux and MacOS via gocryptfs. Add test for handle map behavior. Fixes https://github.com/hanwen/go-fuse/issues/204 See also https://github.com/hanwen/go-fuse/pull/205 --- fuse/nodefs/handle.go | 41 +++++++++++++++++++++----------------- fuse/nodefs/handle_test.go | 33 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/fuse/nodefs/handle.go b/fuse/nodefs/handle.go index 0e6d36792..8bad21db7 100644 --- a/fuse/nodefs/handle.go +++ b/fuse/nodefs/handle.go @@ -35,8 +35,9 @@ type handleMap interface { } type handled struct { - handle uint64 - count int + handle uint64 + generation uint64 + count int } func (h *handled) verify() { @@ -75,25 +76,29 @@ func newPortableHandleMap() *portableHandleMap { func (m *portableHandleMap) Register(obj *handled) (handle, generation uint64) { m.Lock() - if obj.count == 0 { - if len(m.freeIds) == 0 { - handle = uint64(len(m.handles)) - m.handles = append(m.handles, obj) - } else { - handle = m.freeIds[len(m.freeIds)-1] - m.freeIds = m.freeIds[:len(m.freeIds)-1] - m.generation++ - m.handles[handle] = obj - } - m.used++ - obj.handle = handle + defer m.Unlock() + // Reuse existing handle + if obj.count != 0 { + obj.count++ + return obj.handle, obj.generation + } + // Create a new handle number or recycle one on from the free list + if len(m.freeIds) == 0 { + obj.handle = uint64(len(m.handles)) + m.handles = append(m.handles, obj) } else { - handle = obj.handle + obj.handle = m.freeIds[len(m.freeIds)-1] + m.freeIds = m.freeIds[:len(m.freeIds)-1] + m.handles[obj.handle] = obj } + // Increment generation number to guarantee the (handle, generation) tuple + // is unique + m.generation++ + m.used++ + obj.generation = m.generation obj.count++ - generation = m.generation - m.Unlock() - return + + return obj.handle, obj.generation } func (m *portableHandleMap) Handle(obj *handled) (h uint64) { diff --git a/fuse/nodefs/handle_test.go b/fuse/nodefs/handle_test.go index 2d94c7688..46b787212 100644 --- a/fuse/nodefs/handle_test.go +++ b/fuse/nodefs/handle_test.go @@ -126,3 +126,36 @@ func TestHandleMapGeneration(t *testing.T) { t.Fatalf("register should increase generation: got %d want greater than %d.", g2, g1) } } + +func TestHandleMapGenerationKnown(t *testing.T) { + hm := newPortableHandleMap() + + o1 := &handled{} + h1, g1 := hm.Register(o1) + + o2 := &handled{} + h2, _ := hm.Register(o2) + + h3, g3 := hm.Register(o1) + + if h1 != h3 { + t.Fatalf("register known should reuse handle: got %d want %d.", h3, h1) + } + if g1 != g3 { + t.Fatalf("register known should reuse generation: got %d want %d.", g3, g1) + } + + hm.Forget(h1, 2) + hm.Forget(h2, 1) + + h1, g1 = hm.Register(o1) + h2, _ = hm.Register(o2) + h3, g3 = hm.Register(o1) + + if h1 != h3 { + t.Fatalf("register known should reuse handle: got %d want %d.", h3, h1) + } + if g1 != g3 { + t.Fatalf("register known should reuse generation: got %d want %d.", g3, g1) + } +}