From a140c74ba4e054446c16b960f36e729b324f56df Mon Sep 17 00:00:00 2001 From: Ashley Cui Date: Fri, 5 Apr 2024 17:53:54 -0400 Subject: [PATCH] Fix machine volumes with long path and paths with dashes AppleHV accepts a max 36 bytes for mount tags. Instead of using the fully qualified path for the mount tag, SHA256 the path, and truncate the shasum to 36 bytes. Also correctly escape dashes in mounted paths. Signed-off-by: Ashley Cui --- pkg/machine/apple/apple.go | 8 ++++---- pkg/machine/e2e/init_test.go | 5 +++-- pkg/machine/volumes.go | 20 ++++++++++---------- pkg/systemd/parser/split.go | 24 +++++++++++++++++------- pkg/systemd/parser/unitfile.go | 6 ++++++ 5 files changed, 40 insertions(+), 23 deletions(-) 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() +}