Skip to content

Commit

Permalink
handleMap: track per-handle generation number
Browse files Browse the repository at this point in the history
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 #204
See also #205
  • Loading branch information
rfjakob authored and hanwen committed Mar 18, 2018
1 parent 73e231d commit a9ddcb8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 18 deletions.
41 changes: 23 additions & 18 deletions fuse/nodefs/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ type handleMap interface {
}

type handled struct {
handle uint64
count int
handle uint64
generation uint64
count int
}

func (h *handled) verify() {
Expand Down Expand Up @@ -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) {
Expand Down
33 changes: 33 additions & 0 deletions fuse/nodefs/handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit a9ddcb8

Please sign in to comment.