summaryrefslogtreecommitdiff
path: root/libpod/container_copy_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/container_copy_linux.go')
-rw-r--r--libpod/container_copy_linux.go222
1 files changed, 10 insertions, 212 deletions
diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go
index 557fead1e..3b029f08f 100644
--- a/libpod/container_copy_linux.go
+++ b/libpod/container_copy_linux.go
@@ -1,226 +1,14 @@
-//go:build linux
-// +build linux
-
package libpod
import (
- "errors"
"fmt"
- "io"
"os"
- "path/filepath"
"runtime"
- "strings"
- buildahCopiah "github.com/containers/buildah/copier"
- "github.com/containers/buildah/pkg/chrootuser"
- "github.com/containers/buildah/util"
"github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/pkg/rootless"
- "github.com/containers/storage/pkg/archive"
- "github.com/containers/storage/pkg/idtools"
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
-func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir bool, rename map[string]string, reader io.Reader) (func() error, error) {
- var (
- mountPoint string
- resolvedRoot string
- resolvedPath string
- unmount func()
- err error
- )
-
- // Make sure that "/" copies the *contents* of the mount point and not
- // the directory.
- if path == "/" {
- path = "/."
- }
-
- // Optimization: only mount if the container is not already.
- if c.state.Mounted {
- mountPoint = c.state.Mountpoint
- unmount = func() {}
- } else {
- // NOTE: make sure to unmount in error paths.
- mountPoint, err = c.mount()
- if err != nil {
- return nil, err
- }
- unmount = func() {
- if err := c.unmount(false); err != nil {
- logrus.Errorf("Failed to unmount container: %v", err)
- }
- }
- }
-
- if c.state.State == define.ContainerStateRunning {
- resolvedRoot = "/"
- resolvedPath = c.pathAbs(path)
- } else {
- resolvedRoot, resolvedPath, err = c.resolvePath(mountPoint, path)
- if err != nil {
- unmount()
- return nil, err
- }
- }
-
- var idPair *idtools.IDPair
- if chown {
- // Make sure we chown the files to the container's main user and group ID.
- user, err := getContainerUser(c, mountPoint)
- if err != nil {
- unmount()
- return nil, err
- }
- idPair = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
- }
-
- decompressed, err := archive.DecompressStream(reader)
- if err != nil {
- unmount()
- return nil, err
- }
-
- logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID())
-
- return func() error {
- defer unmount()
- defer decompressed.Close()
- putOptions := buildahCopiah.PutOptions{
- UIDMap: c.config.IDMappings.UIDMap,
- GIDMap: c.config.IDMappings.GIDMap,
- ChownDirs: idPair,
- ChownFiles: idPair,
- NoOverwriteDirNonDir: noOverwriteDirNonDir,
- NoOverwriteNonDirDir: noOverwriteDirNonDir,
- Rename: rename,
- }
-
- return c.joinMountAndExec(
- func() error {
- return buildahCopiah.Put(resolvedRoot, resolvedPath, putOptions, decompressed)
- },
- )
- }, nil
-}
-
-func (c *Container) copyToArchive(path string, writer io.Writer) (func() error, error) {
- var (
- mountPoint string
- unmount func()
- err error
- )
-
- // Optimization: only mount if the container is not already.
- if c.state.Mounted {
- mountPoint = c.state.Mountpoint
- unmount = func() {}
- } else {
- // NOTE: make sure to unmount in error paths.
- mountPoint, err = c.mount()
- if err != nil {
- return nil, err
- }
- unmount = func() {
- if err := c.unmount(false); err != nil {
- logrus.Errorf("Failed to unmount container: %v", err)
- }
- }
- }
-
- statInfo, resolvedRoot, resolvedPath, err := c.stat(mountPoint, path)
- if err != nil {
- unmount()
- return nil, err
- }
-
- // We optimistically chown to the host user. In case of a hypothetical
- // container-to-container copy, the reading side will chown back to the
- // container user.
- user, err := getContainerUser(c, mountPoint)
- if err != nil {
- unmount()
- return nil, err
- }
- hostUID, hostGID, err := util.GetHostIDs(
- idtoolsToRuntimeSpec(c.config.IDMappings.UIDMap),
- idtoolsToRuntimeSpec(c.config.IDMappings.GIDMap),
- user.UID,
- user.GID,
- )
- if err != nil {
- unmount()
- return nil, err
- }
- idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
-
- logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID())
-
- return func() error {
- defer unmount()
- getOptions := buildahCopiah.GetOptions{
- // Unless the specified points to ".", we want to copy the base directory.
- KeepDirectoryNames: statInfo.IsDir && filepath.Base(path) != ".",
- UIDMap: c.config.IDMappings.UIDMap,
- GIDMap: c.config.IDMappings.GIDMap,
- ChownDirs: &idPair,
- ChownFiles: &idPair,
- Excludes: []string{"dev", "proc", "sys"},
- // Ignore EPERMs when copying from rootless containers
- // since we cannot read TTY devices. Those are owned
- // by the host's root and hence "nobody" inside the
- // container's user namespace.
- IgnoreUnreadable: rootless.IsRootless() && c.state.State == define.ContainerStateRunning,
- }
- return c.joinMountAndExec(
- func() error {
- return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedPath}, writer)
- },
- )
- }, nil
-}
-
-// getContainerUser returns the specs.User and ID mappings of the container.
-func getContainerUser(container *Container, mountPoint string) (specs.User, error) {
- userspec := container.config.User
-
- uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec)
- u := specs.User{
- UID: uid,
- GID: gid,
- Username: userspec,
- }
-
- if !strings.Contains(userspec, ":") {
- groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
- if err2 != nil {
- if !errors.Is(err2, chrootuser.ErrNoSuchUser) && err == nil {
- err = err2
- }
- } else {
- u.AdditionalGids = groups
- }
- }
-
- return u, err
-}
-
-// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec.
-func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) {
- for _, idmap := range idMaps {
- tempIDMap := specs.LinuxIDMapping{
- ContainerID: uint32(idmap.ContainerID),
- HostID: uint32(idmap.HostID),
- Size: uint32(idmap.Size),
- }
- convertedIDMap = append(convertedIDMap, tempIDMap)
- }
- return convertedIDMap
-}
-
// 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.
@@ -288,3 +76,13 @@ func (c *Container) joinMountAndExec(f func() error) error {
}()
return <-errChan
}
+
+func (c *Container) resolveCopyTarget(mountPoint string, containerPath string) (string, string, 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 "/", c.pathAbs(containerPath), nil
+ }
+ return c.resolvePath(mountPoint, containerPath)
+}