mirror of https://github.com/containers/podman.git
100 lines
3.4 KiB
Go
100 lines
3.4 KiB
Go
//go:build linux || freebsd || windows
|
|
|
|
package qemu
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/podman/v5/pkg/machine/define"
|
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
|
"github.com/containers/storage/pkg/fileutils"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// VirtiofsdSpawner spawns an instance of virtiofsd
|
|
type virtiofsdSpawner struct {
|
|
runtimeDir *define.VMFile
|
|
binaryPath string
|
|
mountCount uint
|
|
}
|
|
|
|
func newVirtiofsdSpawner(runtimeDir *define.VMFile) (*virtiofsdSpawner, error) {
|
|
var binaryPath string
|
|
cfg, err := config.Default()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
binaryPath, err = cfg.FindHelperBinary("virtiofsd", true)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find virtiofsd: %w", err)
|
|
}
|
|
return &virtiofsdSpawner{binaryPath: binaryPath, runtimeDir: runtimeDir}, nil
|
|
}
|
|
|
|
// createVirtiofsCmd returns a new command instance configured to launch virtiofsd.
|
|
func (v *virtiofsdSpawner) createVirtiofsCmd(directory, socketPath string) *exec.Cmd {
|
|
args := []string{"--sandbox", "none", "--socket-path", socketPath, "--shared-dir", "."}
|
|
// We don't need seccomp filtering; we trust our workloads. This incidentally
|
|
// works around issues like https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/200.
|
|
args = append(args, "--seccomp=none")
|
|
cmd := exec.Command(v.binaryPath, args...)
|
|
// This sets things up so that the `.` we passed in the arguments is the target directory
|
|
cmd.Dir = directory
|
|
// Quiet the daemon by default
|
|
cmd.Env = append(cmd.Environ(), "RUST_LOG=ERROR")
|
|
cmd.Stdin = nil
|
|
cmd.Stdout = nil
|
|
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
|
cmd.Stderr = os.Stderr
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
type virtiofsdHelperCmd struct {
|
|
command exec.Cmd
|
|
socket *define.VMFile
|
|
}
|
|
|
|
// spawnForMount returns on success a combination of qemu commandline and child process for virtiofsd
|
|
func (v *virtiofsdSpawner) spawnForMount(hostmnt *vmconfigs.Mount) ([]string, *virtiofsdHelperCmd, error) {
|
|
logrus.Debugf("Initializing virtiofsd mount for %s", hostmnt.Source)
|
|
// By far the most common failure to spawn virtiofsd will be a typo'd source directory,
|
|
// so let's synchronously check that ourselves here.
|
|
if err := fileutils.Exists(hostmnt.Source); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to access virtiofs source directory %s", hostmnt.Source)
|
|
}
|
|
virtiofsChar := fmt.Sprintf("virtiofschar%d", v.mountCount)
|
|
virtiofsCharPath, err := v.runtimeDir.AppendToNewVMFile(virtiofsChar, nil)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
qemuCommand := []string{}
|
|
|
|
qemuCommand = append(qemuCommand, "-chardev", fmt.Sprintf("socket,id=%s,path=%s", virtiofsChar, virtiofsCharPath.Path))
|
|
qemuCommand = append(qemuCommand, "-device", fmt.Sprintf("vhost-user-fs-pci,queue-size=1024,chardev=%s,tag=%s", virtiofsChar, hostmnt.Tag))
|
|
// TODO: Honor hostmnt.readonly somehow here (add an option to virtiofsd)
|
|
virtiofsdCmd := v.createVirtiofsCmd(hostmnt.Source, virtiofsCharPath.Path)
|
|
if err := virtiofsdCmd.Start(); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to start virtiofsd")
|
|
}
|
|
// Wait for the socket
|
|
for {
|
|
if err := fileutils.Exists(virtiofsCharPath.Path); err == nil {
|
|
break
|
|
}
|
|
logrus.Debugf("waiting for virtiofsd socket %q", virtiofsCharPath.Path)
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
// Increment our count of mounts which are used to create unique names for the devices
|
|
v.mountCount += 1
|
|
return qemuCommand, &virtiofsdHelperCmd{
|
|
command: *virtiofsdCmd,
|
|
socket: virtiofsCharPath,
|
|
}, nil
|
|
}
|