spiegel_podman/cmd/podman/machine/cp.go
Brent Baude 0f22c1c772 Provider obfuscation in command line
For Podman 6, we still have providers and will continue to have a default provider for each platform.  But where a platform has multiple providers, we want users to be able to cross provider boudnaries imposed in Podman 4/5.  The key change is to look up virtual machines by name, as before, but to then also iterate all possible providers.  As of this PR, init will still only create with the default provider, but a subsequent PR will introdouce an provider override.

I also removed the "--all-providers" command line option on `podman
machine ls` because it no longer makes sense. And I marked the all
provider list test to be skipped.

Signed-off-by: Brent Baude <bbaude@redhat.com>
2025-10-27 08:40:19 -05:00

156 lines
4.1 KiB
Go

//go:build amd64 || arm64
package machine
import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"github.com/containers/podman/v6/cmd/podman/registry"
"github.com/containers/podman/v6/libpod/events"
"github.com/containers/podman/v6/pkg/copy"
"github.com/containers/podman/v6/pkg/machine"
"github.com/containers/podman/v6/pkg/machine/define"
"github.com/containers/podman/v6/pkg/machine/shim"
"github.com/containers/podman/v6/pkg/machine/vmconfigs"
"github.com/containers/podman/v6/pkg/specgen"
"github.com/spf13/cobra"
)
type cpOptions struct {
Quiet bool
Machine *vmconfigs.MachineConfig
IsSrc bool
SrcPath string
DestPath string
}
var (
cpCmd = &cobra.Command{
Use: "cp [options] SRC_PATH DEST_PATH",
Short: "Securely copy contents between the virtual machine",
Long: "Securely copy files or directories between the virtual machine and your host",
PersistentPreRunE: machinePreRunE,
RunE: cp,
Args: cobra.ExactArgs(2),
Example: `podman machine cp ~/ca.crt podman-machine-default:/etc/containers/certs.d/ca.crt`,
ValidArgsFunction: autocompleteMachineCp,
}
cpOpts = cpOptions{}
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: cpCmd,
Parent: machineCmd,
})
flags := cpCmd.Flags()
quietFlagName := "quiet"
flags.BoolVarP(&cpOpts.Quiet, quietFlagName, "q", false, "Suppress copy status output")
}
func cp(_ *cobra.Command, args []string) error {
var err error
srcMachine, srcPath, destMachine, destPath, err := copy.ParseSourceAndDestination(args[0], args[1])
if err != nil {
return err
}
// NOTE: This will most likely break hyperv or wsl machines with single-letter
// names. It is most likely similar to https://github.com/containers/podman/issues/25218
//
// Passing an absolute windows path of the format <volume>:\<path> will cause
// `copy.ParseSourceAndDestination` to think the volume is a Machine. Check
// if the raw cmdline argument is a Windows host path.
if specgen.IsHostWinPath(args[0]) {
srcMachine = ""
srcPath = args[0]
}
if specgen.IsHostWinPath(args[1]) {
destMachine = ""
destPath = args[1]
}
vmName, err := resolveMachineName(srcMachine, destMachine)
if err != nil {
return err
}
mc, vmProvider, err := shim.VMExists(vmName)
if err != nil {
return err
}
state, err := vmProvider.State(mc, false)
if err != nil {
return err
}
if state != define.Running {
return fmt.Errorf("vm %q is not running", mc.Name)
}
cpOpts.Machine = mc
cpOpts.SrcPath = srcPath
cpOpts.DestPath = destPath
err = localhostSSHCopy(&cpOpts)
if err != nil {
return fmt.Errorf("copy failed: %s", err.Error())
}
fmt.Println("Copy successful")
newMachineEvent(events.Copy, events.Event{Name: mc.Name})
return nil
}
// localhostSSHCopy uses scp to copy files from/to a localhost machine using ssh.
func localhostSSHCopy(opts *cpOptions) error {
srcPath := opts.SrcPath
destPath := opts.DestPath
sshConfig := opts.Machine.SSH
username := sshConfig.RemoteUsername
if cpOpts.Machine.HostUser.Rootful {
username = "root"
}
username += "@localhost:"
if opts.IsSrc {
srcPath = username + srcPath
} else {
destPath = username + destPath
}
args := []string{"-r", "-i", sshConfig.IdentityPath, "-P", strconv.Itoa(sshConfig.Port)}
args = append(args, machine.LocalhostSSHArgs()...) // Warning: This MUST NOT be generalized to allow communication over untrusted networks.
args = append(args, []string{srcPath, destPath}...)
cmd := exec.Command("scp", args...)
if !opts.Quiet {
cmd.Stdout = os.Stdout
}
cmd.Stderr = os.Stderr
return cmd.Run()
}
func resolveMachineName(srcMachine, destMachine string) (string, error) {
if len(srcMachine) > 0 && len(destMachine) > 0 {
return "", errors.New("copying between two machines is unsupported")
}
if len(srcMachine) == 0 && len(destMachine) == 0 {
return "", errors.New("a machine name must prefix either the source path or destination path")
}
name := destMachine
if len(srcMachine) > 0 {
cpOpts.IsSrc = true
name = srcMachine
}
return name, nil
}