vfs: add context parameter to New() for config propagation

Add a ctx parameter to vfs.New() so callers can pass in context
carrying ConfigInfo and FilterInfo. The context is stripped of
cancellation but config and filter values are preserved into a fresh
background context.
This commit is contained in:
Nick Craig-Wood
2026-03-25 18:37:43 +00:00
parent 9f2edc3077
commit 7b8994ab32
22 changed files with 37 additions and 28 deletions

View File

@@ -36,7 +36,7 @@ func New(ctx context.Context, wrappedFs fs.Fs, remote, prefix, root string) (*Fs
// FIXME vfs cache?
// FIXME could factor out ReadFileHandle and just use that rather than the full VFS
fs.Debugf(nil, "New: remote=%q, prefix=%q, root=%q", remote, prefix, root)
VFS := vfs.New(wrappedFs, nil)
VFS := vfs.New(ctx, wrappedFs, nil)
node, err := VFS.Stat(remote)
if err != nil {
return nil, fmt.Errorf("failed to find %q archive: %w", remote, err)

View File

@@ -50,7 +50,7 @@ func New(ctx context.Context, wrappedFs fs.Fs, remote, prefix, root string) (fs.
fs.Debugf(nil, "Squashfs: New: remote=%q, prefix=%q, root=%q", remote, prefix, root)
vfsOpt := vfscommon.Opt
vfsOpt.ReadWait = 0
VFS := vfs.New(wrappedFs, &vfsOpt)
VFS := vfs.New(ctx, wrappedFs, &vfsOpt)
node, err := VFS.Stat(remote)
if err != nil {
return nil, fmt.Errorf("failed to find %q archive: %w", remote, err)

View File

@@ -52,7 +52,7 @@ func New(ctx context.Context, wrappedFs fs.Fs, remote, prefix, root string) (fs.
fs.Debugf(nil, "Zip: New: remote=%q, prefix=%q, root=%q", remote, prefix, root)
vfsOpt := vfscommon.Opt
vfsOpt.ReadWait = 0
VFS := vfs.New(wrappedFs, &vfsOpt)
VFS := vfs.New(ctx, wrappedFs, &vfsOpt)
node, err := VFS.Stat(remote)
if err != nil {
return nil, fmt.Errorf("failed to find %q archive: %w", remote, err)

View File

@@ -369,7 +369,7 @@ func (m *MountPoint) Mount() (mountDaemon *os.Process, err error) {
}
}
m.VFS = vfs.New(m.Fs, &m.VFSOpt)
m.VFS = vfs.New(context.Background(), m.Fs, &m.VFSOpt)
m.ErrChan, m.UnmountFn, err = m.MountFn(m.VFS, m.MountPoint, &m.MountOpt)
if err != nil {

View File

@@ -40,7 +40,7 @@ func TestMount(t *testing.T) {
nfs.Opt.HandleCacheDir = t.TempDir()
require.NoError(t, nfs.Opt.HandleCache.Set(cacheType))
// Check we can create a handler
_, err := nfs.NewHandler(context.Background(), vfs.New(object.MemoryFs, nil), &nfs.Opt)
_, err := nfs.NewHandler(context.Background(), vfs.New(context.Background(), object.MemoryFs, nil), &nfs.Opt)
if errors.Is(err, nfs.ErrorSymlinkCacheNotSupported) || errors.Is(err, nfs.ErrorSymlinkCacheNoPermission) {
t.Skip(err.Error() + ": run with: go test -c && sudo setcap cap_dac_read_search+ep ./nfsmount.test && ./nfsmount.test -test.v")
}

View File

@@ -16,7 +16,7 @@ func TestMediaWithResources(t *testing.T) {
fs, err := localBackend.NewFs(context.Background(), "testdatafiles", "testdata/files", configmap.New())
require.NoError(t, err)
myvfs := vfs.New(fs, nil)
myvfs := vfs.New(context.Background(), fs, nil)
{
rootNode, err := myvfs.Stat("")
require.NoError(t, err)

View File

@@ -203,7 +203,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
waitChan: make(chan struct{}),
httpListenAddr: opt.ListenAddr,
f: f,
vfs: vfs.New(f, vfsOpt),
vfs: vfs.New(ctx, f, vfsOpt),
}
s.services = map[string]UPnPService{

View File

@@ -203,7 +203,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
d.proxy = proxy.New(ctx, proxyOpt, vfsOpt)
d.userPass = make(map[string]string, 16)
} else {
d.globalVFS = vfs.New(f, vfsOpt)
d.globalVFS = vfs.New(ctx, f, vfsOpt)
}
d.useTLS = d.opt.TLSKey != ""

View File

@@ -188,7 +188,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
// override auth
s.opt.Auth.CustomAuthFn = s.auth
} else {
s._vfs = vfs.New(f, vfsOpt)
s._vfs = vfs.New(ctx, f, vfsOpt)
}
s.server, err = libhttp.NewServer(ctx,

View File

@@ -122,7 +122,7 @@ func TestCache(t *testing.T) {
for _, cacheType := range []handleCache{cacheMemory, cacheDisk, cacheSymlink} {
t.Run(cacheType.String(), func(t *testing.T) {
h := &Handler{
vfs: vfs.New(object.MemoryFs, nil),
vfs: vfs.New(context.Background(), object.MemoryFs, nil),
billyFS: billyFS,
}
h.vfs.Opt.MetadataExtension = ".metadata"

View File

@@ -94,7 +94,7 @@ func init() {
if err != nil {
return nil, err
}
VFS := vfs.New(f, &vfsOpt)
VFS := vfs.New(ctx, f, &vfsOpt)
// Read opts
var opt = Opt // set default opts
err = configstruct.SetAny(in, &opt)
@@ -112,7 +112,7 @@ func Run(command *cobra.Command, args []string) {
cmd.CheckArgs(1, 1, command, args)
f = cmd.NewFsSrc(args)
cmd.Run(false, true, command, func() error {
s, err := NewServer(context.Background(), vfs.New(f, &vfscommon.Opt), &Opt)
s, err := NewServer(context.Background(), vfs.New(context.Background(), f, &vfscommon.Opt), &Opt)
if err != nil {
return err
}

View File

@@ -257,7 +257,7 @@ func (p *Proxy) call(user, auth string, isPublicKey bool) (value any, err error)
// need to in memory. An attacker would find it easier to go
// after the unencrypted password in memory most likely.
entry := cacheEntry{
vfs: vfs.New(f, &p.vfsOpt),
vfs: vfs.New(p.ctx, f, &p.vfsOpt),
pwHash: sha256.Sum256([]byte(auth)),
}
return entry, true, nil

View File

@@ -94,7 +94,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
w.handler = proxyAuthMiddleware(w.handler, w)
w.handler = authPairMiddleware(w.handler, w)
} else {
w._vfs = vfs.New(f, vfsOpt)
w._vfs = vfs.New(ctx, f, vfsOpt)
if len(opt.AuthKey) > 0 {
w.faker.AddAuthKeys(authList)

View File

@@ -358,7 +358,7 @@ func serveStdio(f fs.Fs) error {
stdin: os.Stdin,
stdout: os.Stdout,
}
handlers := newVFSHandler(vfs.New(f, &vfscommon.Opt))
handlers := newVFSHandler(vfs.New(context.Background(), f, &vfscommon.Opt))
return serveChannel(sshChannel, handlers, "stdio")
}

View File

@@ -55,7 +55,7 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
if proxy.Opt.AuthProxy != "" {
s.proxy = proxy.New(ctx, proxyOpt, vfsOpt)
} else {
s.vfs = vfs.New(f, vfsOpt)
s.vfs = vfs.New(ctx, f, vfsOpt)
}
err := s.configure()
if err != nil {

View File

@@ -263,7 +263,7 @@ func newWebDAV(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt
// override auth
w.opt.Auth.CustomAuthFn = w.auth
} else {
w._vfs = vfs.New(f, vfsOpt)
w._vfs = vfs.New(ctx, f, vfsOpt)
}
w.server, err = libhttp.NewServer(ctx,

View File

@@ -202,7 +202,7 @@ func TestFileOpenReadUnknownSize(t *testing.T) {
assert.Equal(t, int64(-1), testObj.Size())
// create a VFS from that mockfs
vfs := New(f, nil)
vfs := New(context.Background(), f, nil)
defer cleanupVFS(t, vfs)
// find the file

View File

@@ -48,7 +48,7 @@ func TestRcGetVFS(t *testing.T) {
opt := vfscommon.Opt
opt.NoModTime = true
vfs3 := New(r.Fremote, &opt)
vfs3 := New(context.Background(), r.Fremote, &opt)
defer vfs3.Shutdown()
vfs, err = getVFS(in)

View File

@@ -38,6 +38,7 @@ import (
"github.com/go-git/go-billy/v5"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/cache"
"github.com/rclone/rclone/fs/filter"
"github.com/rclone/rclone/fs/log"
"github.com/rclone/rclone/fs/rc"
"github.com/rclone/rclone/fs/walk"
@@ -196,10 +197,18 @@ var (
)
// New creates a new VFS and root directory. If opt is nil, then
// DefaultOpt will be used
func New(f fs.Fs, opt *vfscommon.Options) *VFS {
// DefaultOpt will be used.
//
// The ctx passed in is not used for cancellation but is used to find
// the config in the context (if any) and filter config in the context
// (if any).
func New(ctx context.Context, f fs.Fs, opt *vfscommon.Options) *VFS {
fsDir := fs.NewDir("", time.Now())
ctx, cancel := context.WithCancel(context.Background())
// Strip the ctx of any cancellation but copy the config across
newCtx := context.Background()
newCtx = fs.CopyConfig(newCtx, ctx)
newCtx = filter.CopyConfig(newCtx, ctx)
ctx, cancel := context.WithCancel(newCtx)
vfs := &VFS{
f: f,
ctx: ctx,

View File

@@ -36,12 +36,12 @@ func TestCaseSensitivity(t *testing.T) {
// Create a case-Sensitive and case-INsensitive VFS
optCS := vfscommon.Opt
optCS.CaseInsensitive = false
vfsCS := New(r.Fremote, &optCS)
vfsCS := New(context.Background(), r.Fremote, &optCS)
defer cleanupVFS(t, vfsCS)
optCI := vfscommon.Opt
optCI.CaseInsensitive = true
vfsCI := New(r.Fremote, &optCI)
vfsCI := New(context.Background(), r.Fremote, &optCI)
defer cleanupVFS(t, vfsCI)
// Run basic checks that must pass on VFS of any type.
@@ -180,7 +180,7 @@ func TestUnicodeNormalization(t *testing.T) {
// Create VFS
opt := vfscommon.Opt
vfs := New(r.Fremote, &opt)
vfs := New(context.Background(), r.Fremote, &opt)
defer cleanupVFS(t, vfs)
// assert that both files are found under NFD-normalized names

View File

@@ -47,7 +47,7 @@ func cleanupVFS(t *testing.T, vfs *VFS) {
// Create a new VFS
func newTestVFSOpt(t *testing.T, opt *vfscommon.Options) (r *fstest.Run, vfs *VFS) {
r = fstest.NewRun(t)
vfs = New(r.Fremote, opt)
vfs = New(context.Background(), r.Fremote, opt)
t.Cleanup(func() {
cleanupVFS(t, vfs)
})
@@ -144,7 +144,7 @@ func TestVFSNew(t *testing.T) {
// Check that we get the same VFS if we ask for it again with
// the same options
vfs2 := New(r.Fremote, nil)
vfs2 := New(context.Background(), r.Fremote, nil)
assert.Equal(t, fmt.Sprintf("%p", vfs), fmt.Sprintf("%p", vfs2))
checkActiveCacheEntries(1)

View File

@@ -41,7 +41,7 @@ func (r *Run) startMountSubProcess() {
// If testing the VFS we don't start a subprocess, we just use
// the VFS directly
if r.useVFS {
vfs := vfs.New(r.fremote, r.vfsOpt)
vfs := vfs.New(context.Background(), r.fremote, r.vfsOpt)
r.os = vfsOs{vfs}
return
}