summaryrefslogtreecommitdiff
path: root/pkg/domain/infra/abi/archive.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain/infra/abi/archive.go')
-rw-r--r--pkg/domain/infra/abi/archive.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go
new file mode 100644
index 000000000..809813756
--- /dev/null
+++ b/pkg/domain/infra/abi/archive.go
@@ -0,0 +1,172 @@
+package abi
+
+import (
+ "context"
+ "io"
+ "strings"
+
+ buildahCopiah "github.com/containers/buildah/copier"
+ "github.com/containers/buildah/pkg/chrootuser"
+ "github.com/containers/buildah/util"
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// NOTE: Only the parent directory of the container path must exist. The path
+// itself may be created while copying.
+func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) {
+ container, err := ic.Libpod.LookupContainer(nameOrID)
+ if err != nil {
+ return nil, err
+ }
+
+ unmount := func() {
+ if err := container.Unmount(false); err != nil {
+ logrus.Errorf("Error unmounting container: %v", err)
+ }
+ }
+
+ _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ decompressed, err := archive.DecompressStream(reader)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID())
+
+ return func() error {
+ defer unmount()
+ defer decompressed.Close()
+ putOptions := buildahCopiah.PutOptions{
+ UIDMap: idMappings.UIDMap,
+ GIDMap: idMappings.GIDMap,
+ ChownDirs: idPair,
+ ChownFiles: idPair,
+ }
+ return buildahCopiah.Put(resolvedRoot, resolvedContainerPath, putOptions, decompressed)
+ }, nil
+}
+
+func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) {
+ container, err := ic.Libpod.LookupContainer(nameOrID)
+ if err != nil {
+ return nil, err
+ }
+
+ unmount := func() {
+ if err := container.Unmount(false); err != nil {
+ logrus.Errorf("Error unmounting container: %v", err)
+ }
+ }
+
+ // Make sure that "/" copies the *contents* of the mount point and not
+ // the directory.
+ if containerPath == "/" {
+ containerPath = "/."
+ }
+
+ _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID())
+
+ return func() error {
+ defer container.Unmount(false)
+ getOptions := buildahCopiah.GetOptions{
+ // Unless the specified path ends with ".", we want to copy the base directory.
+ KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."),
+ UIDMap: idMappings.UIDMap,
+ GIDMap: idMappings.GIDMap,
+ ChownDirs: idPair,
+ ChownFiles: idPair,
+ }
+ return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedContainerPath}, writer)
+ }, nil
+}
+
+// getIDMappingsAndPair returns the ID mappings for the container and the host
+// ID pair.
+func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) {
+ user, err := getContainerUser(container, containerMount)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ idMappingOpts, err := container.IDMappings()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
+ return &idMappingOpts, &idPair, nil
+}
+
+// getContainerUser returns the specs.User of the container.
+func getContainerUser(container *libpod.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.Cause(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
+}