Files
rclone/vfs/aux_test.go
T
Nick Craig-Wood e006d7c13f vfs: fix crash when multiple mounts or servers share the same VFS
The VFS is shared between users with the same remote and options, for
example two mounts created over the rc, or a mount and an NFS server.

Each node has a single Sys() slot which mount, mount2 and serve nfs
all used to attach their per-node data. With a shared VFS the users
overwrote each other's data: at best churning the cached FUSE nodes,
and since the slot was an atomic.Value, panicking with "store of
inconsistently typed value" as soon as two users stored different
types on the same node.

This change gives each node auxiliary values keyed by owner, set with
SetAux and read with Aux, so each user of the VFS has an independent
slot. The mounts now cache their FUSE nodes under their own key,
leaving Sys - which is read through the os.FileInfo interface -
reserved for users like serve nfs which need to control what that
returns.

Nodes with nothing attached use less memory than before (one pointer
instead of an atomic.Value) and reads remain lock free.

Bug discovered while thinking about #9617
2026-07-27 14:55:20 +01:00

74 lines
1.4 KiB
Go

package vfs
import (
"fmt"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAux(t *testing.T) {
var a aux
owner1, owner2 := new(int), new(int)
// Nothing attached yet
assert.Nil(t, a.Aux(owner1))
assert.Nil(t, a.Sys())
// Values attached by different owners are independent even if
// they have different types
a.SetAux(owner1, "potato")
a.SetAux(owner2, 2)
assert.Equal(t, "potato", a.Aux(owner1))
assert.Equal(t, 2, a.Aux(owner2))
// Replace a value
a.SetAux(owner1, "sausage")
assert.Equal(t, "sausage", a.Aux(owner1))
// Remove a value
a.SetAux(owner1, nil)
assert.Nil(t, a.Aux(owner1))
assert.Equal(t, 2, a.Aux(owner2))
// Sys is independent of the other owners
assert.Nil(t, a.Sys())
a.SetSys(42)
assert.Equal(t, 42, a.Sys())
assert.Equal(t, 2, a.Aux(owner2))
// Changing the type of the value stored must not panic
a.SetSys("42")
assert.Equal(t, "42", a.Sys())
// Remove the remaining values
a.SetSys(nil)
a.SetAux(owner2, nil)
assert.Nil(t, a.entries.Load())
}
func TestAuxConcurrent(t *testing.T) {
const (
owners = 4
iterations = 100
)
var (
a aux
wg sync.WaitGroup
)
for i := range owners {
wg.Add(1)
go func() {
defer wg.Done()
owner := &i
for j := range iterations {
value := fmt.Sprintf("%d-%d", i, j)
a.SetAux(owner, value)
assert.Equal(t, value, a.Aux(owner))
}
}()
}
wg.Wait()
}