diff --git a/pkg/machine/apple/apple.go b/pkg/machine/apple/apple.go index f4aab463bf..93201407e9 100644 --- a/pkg/machine/apple/apple.go +++ b/pkg/machine/apple/apple.go @@ -96,19 +96,19 @@ func GenerateSystemDFilesForVirtiofsMounts(mounts []machine.VirtIoFs) ([]ignitio virtiofsAutomount := ignition.Unit{ Enabled: ignition.BoolToPtr(true), - Name: fmt.Sprintf("%s.automount", mnt.Tag), - Contents: ignition.StrToPtr(fmt.Sprintf(autoMountUnitFile, mnt.Target, mnt.Target)), + Name: fmt.Sprintf("%s.automount", parser.PathEscape(mnt.Target)), + Contents: ignition.StrToPtr(fmt.Sprintf(autoMountUnitFile, mnt.Tag, mnt.Target)), } virtiofsMount := ignition.Unit{ Enabled: ignition.BoolToPtr(true), - Name: fmt.Sprintf("%s.mount", mnt.Tag), + Name: fmt.Sprintf("%s.mount", parser.PathEscape(mnt.Target)), Contents: ignition.StrToPtr(fmt.Sprintf(mountUnitFile, mnt.Tag, mnt.Target)), } // This "unit" simulates something like systemctl enable virtiofs-mount-prepare@ enablePrep := ignition.Unit{ Enabled: ignition.BoolToPtr(true), - Name: fmt.Sprintf("virtiofs-mount-prepare@%s.service", mnt.Tag), + Name: fmt.Sprintf("virtiofs-mount-prepare@%s.service", parser.PathEscape(mnt.Target)), } unitFiles = append(unitFiles, virtiofsAutomount, virtiofsMount, enablePrep) diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index daf4880ef6..e3844cb8c2 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -207,7 +207,8 @@ var _ = Describe("podman machine init", func() { Expect(err).ToNot(HaveOccurred()) _, err = os.CreateTemp(tmpDir, "example") Expect(err).ToNot(HaveOccurred()) - mount := tmpDir + ":/testmountdir" + // Test long target path, see https://github.com/containers/podman/issues/22226 + mount := tmpDir + ":/very-long-test-mount-dir-path-more-than-thirty-six-bytes" defer func() { _ = utils.GuardedRemoveAll(tmpDir) }() name := randomString() @@ -217,7 +218,7 @@ var _ = Describe("podman machine init", func() { Expect(session).To(Exit(0)) ssh := sshMachine{} - sshSession, err := mb.setName(name).setCmd(ssh.withSSHCommand([]string{"ls /testmountdir"})).run() + sshSession, err := mb.setName(name).setCmd(ssh.withSSHCommand([]string{"ls /very-long-test-mount-dir-path-more-than-thirty-six-bytes"})).run() Expect(err).ToNot(HaveOccurred()) Expect(sshSession).To(Exit(0)) Expect(sshSession.outputToString()).To(ContainSubstring("example")) diff --git a/pkg/machine/volumes.go b/pkg/machine/volumes.go index b451e8c634..8fda9155e3 100644 --- a/pkg/machine/volumes.go +++ b/pkg/machine/volumes.go @@ -1,7 +1,8 @@ package machine import ( - "strings" + "crypto/sha256" + "encoding/hex" "github.com/containers/podman/v5/pkg/machine/vmconfigs" ) @@ -29,14 +30,13 @@ func (v VirtIoFs) Kind() string { return string(VirtIOFsVk) } -// unitName is the fq path where /'s are replaced with -'s -func (v VirtIoFs) unitName() string { - // delete the leading - - unit := strings.ReplaceAll(v.Target, "/", "-") - if strings.HasPrefix(unit, "-") { - return unit[1:] - } - return unit +// generateTag generates a tag for VirtIOFs mounts. +// AppleHV requires tags to be 36 bytes or fewer. +// SHA256 the path, then truncate to 36 bytes +func (v VirtIoFs) generateTag() string { + sum := sha256.Sum256([]byte(v.Target)) + stringSum := hex.EncodeToString(sum[:]) + return stringSum[:36] } func (v VirtIoFs) ToMount() vmconfigs.Mount { @@ -58,7 +58,7 @@ func NewVirtIoFsMount(src, target string, readOnly bool) VirtIoFs { Source: src, Target: target, } - vfs.Tag = vfs.unitName() + vfs.Tag = vfs.generateTag() return vfs } diff --git a/pkg/systemd/parser/split.go b/pkg/systemd/parser/split.go index c2778032a9..433e869a65 100644 --- a/pkg/systemd/parser/split.go +++ b/pkg/systemd/parser/split.go @@ -428,13 +428,17 @@ func splitString(s string, separators string, flags SplitFlags) ([]string, error return splitStringAppend(make([]string, 0), s, separators, flags) } -func charNeedEscape(c rune) bool { +func charNeedEscape(c rune, isPath bool) bool { if c > 128 { return false /* unicode is ok */ } + pathRune := (isPath && c == '-') || + (isPath && c == '/') + return unicode.IsSpace(c) || unicode.IsControl(c) || + pathRune || c == '"' || c == '\'' || c == '\\' @@ -442,18 +446,22 @@ func charNeedEscape(c rune) bool { func wordNeedEscape(word string) bool { for _, c := range word { - if charNeedEscape(c) { + if charNeedEscape(c, false) { return true } } - return false } func appendEscapeWord(escaped *strings.Builder, word string) { escaped.WriteRune('"') - for _, c := range word { - if charNeedEscape(c) { + escapeString(escaped, word, false) + escaped.WriteRune('"') +} + +func escapeString(escaped *strings.Builder, word string, isPath bool) { + for i, c := range word { + if charNeedEscape(c, isPath) { switch c { case '\a': escaped.WriteString("\\a") @@ -477,6 +485,10 @@ func appendEscapeWord(escaped *strings.Builder, word string) { escaped.WriteString("\\\"") case '\'': escaped.WriteString("'") + case '/': + if isPath && i != 0 { + escaped.WriteString("-") + } default: escaped.WriteString(fmt.Sprintf("\\x%.2x", c)) } @@ -484,7 +496,6 @@ func appendEscapeWord(escaped *strings.Builder, word string) { escaped.WriteRune(c) } } - escaped.WriteRune('"') } func escapeWords(words []string) string { @@ -500,6 +511,5 @@ func escapeWords(words []string) string { escaped.WriteString(word) } } - return escaped.String() } diff --git a/pkg/systemd/parser/unitfile.go b/pkg/systemd/parser/unitfile.go index b7b47d54a1..e9b0186611 100644 --- a/pkg/systemd/parser/unitfile.go +++ b/pkg/systemd/parser/unitfile.go @@ -932,3 +932,9 @@ func (f *UnitFile) GetTemplateParts() (string, string) { } return parts[0] + "@" + ext, parts[1] } + +func PathEscape(path string) string { + var escaped strings.Builder + escapeString(&escaped, path, true) + return escaped.String() +}