mirror of
https://github.com/containers/podman.git
synced 2025-05-18 15:47:51 +08:00

This should never be pulled into the remote client. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
215 lines
5.8 KiB
Go
215 lines
5.8 KiB
Go
//go:build !remote && (linux || freebsd)
|
|
// +build !remote
|
|
// +build linux freebsd
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"path/filepath"
|
|
"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"
|
|
)
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
resolvedRoot, resolvedPath, err = c.resolveCopyTarget(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
|
|
}
|