Files
podman/libpod/container_copy_linux.go
Pooyan dd2f16e89b libpod: copy from image mount subpaths in created containers
Copies involving a non-running container resolve mounts on the host. Image
mounts were missing from that resolution, so copying from an image subpath
looked in the container rootfs and failed.

Resolve image mounts and their subpaths, including symlinks that point to
other container paths. Reject copies into an image mount while its writable
overlay is absent. Also honor read-only named volumes and bind mounts when
copying into their host paths.

Cover created, stopped, and exited containers in system tests, and test
image-mount symlink resolution separately.

Fixes: #29857
Signed-off-by: pooyanazad <pooyan.azadparvar@gmail.com>
Signed-off-by: Pooyan <pooyan.azadparvar@gmail.com>
2026-10-06 13:33:03 +02:00

92 lines
2.1 KiB
Go

//go:build !remote
package libpod
import (
"context"
"fmt"
"os"
"runtime"
"go.podman.io/podman/v6/libpod/define"
"golang.org/x/sys/unix"
)
// joinMountAndExec executes the specified function `f` inside the container's
// mount and PID namespace. That allows for having the exact view on the
// container's file system.
//
// Note, if the container is not running `f()` will be executed as is.
func (c *Container) joinMountAndExec(f func() error) error {
if c.state.State != define.ContainerStateRunning {
return f()
}
// Container's running, so we need to execute `f()` inside its mount NS.
errChan := make(chan error)
go func() {
runtime.LockOSThread()
// Join the mount and PID NS of the container.
getFD := func(ns LinuxNS) (*os.File, error) {
nsPath, err := c.namespacePath(ns)
if err != nil {
return nil, err
}
return os.Open(nsPath)
}
mountFD, err := getFD(MountNS)
if err != nil {
errChan <- err
return
}
defer mountFD.Close()
inHostPidNS, err := c.inHostPidNS()
if err != nil {
errChan <- fmt.Errorf("checking inHostPidNS: %w", err)
return
}
var pidFD *os.File
if !inHostPidNS {
pidFD, err = getFD(PIDNS)
if err != nil {
errChan <- err
return
}
defer pidFD.Close()
}
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
}
// Last but not least, execute the workload.
errChan <- f()
}()
return <-errChan
}
func (c *Container) resolveCopyTarget(ctx context.Context, mountPoint string, containerPath string) (pathResolution, error) {
// If the container is running, we will execute the copy
// inside the container's mount namespace so we return a path
// relative to the container's root.
if c.state.State == define.ContainerStateRunning {
return pathResolution{root: "/", path: c.pathAbs(containerPath)}, nil
}
return c.resolvePath(ctx, mountPoint, containerPath)
}