mirror of
https://github.com/containers/podman.git
synced 2026-03-13 08:01:19 +08:00
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:
@@ -64,6 +64,13 @@ func (c *Container) stat(ctx context.Context, containerMountPoint string, contai
|
|||||||
containerPath = "/."
|
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 c.state.State == define.ContainerStateRunning {
|
||||||
// If the container is running, we need to join it's mount namespace
|
// If the container is running, we need to join it's mount namespace
|
||||||
// and stat there.
|
// and stat there.
|
||||||
@@ -88,7 +95,8 @@ func (c *Container) stat(ctx context.Context, containerMountPoint string, contai
|
|||||||
}
|
}
|
||||||
|
|
||||||
if statInfo.IsSymlink {
|
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
|
absContainerPath = statInfo.ImmediateTarget
|
||||||
} else if strings.HasPrefix(resolvedPath, containerMountPoint) {
|
} else if strings.HasPrefix(resolvedPath, containerMountPoint) {
|
||||||
// If the path is on the container's mount point, strip it off.
|
// 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 {
|
if len(globStats) != 1 {
|
||||||
return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats))
|
return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats))
|
||||||
}
|
}
|
||||||
|
if len(globStats) != 1 {
|
||||||
stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay
|
return nil, errors.Errorf("internal error: secureStat: expected 1 result but got %d", len(globStats[0].Results))
|
||||||
if !exists {
|
|
||||||
return nil, copy.ErrENOENT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var statErr error
|
// NOTE: the key in the map differ from `glob` when hitting symlink.
|
||||||
if stat.Error != "" {
|
// Hence, we just take the first (and only) key/value pair.
|
||||||
statErr = errors.New(stat.Error)
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" {
|
@test "podman cp file from host to container volume" {
|
||||||
srcdir=$PODMAN_TMPDIR/cp-test-volume
|
srcdir=$PODMAN_TMPDIR/cp-test-volume
|
||||||
mkdir -p $srcdir
|
mkdir -p $srcdir
|
||||||
|
|||||||
Reference in New Issue
Block a user