Skip to content

Commit 8e56f7e

Browse files
committed
ioutilx, hostagent: support reverse-sshfs on plain Windows
ioutilx.WindowsSubsystemPath: keep cygpath as the preferred backend (it respects any custom fstab the user has configured for MSYS2 / Git for Windows), but add a native fallback for the common drive-letter case (C:\Users\jan -> /c/Users/jan). Without the fallback, plain Windows installs that have neither Git for Windows nor MSYS2 hit a fatal error during fillDefault when computing the default mountPoint for a host mount. After this change, the default mountPoint is computed correctly without external tooling. hostagent.setupMount: switch the host-path translation from ioutilx.WindowsSubsystemPath to sshutil.PathForSSH. The path is consumed by the sftp-server that sshocker spawns, and the format that binary expects depends on toolchain: Cygwin sftp-server (Git for Windows / MSYS2) wants Cygwin paths, native Windows sftp-server wants native forward-slash paths. PathForSSH already encodes that decision. Verified end-to-end on Windows 11 with QEMU 10.2.0 and only native Windows OpenSSH on PATH (no Git for Windows, no MSYS2): reverse-sshfs mounts a host directory into the guest, both sides see the same files, read and write both work, and the host-side sftp-server is the ssh-built-in C:\Windows\System32\OpenSSH\sftp-server.exe (auto-detected by sshocker via exec.LookPath). Signed-off-by: Jan Dubois <jan.dubois@suse.com>
1 parent df59f8f commit 8e56f7e

2 files changed

Lines changed: 25 additions & 8 deletions

File tree

pkg/hostagent/mount.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/lima-vm/sshocker/pkg/reversesshfs"
1414
"github.com/sirupsen/logrus"
1515

16-
"github.com/lima-vm/lima/v2/pkg/ioutilx"
1716
"github.com/lima-vm/lima/v2/pkg/limatype"
1817
"github.com/lima-vm/lima/v2/pkg/sshutil"
1918
)
@@ -54,8 +53,16 @@ func (a *HostAgent) setupMount(ctx context.Context, m limatype.Mount) (*mount, e
5453

5554
resolvedLocation := m.Location
5655
if runtime.GOOS == "windows" {
57-
var err error
58-
resolvedLocation, err = ioutilx.WindowsSubsystemPath(ctx, m.Location)
56+
// Convert the host path to the form the sftp-server invoked by sshocker
57+
// expects. PathForSSH tracks the toolchain Lima is using: Cygwin-style
58+
// (e.g. /c/Users/jan) when ssh comes from Git for Windows / MSYS2 (where
59+
// sftp-server is also Cygwin-based), and native forward-slash form
60+
// (e.g. C:/Users/jan) when ssh is native Windows OpenSSH.
61+
sshExe, err := sshutil.NewSSHExe()
62+
if err != nil {
63+
return nil, err
64+
}
65+
resolvedLocation, err = sshutil.PathForSSH(ctx, sshExe, m.Location)
5966
if err != nil {
6067
return nil, err
6168
}

pkg/ioutilx/ioutilx.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,23 @@ func FromUTF16leToString(r io.Reader) (string, error) {
5050
return string(out), nil
5151
}
5252

53+
// WindowsSubsystemPath converts a Windows path to a Cygwin/MSYS-style path
54+
// (e.g. C:\Users\jan -> /c/Users/jan). It prefers cygpath, since that respects
55+
// any custom fstab the user has configured for MSYS2 / Git for Windows. When
56+
// cygpath is unavailable (plain Windows install with neither Git for Windows
57+
// nor MSYS2), it falls back to a native conversion that handles the common
58+
// drive-letter case. UNC paths and other inputs without a drive letter return
59+
// an error.
5360
func WindowsSubsystemPath(ctx context.Context, orig string) (string, error) {
54-
out, err := exec.CommandContext(ctx, "cygpath", filepath.ToSlash(orig)).CombinedOutput()
55-
if err != nil {
56-
logrus.WithError(err).Errorf("failed to convert path to mingw, maybe not using Git ssh?")
57-
return "", err
61+
if out, err := exec.CommandContext(ctx, "cygpath", filepath.ToSlash(orig)).CombinedOutput(); err == nil {
62+
return strings.TrimSpace(string(out)), nil
63+
} else {
64+
logrus.WithError(err).Debugf("cygpath unavailable for %q, attempting native conversion", orig)
5865
}
59-
return strings.TrimSpace(string(out)), nil
66+
if vol := filepath.VolumeName(orig); len(vol) == 2 && vol[1] == ':' {
67+
return "/" + strings.ToLower(vol[:1]) + filepath.ToSlash(orig[2:]), nil
68+
}
69+
return "", fmt.Errorf("cannot convert %q to a Cygwin-style path: cygpath unavailable and input is not a drive-letter path", orig)
6070
}
6171

6272
func WindowsSubsystemPathForLinux(ctx context.Context, orig, distro string) (string, error) {

0 commit comments

Comments
 (0)