Files
rclone/cmd/mountlib/rc_test.go
Nick Craig-Wood 6b67be9d48 mountlib: rc: fix mounts created with mountPoint "*" overwriting each other
On Windows, passing "*" as mountPoint to the mount/mount RC command
auto-assigns a drive letter (e.g. "Z:"), but the resolved letter was
never propagated back to mountlib. This caused liveMounts to be keyed
on the literal "*", breaking tracking of multiple mounts and making
unmount unreliable.

Change MountFn to return the actual mount point as an additional
return value. Update MountPoint.Mount() to store the resolved value,
and mountRc() to use it as the liveMounts key. The mount/mount RC
response now returns the actual mountPoint so callers can discover
which drive letter was assigned.
2026-04-27 15:09:14 +01:00

130 lines
3.4 KiB
Go

package mountlib_test
import (
"context"
"os"
"path/filepath"
"runtime"
"testing"
"time"
_ "github.com/rclone/rclone/backend/local"
_ "github.com/rclone/rclone/cmd/cmount"
_ "github.com/rclone/rclone/cmd/mount"
_ "github.com/rclone/rclone/cmd/mount2"
"github.com/rclone/rclone/cmd/mountlib"
"github.com/rclone/rclone/fs/config/configfile"
"github.com/rclone/rclone/fs/rc"
"github.com/rclone/rclone/fstest/testy"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRc(t *testing.T) {
// Disable tests under macOS and the CI since they are locking up
if runtime.GOOS == "darwin" {
testy.SkipUnreliable(t)
}
ctx := context.Background()
configfile.Install()
mount := rc.Calls.Get("mount/mount")
assert.NotNil(t, mount)
unmount := rc.Calls.Get("mount/unmount")
assert.NotNil(t, unmount)
getMountTypes := rc.Calls.Get("mount/types")
assert.NotNil(t, getMountTypes)
localDir := t.TempDir()
err := os.WriteFile(filepath.Join(localDir, "file.txt"), []byte("hello"), 0666)
require.NoError(t, err)
mountPoint := t.TempDir()
if runtime.GOOS == "windows" {
// Windows requires the mount point not to exist
require.NoError(t, os.RemoveAll(mountPoint))
}
out, err := getMountTypes.Fn(ctx, nil)
require.NoError(t, err)
var mountTypes []string
err = out.GetStruct("mountTypes", &mountTypes)
require.NoError(t, err)
t.Logf("Mount types %v", mountTypes)
t.Run("Errors", func(t *testing.T) {
_, err := mount.Fn(ctx, rc.Params{})
assert.Error(t, err)
_, err = mount.Fn(ctx, rc.Params{"fs": "/tmp"})
assert.Error(t, err)
_, err = mount.Fn(ctx, rc.Params{"mountPoint": "/tmp"})
assert.Error(t, err)
})
t.Run("Mount", func(t *testing.T) {
if len(mountTypes) == 0 {
t.Skip("Can't mount")
}
in := rc.Params{
"fs": localDir,
"mountPoint": mountPoint,
"vfsOpt": rc.Params{
"FilePerms": 0400,
},
}
// check file.txt is not there
filePath := filepath.Join(mountPoint, "file.txt")
_, err := os.Stat(filePath)
require.Error(t, err)
require.True(t, os.IsNotExist(err))
// mount
out, err := mount.Fn(ctx, in)
if err != nil {
t.Skipf("Mount failed - skipping test: %v", err)
}
// check the returned mount point matches what we asked for
returnedMountPoint, err := out.GetString("mountPoint")
require.NoError(t, err)
assert.Equal(t, mountPoint, returnedMountPoint)
// check file.txt is there now
fi, err := os.Stat(filePath)
require.NoError(t, err)
assert.Equal(t, int64(5), fi.Size())
if runtime.GOOS == "linux" {
assert.Equal(t, os.FileMode(0400), fi.Mode())
}
// check mount point list
checkMountList := func() []mountlib.MountInfo {
listCall := rc.Calls.Get("mount/listmounts")
require.NotNil(t, listCall)
listReply, err := listCall.Fn(ctx, rc.Params{})
require.NoError(t, err)
mountPointsReply, err := listReply.Get("mountPoints")
require.NoError(t, err)
mountPoints, ok := mountPointsReply.([]mountlib.MountInfo)
require.True(t, ok)
return mountPoints
}
mountPoints := checkMountList()
require.Equal(t, 1, len(mountPoints))
require.Equal(t, mountPoint, mountPoints[0].MountPoint)
// FIXME the OS sometimes appears to be using the mount
// immediately after it appears so wait a moment
time.Sleep(100 * time.Millisecond)
t.Run("Unmount", func(t *testing.T) {
_, err := unmount.Fn(ctx, in)
require.NoError(t, err)
assert.Equal(t, 0, len(checkMountList()))
})
})
}