Skip to content

Commit

Permalink
podman cp: evaluate symlink correctly when copying from container
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
vrothberg committed Mar 9, 2021
1 parent 31b11b5 commit 1f2f7e7
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
42 changes: 33 additions & 9 deletions libpod/container_stat_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
40 changes: 40 additions & 0 deletions test/system/065-cp.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1f2f7e7

Please sign in to comment.