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 <acui@redhat.com>
This commit is contained in:
Ashley Cui
2024-04-05 17:53:54 -04:00
parent ba73c604c4
commit a140c74ba4
5 changed files with 40 additions and 23 deletions

View File

@ -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)

View File

@ -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"))

View File

@ -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
}

View File

@ -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()
}

View File

@ -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()
}