Repository: containers/podman
Pull request #25485: Don't try to resolve host path if copying from stdin
Author: login: danegsta
Labels: name: lgtm
description: Indicates that a PR is ready to be merged.
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: machine
name: release-note-none
name: locked - please file new issue/PR
description: Assist humans wanting to comment on an old issue or PR with locked comments.
Created: 2025-03-05T17:38:07Z | Merged: 2025-03-09T18:14:52Z

<!--
Thanks for sending a pull request!

Please make sure you've read our contributing guidelines and how to submit a pull request (https://github.com/containers/podman/blob/main/CONTRIBUTING.md#submitting-pull-requests).

Finally, be sure to sign commits with your real name. Since by opening
a PR you already have commits, you can add signatures if needed with
something like `git commit -s --amend`.
-->

#### Does this PR introduce a user-facing change?

<!--
If no, just write `None` in the release-note block below. If yes, a release note
is required: Enter your extended release note in the block below. If the PR
requires additional action from users switching to the new release, include the
string "action required".

For more information on release notes, please follow the Kubernetes model:
https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note
None
```

Resolves #25472

Calling `copy.ResolveHostPath` fails when copying from stdin on Windows as /dev/stdin isn't a real folder. The result of `copy.ResolveHostPath` isn't actually used in the stdin code flow, so an easy fix is to condition the call on whether isStdin is true or false. The only potential behavior change is that, if for some unlikely reason /dev/stdin was missing on Linux or MacOS, the copy command would fail with a different error than it did previously.

Linked issue #25472: Copying files into a container via stdin with `podman container cp -a - ...` fails on Windows
### Issue Description

When attempting to copy files into a container on Windows via `docker container cp -a - ...` the CLI returns an error: `Error: "/dev/stdin" could not be found on the host: no such file or directory`.

Looking at the code for the cp command, the issue is due to the [`copy.ResolveHostPath(hostPath)` call in cp.go](https://github.com/containers/podman/blob/7f27a6661ae12052516bc27f692b4278b2fe574d/cmd/podman/containers/cp.go#L349). When copying from stdio on Windows, `copy.ResolveHostPath` ends up trying to do an `os.Stat` call for `/dev/stdio` due to that still being the value for `os.Stdin.Name()` on Windows, but this understandably fails and exits out. However, there's no real reason to do the `copy.ResolveHostPath` check when the input is from stdin, as the result isn't actually used during stdin copy. Ideally, the call to `copy.ResolveHostPath` can be conditioned on `!isStdin`.

### Steps to reproduce the issue

Steps to reproduce the issue
1. On Windows, prepare a tar file to copy into a container
2. Create or run a new container
3. Run `Get-Content -Raw input.tar | podman container cp -a - <container>:/tmp` in PowerShell


### Describe the results you received

Command failed with `Error: "/dev/stdin" could not be found on the host: no such file or directory`

### Describe the results you expected

Contents of tar file are copied into the container. This works on MacOS and in Linux, but fails on Windows.

### podman info output

```yaml
Client:
  APIVersion: 5.4.0
  Built: 1739297196
  BuiltTime: Tue Feb 11 10:06:36 2025
  GitCommit: f9f7d48b24b1ca4403f189caaeab1cb8ff4a9aa2
  GoVersion: go1.23.6
  Os: windows
  OsArch: windows/amd64
  Version: 5.4.0
host:
  arch: amd64
  buildahVersion: 1.36.0-dev
  cgroupControllers:
  - cpuset
  - cpu
  - cpuacct
  - blkio
  - memory
  - devices
  - freezer
  - net_cls
  - perf_event
  - net_prio
  - hugetlb
  - pids
  - rdma
  - misc
  cgroupManager: cgroupfs
  cgroupVersion: v1
  conmon:
    package: conmon-2.1.10-1.20240313132120223048.main.19.gaffab49.fc39.x86_64
    path: /usr/bin/conmon
    version: 'conmon version 2.1.10, commit: '
  cpuUtilization:
    idlePercent: 98.79
    systemPercent: 0.21
    userPercent: 1
  cpus: 16
  databaseBackend: sqlite
  distribution:
    distribution: fedora
    variant: container
    version: "39"
  eventLogger: journald
  freeLocks: 2026
  hostname: CPC-daneg-UN5C5
  idMappings:
    gidmap: null
    uidmap: null
  kernel: 5.15.167.4-microsoft-standard-WSL2
  linkmode: dynamic
  logDriver: journald
  memFree: 30125613056
  memTotal: 33632358400
  networkBackend: netavark
  networkBackendInfo:
    backend: netavark
    dns:
      package: aardvark-dns-1.10.0-1.20240327154432243377.main.24.g9e8db9c.fc39.x86_64
      path: /usr/libexec/podman/aardvark-dns
      version: aardvark-dns 1.11.0-dev
    package: netavark-1.10.1-1.20240327174936429720.main.58.gd5be5ad.fc39.x86_64
    path: /usr/libexec/podman/netavark
    version: netavark 1.11.0-dev
  ociRuntime:
    name: crun
    package: crun-1.14.4-1.20240327203052128727.main.16.gdcbdf9d.fc39.x86_64
    path: /usr/bin/crun
    version: |-
      crun version UNKNOWN
      commit: fc5c0828a410a954dc1c864f6bba070d1443d297
      rundir: /run/crun
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +LIBKRUN +WASM:wasmedge +YAJL
  os: linux
  pasta:
    executable: /usr/bin/pasta
    package: passt-0^20240220.g1e6f92b-1.fc39.x86_64
    version: |
      pasta 0^20240220.g1e6f92b-1.fc39.x86_64
      Copyright Red Hat
      GNU General Public License, version 2 or later
        <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
      This is free software: you are free to change and redistribute it.
      There is NO WARRANTY, to the extent permitted by law.
  remoteSocket:
    exists: true
    path: /run/podman/podman.sock
  rootlessNetworkCmd: pasta
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: false
    seccompEnabled: true
    seccompProfilePath: /usr/share/containers/seccomp.json
    selinuxEnabled: false
  serviceIsRemote: true
  slirp4netns:
    executable: ""
    package: ""
    version: ""
  swapFree: 8589934592
  swapTotal: 8589934592
  uptime: 270h 42m 42.00s (Approximately 11.25 days)
  variant: ""
plugins:
  authorization: null
  log:
  - k8s-file
  - none
  - passthrough
  - journald
  network:
  - bridge
  - macvlan
  - ipvlan
  volume:
  - local
registries:
  search:
  - docker.io
store:
  configFile: /usr/share/containers/storage.conf
  containerStore:
    number: 3
    paused: 0
    running: 1
    stopped: 2
  graphDriverName: overlay
  graphOptions:
    overlay.mountopt: nodev,metacopy=on
  graphRoot: /var/lib/containers/storage
  graphRootAllocated: 1081101176832
  graphRootUsed: 15046189056
  graphStatus:
    Backing Filesystem: extfs
    Native Overlay Diff: "false"
    Supports d_type: "true"
    Supports shifting: "false"
    Supports volatile: "true"
    Using metacopy: "true"
  imageCopyTmpDir: /var/tmp
  imageStore:
    number: 31
  runRoot: /run/containers/storage
  transientStore: false
  volumePath: /var/lib/containers/storage/volumes
version:
  APIVersion: 5.1.0-dev-8ec821452
  Built: 1711497600
  BuiltTime: Wed Mar 27 00:00:00 2024
  GitCommit: ""
  GoVersion: go1.21.8
  Os: linux
  OsArch: linux/amd64
  Version: 5.1.0-dev-8ec821452
```

### Podman in a container

No

### Privileged Or Rootless

Privileged

### Upstream Latest Release

Yes

### Additional environment details

Running Podman Desktop (1.17.1) on Windows 11

### Additional information

I've tested changing lines 348-352 in cp.go from

```Go
	// Make sure that host path exists.
	hostInfo, err := copy.ResolveHostPath(hostPath)
	if err != nil {
		return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
	}
```

to

```Go
	hostInfo := &copy.FileInfo{}
	var err error

	if !isStdin {
		// Make sure that host path exists.
		hostInfo, err = copy.ResolveHostPath(hostPath)
		if err != nil {
			return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
		}
	}
```

and running a local build of the CLI and that resolves the issue.
Linked issue comments: user: login: lsm5
created_at: 2025-03-05T10:18:16Z
body: @l0rd PTAL
author_association: MEMBER
user: login: danegsta
created_at: 2025-03-05T17:19:58Z
body: Happy to open a PR with the changes I tested if that'd help.
author_association: CONTRIBUTOR
user: login: l0rd
created_at: 2025-03-05T18:40:49Z
body: Thank you @danegsta I will look at it tomorrow
author_association: MEMBER

Diff:
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index be8c8c1a3c9..648c7d5f550 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -339,16 +339,18 @@ func copyToContainer(container string, containerPath string, hostPath string) er
 		return err
 	}
 
+	hostInfo := &copy.FileInfo{}
+	var err error
+
 	isStdin := false
 	if hostPath == "-" {
-		hostPath = os.Stdin.Name()
 		isStdin = true
-	}
-
-	// Make sure that host path exists.
-	hostInfo, err := copy.ResolveHostPath(hostPath)
-	if err != nil {
-		return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
+	} else {
+		// Make sure that host path exists if not copying from stdin.
+		hostInfo, err = copy.ResolveHostPath(hostPath)
+		if err != nil {
+			return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
+		}
 	}
 
 	containerBaseName, containerInfo, containerResolvedToParentDir, err := resolvePathOnDestinationContainer(container, containerPath, isStdin)
diff --git a/pkg/machine/e2e/basic_test.go b/pkg/machine/e2e/basic_test.go
index c060479cd98..753066b9fb1 100644
--- a/pkg/machine/e2e/basic_test.go
+++ b/pkg/machine/e2e/basic_test.go
@@ -1,8 +1,11 @@
 package e2e_test
 
 import (
+	"archive/tar"
+	"bytes"
 	"fmt"
 	"io"
+	"io/fs"
 	"net"
 	"net/http"
 	"net/url"
@@ -253,6 +256,87 @@ var _ = Describe("run basic podman commands", func() {
 		Expect(run).To(Exit(0))
 		Expect(build.outputToString()).To(ContainSubstring(name))
 	})
+
+	It("Copy ops", func() {
+		var (
+			stdinDirectory = "stdin-dir"
+			stdinFile      = "file.txt"
+		)
+
+		now := time.Now()
+
+		tarBuffer := &bytes.Buffer{}
+		tw := tar.NewWriter(tarBuffer)
+
+		// Write a directory header to the tar
+		err := tw.WriteHeader(&tar.Header{
+			Name:       stdinDirectory,
+			Mode:       int64(0640 | fs.ModeDir),
+			Gid:        1000,
+			ModTime:    now,
+			ChangeTime: now,
+			AccessTime: now,
+			Typeflag:   tar.TypeDir,
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		// Write a file header to the tar
+		err = tw.WriteHeader(&tar.Header{
+			Name:       path.Join(stdinDirectory, stdinFile),
+			Mode:       0755,
+			Uid:        1000,
+			ModTime:    now,
+			ChangeTime: now,
+			AccessTime: now,
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		err = tw.Close()
+		Expect(err).ToNot(HaveOccurred())
+
+		name := randomString()
+		i := new(initMachine)
+		session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(session).To(Exit(0))
+
+		bm := basicMachine{}
+		newImgs, err := mb.setCmd(bm.withPodmanCommand([]string{"pull", "quay.io/libpod/alpine_nginx"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(newImgs).To(Exit(0))
+		Expect(newImgs.outputToStringSlice()).To(HaveLen(1))
+
+		createAlp, err := mb.setCmd(bm.withPodmanCommand([]string{"create", "quay.io/libpod/alpine_nginx"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(createAlp).To(Exit(0))
+		Expect(createAlp.outputToStringSlice()).To(HaveLen(1))
+
+		// Testing stdin copy with archive mode disabled (ownership will be determined by the tar file)
+		containerID := createAlp.outputToStringSlice()[0]
+		cpTar, err := mb.setCmd(bm.withPodmanCommand([]string{"cp", "-a=false", "-", containerID + ":/tmp"})).setStdin(tarBuffer).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(cpTar).To(Exit(0))
+
+		start, err := mb.setCmd(bm.withPodmanCommand([]string{"start", containerID})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(start).To(Exit(0))
+
+		// Check the directory is created with the appropriate mode, uid, gid
+		exec, err := mb.setCmd(bm.withPodmanCommand([]string{"exec", containerID, "stat", "-c", "%a %u %g", "/tmp/stdin-dir"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(exec).To(Exit(0))
+		execStdOut := exec.outputToStringSlice()
+		Expect(execStdOut).To(HaveLen(1))
+		Expect(execStdOut[0]).To(Equal("640 0 1000"))
+
+		// Check the file is created with the appropriate mode, uid, gid
+		exec, err = mb.setCmd(bm.withPodmanCommand([]string{"exec", containerID, "stat", "-c", "%a %u %g", "/tmp/stdin-dir/file.txt"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(exec).To(Exit(0))
+		execStdOut = exec.outputToStringSlice()
+		Expect(execStdOut).To(HaveLen(1))
+		Expect(execStdOut[0]).To(Equal("755 1000 0"))
+	})
 })
 
 func testHTTPServer(port string, shouldErr bool, expectedResponse string) {
diff --git a/pkg/machine/e2e/config_test.go b/pkg/machine/e2e/config_test.go
index 7d2d420c705..388cdc272ed 100644
--- a/pkg/machine/e2e/config_test.go
+++ b/pkg/machine/e2e/config_test.go
@@ -3,6 +3,7 @@ package e2e_test
 import (
 	"encoding/json"
 	"fmt"
+	"io"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -44,6 +45,7 @@ type machineSession struct {
 
 type machineTestBuilder struct {
 	cmd          []string
+	stdin        io.Reader
 	imagePath    string
 	name         string
 	names        []string
@@ -139,6 +141,13 @@ func (m *machineTestBuilder) setCmd(mc machineCommand) *machineTestBuilder {
 		m.names = append(m.names, m.name)
 	}
 	m.cmd = mc.buildCmd(m)
+	m.stdin = nil
+	return m
+}
+
+// setStdin sets the stdin for the next command to be run
+func (m *machineTestBuilder) setStdin(data io.Reader) *machineTestBuilder {
+	m.stdin = data
 	return m
 }
 
@@ -152,7 +161,7 @@ func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuild
 func (m *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) {
 	args := []string{"machine", "inspect"}
 	args = append(args, m.names...)
-	session, err := runWrapper(m.podmanBinary, args, defaultTimeout, true)
+	session, err := runWrapper(m.podmanBinary, args, nil, defaultTimeout, true)
 	if err != nil {
 		return nil, -1, err
 	}
@@ -162,20 +171,24 @@ func (m *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, er
 }
 
 func (m *machineTestBuilder) runWithoutWait() (*machineSession, error) {
-	return runWrapper(m.podmanBinary, m.cmd, m.timeout, false)
+	return runWrapper(m.podmanBinary, m.cmd, m.stdin, m.timeout, false)
 }
 
 func (m *machineTestBuilder) run() (*machineSession, error) {
-	s, err := runWrapper(m.podmanBinary, m.cmd, m.timeout, true)
+	s, err := runWrapper(m.podmanBinary, m.cmd, m.stdin, m.timeout, true)
 	return s, err
 }
 
-func runWrapper(podmanBinary string, cmdArgs []string, timeout time.Duration, wait bool) (*machineSession, error) {
+func runWrapper(podmanBinary string, cmdArgs []string, stdinData io.Reader, timeout time.Duration, wait bool) (*machineSession, error) {
 	if len(os.Getenv("DEBUG")) > 0 {
 		cmdArgs = append([]string{"--log-level=debug"}, cmdArgs...)
 	}
 	GinkgoWriter.Println(podmanBinary + " " + strings.Join(cmdArgs, " "))
 	c := exec.Command(podmanBinary, cmdArgs...)
+	if stdinData != nil {
+		c.Stdin = stdinData
+	}
+
 	session, err := Start(c, GinkgoWriter, GinkgoWriter)
 	if err != nil {
 		Fail(fmt.Sprintf("Unable to start session: %q", err))


Reviews:
user: login: Luap99
body: Also please squash all commits into one.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T10:49:51Z
user: login: danegsta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:50:24Z
user: login: danegsta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:51:18Z
user: login: Luap99
body: /lgtm
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-08T11:12:06Z

Review comments:
diff_hunk: @@ -44,6 +45,7 @@ type machineSession struct {
 
 type machineTestBuilder struct {
 	cmd          []string
+	stdin        *bytes.Buffer
path: pkg/machine/e2e/config_test.go
user: login: Luap99
body: This should just be an `io.Reader` so it is not limited to one specific type
created_at: 2025-03-06T10:40:37Z
author_association: MEMBER
diff_hunk: @@ -253,6 +255,62 @@ var _ = Describe("run basic podman commands", func() {
 		Expect(run).To(Exit(0))
 		Expect(build.outputToString()).To(ContainSubstring(name))
 	})
+
+	It("Copy ops", func() {
+		var (
+			stdinDirectory = "stdin-dir"
+			stdinFile      = "file.txt"
+		)
+
+		now := time.Now()
+
+		tarBuffer := &bytes.Buffer{}
+		tw := tar.NewWriter(tarBuffer)
+
+		err := tw.WriteHeader(&tar.Header{
+			Name:       path.Join(stdinDirectory, stdinFile),
+			Mode:       0755,
+			Size:       0,
+			ModTime:    now,
+			ChangeTime: now,
+			AccessTime: now,
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		err = tw.Close()
+		Expect(err).ToNot(HaveOccurred())
+
+		name := randomString()
+		i := new(initMachine)
+		session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(session).To(Exit(0))
+
+		bm := basicMachine{}
+		imgs, err := mb.setCmd(bm.withPodmanCommand([]string{"images", "-q"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(imgs).To(Exit(0))
+		Expect(imgs.outputToStringSlice()).To(BeEmpty())
+
+		newImgs, err := mb.setCmd(bm.withPodmanCommand([]string{"pull", "quay.io/libpod/alpine_nginx"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(newImgs).To(Exit(0))
+		Expect(newImgs.outputToStringSlice()).To(HaveLen(1))
+
+		createAlp, err := mb.setCmd(bm.withPodmanCommand([]string{"create", "quay.io/libpod/alpine_nginx", "ls", "/tmp/stdin-dir"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(createAlp).To(Exit(0))
+		Expect(createAlp.outputToStringSlice()).To(HaveLen(1))
+
+		containerID := createAlp.outputToStringSlice()[0]
+		cpTar, err := mb.setCmd(bm.withPodmanCommand([]string{"cp", "-", containerID + ":/tmp"})).setStdin(tarBuffer).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(cpTar).To(Exit(0))
+
+		exec, err := mb.setCmd(bm.withPodmanCommand([]string{"start", containerID})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(exec).To(Exit(0))
path: pkg/machine/e2e/basic_test.go
user: login: Luap99
body: This doesn't actually verify anything useful other than the the cp command worked without an error.
start here just executes the ls command which will always work regardless of if the dir exists or not.

you likely want to use `start --attach` here and then please make sure to check the actual command output that is containers your file.

created_at: 2025-03-06T10:45:16Z
author_association: MEMBER
diff_hunk: @@ -253,6 +255,62 @@ var _ = Describe("run basic podman commands", func() {
 		Expect(run).To(Exit(0))
 		Expect(build.outputToString()).To(ContainSubstring(name))
 	})
+
+	It("Copy ops", func() {
+		var (
+			stdinDirectory = "stdin-dir"
+			stdinFile      = "file.txt"
+		)
+
+		now := time.Now()
+
+		tarBuffer := &bytes.Buffer{}
+		tw := tar.NewWriter(tarBuffer)
+
+		err := tw.WriteHeader(&tar.Header{
+			Name:       path.Join(stdinDirectory, stdinFile),
+			Mode:       0755,
+			Size:       0,
+			ModTime:    now,
+			ChangeTime: now,
+			AccessTime: now,
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		err = tw.Close()
+		Expect(err).ToNot(HaveOccurred())
+
+		name := randomString()
+		i := new(initMachine)
+		session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(session).To(Exit(0))
+
+		bm := basicMachine{}
+		imgs, err := mb.setCmd(bm.withPodmanCommand([]string{"images", "-q"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(imgs).To(Exit(0))
+		Expect(imgs.outputToStringSlice()).To(BeEmpty())
path: pkg/machine/e2e/basic_test.go
user: login: Luap99
body: This can be removed, it does nothing for you test
created_at: 2025-03-06T10:46:03Z
author_association: MEMBER
diff_hunk: @@ -345,10 +345,15 @@ func copyToContainer(container string, containerPath string, hostPath string) er
 		isStdin = true
 	}
 
-	// Make sure that host path exists.
-	hostInfo, err := copy.ResolveHostPath(hostPath)
-	if err != nil {
-		return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
+	hostInfo := &copy.FileInfo{}
+	var err error
+
+	if !isStdin {
path: cmd/podman/containers/cp.go
user: login: Luap99
body: this could just be an else on the conditional above instead of a new if
created_at: 2025-03-06T10:49:26Z
author_association: MEMBER
diff_hunk: @@ -345,10 +345,15 @@ func copyToContainer(container string, containerPath string, hostPath string) er
 		isStdin = true
 	}
 
-	// Make sure that host path exists.
-	hostInfo, err := copy.ResolveHostPath(hostPath)
-	if err != nil {
-		return fmt.Errorf("%q could not be found on the host: %w", hostPath, err)
+	hostInfo := &copy.FileInfo{}
+	var err error
+
+	if !isStdin {
path: cmd/podman/containers/cp.go
user: login: danegsta
body: Simplified into a single if/else with the stdin check.
created_at: 2025-03-06T17:50:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -253,6 +255,62 @@ var _ = Describe("run basic podman commands", func() {
 		Expect(run).To(Exit(0))
 		Expect(build.outputToString()).To(ContainSubstring(name))
 	})
+
+	It("Copy ops", func() {
+		var (
+			stdinDirectory = "stdin-dir"
+			stdinFile      = "file.txt"
+		)
+
+		now := time.Now()
+
+		tarBuffer := &bytes.Buffer{}
+		tw := tar.NewWriter(tarBuffer)
+
+		err := tw.WriteHeader(&tar.Header{
+			Name:       path.Join(stdinDirectory, stdinFile),
+			Mode:       0755,
+			Size:       0,
+			ModTime:    now,
+			ChangeTime: now,
+			AccessTime: now,
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		err = tw.Close()
+		Expect(err).ToNot(HaveOccurred())
+
+		name := randomString()
+		i := new(initMachine)
+		session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(session).To(Exit(0))
+
+		bm := basicMachine{}
+		imgs, err := mb.setCmd(bm.withPodmanCommand([]string{"images", "-q"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(imgs).To(Exit(0))
+		Expect(imgs.outputToStringSlice()).To(BeEmpty())
+
+		newImgs, err := mb.setCmd(bm.withPodmanCommand([]string{"pull", "quay.io/libpod/alpine_nginx"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(newImgs).To(Exit(0))
+		Expect(newImgs.outputToStringSlice()).To(HaveLen(1))
+
+		createAlp, err := mb.setCmd(bm.withPodmanCommand([]string{"create", "quay.io/libpod/alpine_nginx", "ls", "/tmp/stdin-dir"})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(createAlp).To(Exit(0))
+		Expect(createAlp.outputToStringSlice()).To(HaveLen(1))
+
+		containerID := createAlp.outputToStringSlice()[0]
+		cpTar, err := mb.setCmd(bm.withPodmanCommand([]string{"cp", "-", containerID + ":/tmp"})).setStdin(tarBuffer).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(cpTar).To(Exit(0))
+
+		exec, err := mb.setCmd(bm.withPodmanCommand([]string{"start", containerID})).run()
+		Expect(err).ToNot(HaveOccurred())
+		Expect(exec).To(Exit(0))
path: pkg/machine/e2e/basic_test.go
user: login: danegsta
body: I updated the test to do create, copy, start, and then run stat to check the expected directory and file were created.
created_at: 2025-03-06T17:51:18Z
author_association: CONTRIBUTOR

PR comments:
user: login: danegsta
created_at: 2025-03-05T17:59:15Z
body: This scenario is [already covered in the existing tests](https://github.com/containers/podman/blob/919247a77cbea0b0d5edab573b287ca481f5b700/test/system/065-cp.bats#L863); there's not really any additional test coverage to add outside of the general ability to run the tests on Windows.
author_association: CONTRIBUTOR
user: login: l0rd
created_at: 2025-03-05T18:45:11Z
body: > This scenario is [already covered in the existing tests](https://github.com/containers/podman/blob/919247a77cbea0b0d5edab573b287ca481f5b700/test/system/065-cp.bats#L863); there's not really any additional test coverage to add outside of the general ability to run the tests on Windows.

Right. Machine tests are the ones that are run on Windows, and you can find them in [pkg/machine/e2e/](https://github.com/containers/podman/blob/main/pkg/machine/e2e/README.md)
author_association: MEMBER
user: login: danegsta
created_at: 2025-03-05T19:01:44Z
body: I'm updating the e2e tests to include copy over stdin.
author_association: CONTRIBUTOR
user: login: l0rd
created_at: 2025-03-06T12:19:14Z
body: Thank you @danegsta for submitting this PR and in particular for adding the first cp e2e test for machine. Please address @Luap99 suggestions and remember to sign your commit so that [DCO check passes](https://probot.github.io/apps/dco/).
author_association: MEMBER
user: login: danegsta
created_at: 2025-03-06T18:17:54Z
body: I've squashed the commits and made sure to sign. Sorry for the extra commit noise as this is my first PR to the project, so needed to get my head wrapped around the expected commit format.
author_association: CONTRIBUTOR
user: login: l0rd
created_at: 2025-03-06T22:04:13Z
body: LGTM, I have retriggered the tests that are failing as failures don't seem related to this PR.
author_association: MEMBER
user: login: Luap99
created_at: 2025-03-07T10:24:11Z
body: Sorry for just mention this now but can you fix up the commit message.
```
Add e2e test for podman cp using stdio

Fix lint error

Simplify resetting stdin

Update new test case, avoid additional if check

Make test stdio a Reader

Fix lint errors
```

There is no useful information in this so just drop it. And instead `Fixes: #25472` should be added in the commit message not just the PR description, https://github.com/containers/podman/blob/main/CONTRIBUTING.md#describe-your-changes-in-commit-messages

author_association: MEMBER
user: login: mheon
created_at: 2025-03-07T13:43:23Z
body: LGTM, ready to merge once the commit message is updated.
/approve
author_association: MEMBER
user: login: baude
created_at: 2025-03-07T16:02:21Z
body: thank you for the submission @danegsta 
author_association: MEMBER
user: login: danegsta
created_at: 2025-03-07T18:55:51Z
body: I've amended the commit message to remove the messages from the individual squashed commits (and added the fixed issue reference).
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-03-08T11:12:14Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/containers/podman/pull/25485#" title="Author self-approved">danegsta</a>*, *<a href="https://github.com/containers/podman/pull/25485#pullrequestreview-2669115173" title="LGTM">Luap99</a>*, *<a href="https://github.com/containers/podman/pull/25485#issuecomment-2706488200" title="Approved">mheon</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=containers%2Fpodman).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/containers/podman/blob/main/OWNERS)~~ [Luap99,mheon]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR