Skip to content

Commit

Permalink
Fix problem copying files when container is in host pid namespace
Browse files Browse the repository at this point in the history
When attempting to copy files into and out of running containers
within the host pidnamespace, the code was attempting to join the
host pidns again, and getting an error. This was causing the podman
cp command to fail. Since we are already in the host pid namespace,
we should not be attempting to join.  This PR adds a check to see if
the container is in NOT host pid namespace, and only then attempts to
join.

Fixes: containers#9985

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed May 19, 2021
1 parent 18efc5a commit bc0e12a
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 7 deletions.
25 changes: 18 additions & 7 deletions libpod/container_copy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,32 @@ func (c *Container) joinMountAndExec(ctx context.Context, f func() error) error
}
defer mountFD.Close()

pidFD, err := getFD(PIDNS)
inHostPidNS, err := c.inHostPidNS()
if err != nil {
errChan <- err
errChan <- errors.Wrap(err, "checking inHostPidNS")
return
}
defer pidFD.Close()
if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
errChan <- err
return
var pidFD *os.File
if !inHostPidNS {
pidFD, err = getFD(PIDNS)
if err != nil {
errChan <- err
return
}
defer pidFD.Close()
}
if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil {

if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
errChan <- err
return
}

if pidFD != nil {
if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil {
errChan <- err
return
}
}
if err := unix.Setns(int(mountFD.Fd()), unix.CLONE_NEWNS); err != nil {
errChan <- err
return
Expand Down
23 changes: 23 additions & 0 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -892,3 +892,26 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named

return hostConfig, nil
}

// Return true if the container is running in the host's PID NS.
func (c *Container) inHostPidNS() (bool, error) {
if c.config.PIDNsCtr != "" {
return false, nil
}
ctrSpec, err := c.specFromState()
if err != nil {
return false, err
}
if ctrSpec.Linux != nil {
// Locate the spec's PID namespace.
// If there is none, it's pid=host.
// If there is one and it has a path, it's "ns:".
// If there is no path, it's default - the empty string.
for _, ns := range ctrSpec.Linux.Namespaces {
if ns.Type == spec.PIDNamespace {
return false, nil
}
}
}
return true, nil
}
44 changes: 44 additions & 0 deletions test/e2e/cp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,50 @@ var _ = Describe("Podman cp", func() {
Expect(roundtripContent).To(Equal(originalContent))
})

// Copy a file to the container, then back to the host in --pid=host
It("podman cp --pid=host file", func() {
SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1")
srcFile, err := ioutil.TempFile("", "")
Expect(err).To(BeNil())
defer srcFile.Close()
defer os.Remove(srcFile.Name())

originalContent := []byte("podman cp file test")
err = ioutil.WriteFile(srcFile.Name(), originalContent, 0644)
Expect(err).To(BeNil())

// Create a container. NOTE that container mustn't be running for copying.
session := podmanTest.Podman([]string{"create", "--pid=host", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
name := session.OutputToString()

session = podmanTest.Podman([]string{"start", name})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

// The file will now be created (and written to).
session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

// Copy FROM the container.

destFile, err := ioutil.TempFile("", "")
Expect(err).To(BeNil())
defer destFile.Close()
defer os.Remove(destFile.Name())

session = podmanTest.Podman([]string{"cp", name + ":foo", destFile.Name()})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

// Now make sure the content matches.
roundtripContent, err := ioutil.ReadFile(destFile.Name())
Expect(err).To(BeNil())
Expect(roundtripContent).To(Equal(originalContent))
})

// Create a symlink in the container, use it as a copy destination and
// make sure that the link and the resolved path are accessible and
// give the right content.
Expand Down
16 changes: 16 additions & 0 deletions test/system/065-cp.bats
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ load helpers
}


@test "podman cp file from/to host while --pid=host" {
if is_rootless && ! is_cgroupsv2; then
skip "'podman cp --pid=host' (rootless) only works with cgroups v2"
fi

srcdir=$PODMAN_TMPDIR/cp-pid-equals-host
mkdir -p $srcdir
touch $srcdir/hostfile

run_podman run --pid=host -d --name cpcontainer $IMAGE sleep infinity
run_podman cp $srcdir/hostfile cpcontainer:/tmp/hostfile
run_podman cp cpcontainer:/tmp/hostfile $srcdir/hostfile1
run_podman kill cpcontainer
run_podman rm -f cpcontainer
}

@test "podman cp file from container to host" {
srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host
mkdir -p $srcdir
Expand Down

0 comments on commit bc0e12a

Please sign in to comment.