Skip to content

Commit 8e6ecb9

Browse files
committed
Fix running machines with volumes containing spaces
Machines configured to mount local paths containing spaces failed to start on Hyper-V and silently failed to mount the folder on macOS/Linux. On Windows/hyperv, where local paths are mounted running a 9p client inside the VM, the local host path needs to be surrounding with quotation marks before using in a `podman machine ssh ...` command. A similar behavior happened on Linux/QEMU where the path was used in a SSH command to mount the folder using virtiofs. Quoting the path when buidling the command arguments fixed the problem. On macOS/libkit,applehv the path was written as is in a systemd unit name to instruct how to mount it. Escaping space chars so that they are are parsed successfully fixed this: ```diff -- enable path with spaces.mount ++ enable path\x20with\x20spaces.mount ``` Fixes #25500 Signed-off-by: Mario Loriedo <[email protected]>
1 parent 2588b96 commit 8e6ecb9

File tree

4 files changed

+19
-9
lines changed

4 files changed

+19
-9
lines changed

pkg/machine/e2e/init_test.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,6 @@ var _ = Describe("podman machine init", func() {
283283
})
284284

285285
It("machine init with volume", func() {
286-
if testProvider.VMType() == define.HyperVVirt {
287-
Skip("volumes are not supported on hyperv yet")
288-
}
289286
skipIfWSL("WSL volumes are much different. This test will not pass as is")
290287

291288
tmpDir, err := os.MkdirTemp("", "")
@@ -296,9 +293,16 @@ var _ = Describe("podman machine init", func() {
296293
mount := tmpDir + ":/very-long-test-mount-dir-path-more-than-thirty-six-bytes"
297294
defer func() { _ = utils.GuardedRemoveAll(tmpDir) }()
298295

296+
tmpDirWithSpaces, err := os.MkdirTemp("", "with spaces")
297+
Expect(err).ToNot(HaveOccurred())
298+
_, err = os.CreateTemp(tmpDirWithSpaces, "example")
299+
Expect(err).ToNot(HaveOccurred())
300+
mountWithSpaces := tmpDirWithSpaces + ":/host folder"
301+
defer func() { _ = utils.GuardedRemoveAll(tmpDirWithSpaces) }()
302+
299303
name := randomString()
300304
i := new(initMachine)
301-
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withVolume(mount).withNow()).run()
305+
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withVolume(mount).withVolume(mountWithSpaces).withNow()).run()
302306
Expect(err).ToNot(HaveOccurred())
303307
Expect(session).To(Exit(0))
304308

@@ -307,6 +311,11 @@ var _ = Describe("podman machine init", func() {
307311
Expect(err).ToNot(HaveOccurred())
308312
Expect(sshSession).To(Exit(0))
309313
Expect(sshSession.outputToString()).To(ContainSubstring("example"))
314+
315+
sshSession, err = mb.setName(name).setCmd(ssh.withSSHCommand([]string{"ls \"/host folder\""})).run()
316+
Expect(err).ToNot(HaveOccurred())
317+
Expect(sshSession).To(Exit(0))
318+
Expect(sshSession.outputToString()).To(ContainSubstring("example"))
310319
})
311320

312321
It("machine init with ignition path", func() {

pkg/machine/hyperv/volumes.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"path"
9+
"strconv"
910
"strings"
1011

1112
"github.com/containers/podman/v5/pkg/machine"
@@ -48,7 +49,7 @@ func startShares(mc *vmconfigs.MachineConfig) error {
4849
if requiresChattr {
4950
args = append(args, "sudo", "chattr", "-i", "/", "; ")
5051
}
51-
args = append(args, "sudo", "mkdir", "-p", cleanTarget, "; ")
52+
args = append(args, "sudo", "mkdir", "-p", strconv.Quote(cleanTarget), "; ")
5253
if requiresChattr {
5354
args = append(args, "sudo", "chattr", "+i", "/", "; ")
5455
}
@@ -61,7 +62,7 @@ func startShares(mc *vmconfigs.MachineConfig) error {
6162
if mount.VSockNumber == nil {
6263
return errors.New("cannot start 9p shares with undefined vsock number")
6364
}
64-
args = append(args, "machine", "client9p", fmt.Sprintf("%d", *mount.VSockNumber), mount.Target)
65+
args = append(args, "machine", "client9p", fmt.Sprintf("%d", *mount.VSockNumber), strconv.Quote(mount.Target))
6566

6667
if err := machine.CommonSSH(mc.SSH.RemoteUsername, mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, args); err != nil {
6768
return err

pkg/machine/qemu/stubber.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func (q *QEMUStubber) MountVolumesToVM(mc *vmconfigs.MachineConfig, quiet bool)
349349
if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") {
350350
args = append(args, "sudo", "chattr", "-i", "/", ";")
351351
}
352-
args = append(args, "sudo", "mkdir", "-p", mount.Target)
352+
args = append(args, "sudo", "mkdir", "-p", strconv.Quote(mount.Target))
353353
if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") {
354354
args = append(args, ";", "sudo", "chattr", "+i", "/", ";")
355355
}
@@ -362,7 +362,7 @@ func (q *QEMUStubber) MountVolumesToVM(mc *vmconfigs.MachineConfig, quiet bool)
362362
// in other words we don't want to make people unnecessarily reprovision their machines
363363
// to upgrade from 9p to virtiofs.
364364
mountOptions := []string{"-t", "virtiofs"}
365-
mountOptions = append(mountOptions, []string{mount.Tag, mount.Target}...)
365+
mountOptions = append(mountOptions, []string{mount.Tag, strconv.Quote(mount.Target)}...)
366366
mountFlags := fmt.Sprintf("context=\"%s\"", machine.NFSSELinuxContext)
367367
if mount.ReadOnly {
368368
mountFlags += ",ro"

pkg/systemd/parser/split.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ func escapeString(escaped *strings.Builder, word string, isPath bool) {
473473
case '\\':
474474
escaped.WriteString("\\\\")
475475
case ' ':
476-
escaped.WriteString(" ")
476+
escaped.WriteString("\\x20")
477477
case '"':
478478
escaped.WriteString("\\\"")
479479
case '\'':

0 commit comments

Comments
 (0)