Commit a9ddcb8a authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

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
parent 73e231d6
......@@ -36,6 +36,7 @@ type handleMap interface {
type handled struct {
handle uint64
generation uint64
count int
}
......@@ -75,25 +76,29 @@ func newPortableHandleMap() *portableHandleMap {
func (m *portableHandleMap) Register(obj *handled) (handle, generation uint64) {
m.Lock()
if obj.count == 0 {
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 {
handle = uint64(len(m.handles))
obj.handle = uint64(len(m.handles))
m.handles = append(m.handles, obj)
} else {
handle = m.freeIds[len(m.freeIds)-1]
obj.handle = m.freeIds[len(m.freeIds)-1]
m.freeIds = m.freeIds[:len(m.freeIds)-1]
m.generation++
m.handles[handle] = obj
m.handles[obj.handle] = obj
}
// Increment generation number to guarantee the (handle, generation) tuple
// is unique
m.generation++
m.used++
obj.handle = handle
} else {
handle = obj.handle
}
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) {
......
......@@ -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)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment