podman cp: evaluate symlink correctly when copying from container

When copying from a container, make sure to evaluate the symlinks
correctly.  Add tests copying a symlinked directory from a running and
a non-running container to execute both path-resolution paths.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2021-03-09 08:54:21 +01:00
parent 31b11b5cd6
commit 1f2f7e7459
2 changed files with 73 additions and 9 deletions

View File

@ -64,6 +64,13 @@ func (c *Container) stat(ctx context.Context, containerMountPoint string, contai
containerPath = "/."
}
// Wildcards are not allowed.
// TODO: it's now technically possible wildcards.
// We may consider enabling support in the future.
if strings.Contains(containerPath, "*") {
return nil, "", "", copy.ErrENOENT
}
if c.state.State == define.ContainerStateRunning {
// If the container is running, we need to join it's mount namespace
// and stat there.
@ -88,7 +95,8 @@ func (c *Container) stat(ctx context.Context, containerMountPoint string, contai
}
if statInfo.IsSymlink {
// Evaluated symlinks are always relative to the container's mount point.
// Symlinks are already evaluated and always relative to the
// container's mount point.
absContainerPath = statInfo.ImmediateTarget
} else if strings.HasPrefix(resolvedPath, containerMountPoint) {
// If the path is on the container's mount point, strip it off.
@ -143,15 +151,31 @@ func secureStat(root string, path string) (*copier.StatForItem, error) {
if len(globStats) != 1 {
return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats))
}
stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay
if !exists {
return nil, copy.ErrENOENT
if len(globStats) != 1 {
return nil, errors.Errorf("internal error: secureStat: expected 1 result but got %d", len(globStats[0].Results))
}
var statErr error
if stat.Error != "" {
statErr = errors.New(stat.Error)
// NOTE: the key in the map differ from `glob` when hitting symlink.
// Hence, we just take the first (and only) key/value pair.
for _, stat := range globStats[0].Results {
var statErr error
if stat.Error != "" {
statErr = errors.New(stat.Error)
}
// If necessary evaluate the symlink
if stat.IsSymlink {
target, err := copier.Eval(root, path, copier.EvalOptions{})
if err != nil {
return nil, errors.Wrap(err, "error evaluating symlink in container")
}
// Need to make sure the symlink is relative to the root!
target = strings.TrimPrefix(target, root)
target = filepath.Join("/", target)
stat.ImmediateTarget = target
}
return stat, statErr
}
return stat, statErr
// Nothing found!
return nil, copy.ErrENOENT
}

View File

@ -333,6 +333,46 @@ load helpers
}
@test "podman cp symlinked directory from container" {
destdir=$PODMAN_TMPDIR/cp-weird-symlink
mkdir -p $destdir
# Create 3 files with random content in the container.
local -a randomcontent=(
random-0-$(random_string 10)
random-1-$(random_string 15)
)
run_podman run -d --name cpcontainer $IMAGE sleep infinity
run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile0"
run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /tmp/containerfile1"
run_podman exec cpcontainer sh -c "mkdir /tmp/sub && cd /tmp/sub && ln -s .. weirdlink"
# Commit the image for testing non-running containers
run_podman commit -q cpcontainer
cpimage="$output"
# RUNNING container
# NOTE: /dest does not exist yet but is expected to be created during copy
run_podman cp cpcontainer:/tmp/sub/weirdlink $destdir/dest
run cat $destdir/dest/containerfile0 $destdir/dest/containerfile1
is "${lines[0]}" "${randomcontent[0]}" "eval symlink - running container"
is "${lines[1]}" "${randomcontent[1]}" "eval symlink - running container"
run_podman kill cpcontainer
run_podman rm -f cpcontainer
run rm -rf $srcdir/dest
# CREATED container
run_podman create --name cpcontainer $cpimage
run_podman cp cpcontainer:/tmp/sub/weirdlink $destdir/dest
run cat $destdir/dest/containerfile0 $destdir/dest/containerfile1
is "${lines[0]}" "${randomcontent[0]}" "eval symlink - created container"
is "${lines[1]}" "${randomcontent[1]}" "eval symlink - created container"
run_podman rm -f cpcontainer
}
@test "podman cp file from host to container volume" {
srcdir=$PODMAN_TMPDIR/cp-test-volume
mkdir -p $srcdir