Skip to content

Commit a9ddcb8

Browse files
rfjakobhanwen
authored andcommitted
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 #204 See also #205
1 parent 73e231d commit a9ddcb8

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

fuse/nodefs/handle.go

+23-18
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ type handleMap interface {
3535
}
3636

3737
type handled struct {
38-
handle uint64
39-
count int
38+
handle uint64
39+
generation uint64
40+
count int
4041
}
4142

4243
func (h *handled) verify() {
@@ -75,25 +76,29 @@ func newPortableHandleMap() *portableHandleMap {
7576

7677
func (m *portableHandleMap) Register(obj *handled) (handle, generation uint64) {
7778
m.Lock()
78-
if obj.count == 0 {
79-
if len(m.freeIds) == 0 {
80-
handle = uint64(len(m.handles))
81-
m.handles = append(m.handles, obj)
82-
} else {
83-
handle = m.freeIds[len(m.freeIds)-1]
84-
m.freeIds = m.freeIds[:len(m.freeIds)-1]
85-
m.generation++
86-
m.handles[handle] = obj
87-
}
88-
m.used++
89-
obj.handle = handle
79+
defer m.Unlock()
80+
// Reuse existing handle
81+
if obj.count != 0 {
82+
obj.count++
83+
return obj.handle, obj.generation
84+
}
85+
// Create a new handle number or recycle one on from the free list
86+
if len(m.freeIds) == 0 {
87+
obj.handle = uint64(len(m.handles))
88+
m.handles = append(m.handles, obj)
9089
} else {
91-
handle = obj.handle
90+
obj.handle = m.freeIds[len(m.freeIds)-1]
91+
m.freeIds = m.freeIds[:len(m.freeIds)-1]
92+
m.handles[obj.handle] = obj
9293
}
94+
// Increment generation number to guarantee the (handle, generation) tuple
95+
// is unique
96+
m.generation++
97+
m.used++
98+
obj.generation = m.generation
9399
obj.count++
94-
generation = m.generation
95-
m.Unlock()
96-
return
100+
101+
return obj.handle, obj.generation
97102
}
98103

99104
func (m *portableHandleMap) Handle(obj *handled) (h uint64) {

fuse/nodefs/handle_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,36 @@ func TestHandleMapGeneration(t *testing.T) {
126126
t.Fatalf("register should increase generation: got %d want greater than %d.", g2, g1)
127127
}
128128
}
129+
130+
func TestHandleMapGenerationKnown(t *testing.T) {
131+
hm := newPortableHandleMap()
132+
133+
o1 := &handled{}
134+
h1, g1 := hm.Register(o1)
135+
136+
o2 := &handled{}
137+
h2, _ := hm.Register(o2)
138+
139+
h3, g3 := hm.Register(o1)
140+
141+
if h1 != h3 {
142+
t.Fatalf("register known should reuse handle: got %d want %d.", h3, h1)
143+
}
144+
if g1 != g3 {
145+
t.Fatalf("register known should reuse generation: got %d want %d.", g3, g1)
146+
}
147+
148+
hm.Forget(h1, 2)
149+
hm.Forget(h2, 1)
150+
151+
h1, g1 = hm.Register(o1)
152+
h2, _ = hm.Register(o2)
153+
h3, g3 = hm.Register(o1)
154+
155+
if h1 != h3 {
156+
t.Fatalf("register known should reuse handle: got %d want %d.", h3, h1)
157+
}
158+
if g1 != g3 {
159+
t.Fatalf("register known should reuse generation: got %d want %d.", g3, g1)
160+
}
161+
}

0 commit comments

Comments
 (0)