diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index 9b0a01a2f5..e0161824f6 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -1,19 +1,34 @@
 package containers
 
 import (
+	"io"
+	"io/ioutil"
+	"os"
+	"os/user"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	buildahCopiah "github.com/containers/buildah/copier"
 	"github.com/containers/podman/v2/cmd/podman/common"
 	"github.com/containers/podman/v2/cmd/podman/registry"
+	"github.com/containers/podman/v2/pkg/copy"
 	"github.com/containers/podman/v2/pkg/domain/entities"
+	"github.com/containers/podman/v2/pkg/errorhandling"
+	"github.com/containers/storage/pkg/archive"
+	"github.com/containers/storage/pkg/idtools"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 )
 
 var (
-	cpDescription = `Command copies the contents of SRC_PATH to the DEST_PATH.
+	cpDescription = `Copy the contents of SRC_PATH to the DEST_PATH.
 
-  You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container.  The SRC_PATH or DEST_PATH can be a file or directory.
+  You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or a directory.
 `
 	cpCommand = &cobra.Command{
-		Use:               "cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
+		Use:               "cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
 		Short:             "Copy files/folders between a container and the local filesystem",
 		Long:              cpDescription,
 		Args:              cobra.ExactArgs(2),
@@ -39,19 +54,21 @@ var (
 
 func cpFlags(cmd *cobra.Command) {
 	flags := cmd.Flags()
-	flags.BoolVar(&cpOpts.Extract, "extract", false, "Extract the tar file into the destination directory.")
-	flags.BoolVar(&cpOpts.Pause, "pause", true, "Pause the container while copying")
+	flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...")
+	flags.BoolVar(&cpOpts.Pause, "pause", true, "Deorecated")
+	_ = flags.MarkHidden("extract")
+	_ = flags.MarkHidden("pause")
 }
 
 func init() {
 	registry.Commands = append(registry.Commands, registry.CliCommand{
-		Mode:    []entities.EngineMode{entities.ABIMode},
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
 		Command: cpCommand,
 	})
 	cpFlags(cpCommand)
 
 	registry.Commands = append(registry.Commands, registry.CliCommand{
-		Mode:    []entities.EngineMode{entities.ABIMode},
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
 		Command: containerCpCommand,
 		Parent:  containerCmd,
 	})
@@ -59,5 +76,290 @@ func init() {
 }
 
 func cp(cmd *cobra.Command, args []string) error {
-	return registry.ContainerEngine().ContainerCp(registry.GetContext(), args[0], args[1], cpOpts)
+	// Parse user input.
+	sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(args[0], args[1])
+	if err != nil {
+		return err
+	}
+
+	if len(sourceContainerStr) > 0 {
+		return copyFromContainer(sourceContainerStr, sourcePath, destPath)
+	}
+
+	return copyToContainer(destContainerStr, destPath, sourcePath)
+}
+
+// containerMustExist returns an error if the specified container does not
+// exist.
+func containerMustExist(container string) error {
+	exists, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), container, entities.ContainerExistsOptions{})
+	if err != nil {
+		return err
+	}
+	if !exists.Value {
+		return errors.Errorf("container %q does not exist", container)
+	}
+	return nil
+}
+
+// doCopy executes the two functions in parallel to copy data from A to B and
+// joins the errors if any.
+func doCopy(funcA func() error, funcB func() error) error {
+	errChan := make(chan error)
+	go func() {
+		errChan <- funcA()
+	}()
+	var copyErrors []error
+	copyErrors = append(copyErrors, funcB())
+	copyErrors = append(copyErrors, <-errChan)
+	return errorhandling.JoinErrors(copyErrors)
+}
+
+// copyFromContainer copies from the containerPath on the container to hostPath.
+func copyFromContainer(container string, containerPath string, hostPath string) error {
+	if err := containerMustExist(container); err != nil {
+		return err
+	}
+
+	if hostPath == "-" {
+		hostPath = os.Stdout.Name()
+	}
+
+	containerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
+	if err != nil {
+		return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+	}
+
+	var hostBaseName string
+	hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath)
+	if hostInfoErr != nil {
+		if strings.HasSuffix(hostPath, "/") {
+			return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+		}
+		// If it doesn't exist, then let's have a look at the parent dir.
+		parentDir := filepath.Dir(hostPath)
+		hostInfo, err = copy.ResolveHostPath(parentDir)
+		if err != nil {
+			return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+		}
+		// If the specified path does not exist, we need to assume that
+		// it'll be created while copying.  Hence, we use it as the
+		// base path.
+		hostBaseName = filepath.Base(hostPath)
+	} else {
+		// If the specified path exists on the host, we must use its
+		// base path as it may have changed due to symlink evaluations.
+		hostBaseName = filepath.Base(hostInfo.LinkTarget)
+	}
+
+	reader, writer := io.Pipe()
+	hostCopy := func() error {
+		defer reader.Close()
+		if hostInfo.LinkTarget == os.Stdout.Name() {
+			_, err := io.Copy(os.Stdout, reader)
+			return err
+		}
+
+		groot, err := user.Current()
+		if err != nil {
+			return err
+		}
+
+		// Set the {G,U}ID.  Let's be tolerant towards the different
+		// operating systems and only log the errors, so we can debug
+		// if necessary.
+		idPair := idtools.IDPair{}
+		if i, err := strconv.Atoi(groot.Uid); err == nil {
+			idPair.UID = i
+		} else {
+			logrus.Debugf("Error converting UID %q to int: %v", groot.Uid, err)
+		}
+		if i, err := strconv.Atoi(groot.Gid); err == nil {
+			idPair.GID = i
+		} else {
+			logrus.Debugf("Error converting GID %q to int: %v", groot.Gid, err)
+		}
+
+		putOptions := buildahCopiah.PutOptions{
+			ChownDirs:  &idPair,
+			ChownFiles: &idPair,
+		}
+		if !containerInfo.IsDir && (!hostInfo.IsDir || hostInfoErr != nil) {
+			// If we're having a file-to-file copy, make sure to
+			// rename accordingly.
+			putOptions.Rename = map[string]string{filepath.Base(containerInfo.LinkTarget): hostBaseName}
+		}
+		dir := hostInfo.LinkTarget
+		if !hostInfo.IsDir {
+			dir = filepath.Dir(dir)
+		}
+		if err := buildahCopiah.Put(dir, "", putOptions, reader); err != nil {
+			return errors.Wrap(err, "error copying to host")
+		}
+		return nil
+	}
+
+	containerCopy := func() error {
+		defer writer.Close()
+		copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), container, containerInfo.LinkTarget, writer)
+		if err != nil {
+			return err
+		}
+		if err := copyFunc(); err != nil {
+			return errors.Wrap(err, "error copying from container")
+		}
+		return nil
+	}
+	return doCopy(containerCopy, hostCopy)
+}
+
+// copyToContainer copies the hostPath to containerPath on the container.
+func copyToContainer(container string, containerPath string, hostPath string) error {
+	if err := containerMustExist(container); err != nil {
+		return err
+	}
+
+	isStdin := false
+	if hostPath == "-" {
+		hostPath = os.Stdin.Name()
+		isStdin = true
+	} else if hostPath == os.Stdin.Name() {
+		isStdin = true
+	}
+
+	// Make sure that host path exists.
+	hostInfo, err := copy.ResolveHostPath(hostPath)
+	if err != nil {
+		return errors.Wrapf(err, "%q could not be found on the host", hostPath)
+	}
+
+	// If the path on the container does not exist.  We need to make sure
+	// that it's parent directory exists.  The destination may be created
+	// while copying.
+	var containerBaseName string
+	containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
+	if containerInfoErr != nil {
+		if strings.HasSuffix(containerPath, "/") {
+			return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container)
+		}
+		if isStdin {
+			return errors.New("destination must be a directory when copying from stdin")
+		}
+		// NOTE: containerInfo may actually be set.  That happens when
+		// the container path is a symlink into nirvana.  In that case,
+		// we must use the symlinked path instead.
+		path := containerPath
+		if containerInfo != nil {
+			containerBaseName = filepath.Base(containerInfo.LinkTarget)
+			path = containerInfo.LinkTarget
+		} else {
+			containerBaseName = filepath.Base(containerPath)
+		}
+
+		parentDir, err := containerParentDir(container, path)
+		if err != nil {
+			return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
+		}
+		containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
+		if err != nil {
+			return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+		}
+	} else {
+		// If the specified path exists on the container, we must use
+		// its base path as it may have changed due to symlink
+		// evaluations.
+		containerBaseName = filepath.Base(containerInfo.LinkTarget)
+	}
+
+	var stdinFile string
+	if isStdin {
+		if !containerInfo.IsDir {
+			return errors.New("destination must be a directory when copying from stdin")
+		}
+
+		// Copy from stdin to a temporary file *before* throwing it
+		// over the wire.  This allows for proper client-side error
+		// reporting.
+		tmpFile, err := ioutil.TempFile("", "")
+		if err != nil {
+			return err
+		}
+		_, err = io.Copy(tmpFile, os.Stdin)
+		if err != nil {
+			return err
+		}
+		if err = tmpFile.Close(); err != nil {
+			return err
+		}
+		if !archive.IsArchivePath(tmpFile.Name()) {
+			return errors.New("source must be a (compressed) tar archive when copying from stdin")
+		}
+		stdinFile = tmpFile.Name()
+	}
+
+	reader, writer := io.Pipe()
+	hostCopy := func() error {
+		defer writer.Close()
+		if isStdin {
+			stream, err := os.Open(stdinFile)
+			if err != nil {
+				return err
+			}
+			defer stream.Close()
+			_, err = io.Copy(writer, stream)
+			return err
+		}
+
+		getOptions := buildahCopiah.GetOptions{
+			// Unless the specified path ends with ".", we want to copy the base directory.
+			KeepDirectoryNames: !strings.HasSuffix(hostPath, "."),
+		}
+		if !hostInfo.IsDir && (!containerInfo.IsDir || containerInfoErr != nil) {
+			// If we're having a file-to-file copy, make sure to
+			// rename accordingly.
+			getOptions.Rename = map[string]string{filepath.Base(hostInfo.LinkTarget): containerBaseName}
+		}
+		if err := buildahCopiah.Get("/", "", getOptions, []string{hostInfo.LinkTarget}, writer); err != nil {
+			return errors.Wrap(err, "error copying from host")
+		}
+		return nil
+	}
+
+	containerCopy := func() error {
+		defer reader.Close()
+		target := containerInfo.FileInfo.LinkTarget
+		if !containerInfo.IsDir {
+			target = filepath.Dir(target)
+		}
+
+		copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader)
+		if err != nil {
+			return err
+		}
+		if err := copyFunc(); err != nil {
+			return errors.Wrap(err, "error copying to container")
+		}
+		return nil
+	}
+
+	return doCopy(hostCopy, containerCopy)
+}
+
+// containerParentDir returns the parent directory of the specified path on the
+// container.  If the path is relative, it will be resolved relative to the
+// container's working directory (or "/" if the work dir isn't set).
+func containerParentDir(container string, containerPath string) (string, error) {
+	if filepath.IsAbs(containerPath) {
+		return filepath.Dir(containerPath), nil
+	}
+	inspectData, _, err := registry.ContainerEngine().ContainerInspect(registry.GetContext(), []string{container}, entities.InspectOptions{})
+	if err != nil {
+		return "", err
+	}
+	if len(inspectData) != 1 {
+		return "", errors.Errorf("inspecting container %q: expected 1 data item but got %d", container, len(inspectData))
+	}
+	workDir := filepath.Join("/", inspectData[0].Config.WorkingDir)
+	workDir = filepath.Join(workDir, containerPath)
+	return filepath.Dir(workDir), nil
 }
diff --git a/docs/source/markdown/podman-cp.1.md b/docs/source/markdown/podman-cp.1.md
index 241a74c426..56511c2446 100644
--- a/docs/source/markdown/podman-cp.1.md
+++ b/docs/source/markdown/podman-cp.1.md
@@ -4,9 +4,9 @@
 podman\-cp - Copy files/folders between a container and the local filesystem
 
 ## SYNOPSIS
-**podman cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path*
+**podman cp** [*container*:]*src_path* [*container*:]*dest_path*
 
-**podman container cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path*
+**podman container cp** [*container*:]*src_path* [*container*:]*dest_path*
 
 ## DESCRIPTION
 Copy the contents of **src_path** to the **dest_path**. You can copy from the container's filesystem to the local machine or the reverse, from the local filesystem to the container.
@@ -59,14 +59,6 @@ Using `-` as the *src_path* streams the contents of STDIN as a tar archive. The
 
 ## OPTIONS
 
-#### **--extract**
-
-If the source is a tar archive, extract it to the provided destination (must be a directory).  If the source is not a tar archive, follow the above rules.
-
-#### **--pause**
-
-Pause the container while copying into it to avoid potential security issues around symlinks. Defaults to *true*. On rootless containers with cgroups V1, defaults to false.
-
 ## ALTERNATIVES
 
 Podman has much stronger capabilities than just `podman cp` to achieve copy files between host and container.
@@ -112,8 +104,6 @@ podman cp containerID:/myapp/ /myapp/
 
 podman cp containerID:/home/myuser/. /home/myuser/
 
-podman cp --extract /home/myuser/myfiles.tar.gz containerID:/myfiles
-
 podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz
 
 ## SEE ALSO
diff --git a/go.mod b/go.mod
index a208c244b7..d7c4e8cb69 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
 	github.com/containernetworking/cni v0.8.0
 	github.com/containernetworking/plugins v0.9.0
-	github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c
+	github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c
 	github.com/containers/common v0.31.1
 	github.com/containers/conmon v2.0.20+incompatible
 	github.com/containers/image/v5 v5.9.0
diff --git a/go.sum b/go.sum
index 0156c2e80b..d2561085cd 100644
--- a/go.sum
+++ b/go.sum
@@ -93,9 +93,9 @@ github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urY
 github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0=
 github.com/containernetworking/plugins v0.9.0 h1:c+1gegKhR7+d0Caum9pEHugZlyhXPOG6v3V6xJgIGCI=
 github.com/containernetworking/plugins v0.9.0/go.mod h1:dbWv4dI0QrBGuVgj+TuVQ6wJRZVOhrCQj91YyC92sxg=
-github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c h1:vyc2iYz9b2vfDiigpLyhiXNqXITt/dmDk74HpHzlQow=
-github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c/go.mod h1:B+0OkXUogxdwsEy4ax3a5/vDtJjL6vCisiV6frQZJ4A=
-github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
+github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c h1:DnJiPjBKeoZbzjkUA6YMf/r5ShYpNacK+EcQ/ui1Mxo=
+github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c/go.mod h1:hvIoL3urgYPL0zX8XlK05aWP6qfUnBNqTrsedsYw6OY=
+github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
 github.com/containers/common v0.31.1 h1:oBINnZpYZ2u90HPMnVCXOhm/TsTaTB7wU/56l05hq44=
 github.com/containers/common v0.31.1/go.mod h1:Fehe82hQfJQvDspnRrV9rcdAWG3IalNHEt0F6QWNBHQ=
 github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
@@ -110,7 +110,6 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ
 github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA=
 github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
 github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI=
-github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc=
 github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
 github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E=
 github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
@@ -441,7 +440,6 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200817204227-f9c09b4ea1df/go.m
 github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
 github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
 github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
-github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY=
 github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
 github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
 github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
@@ -557,16 +555,13 @@ github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpc
 github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ=
 github.com/vbauerster/mpb/v5 v5.3.0/go.mod h1:4yTkvAb8Cm4eylAp6t0JRq6pXDkFJ4krUlDqWYkakAs=
 github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
-github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
 github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
 github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
-github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
 github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
-github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
 github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
 github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
@@ -706,7 +701,6 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM=
 golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go
index d8197415cd..083c72ce84 100644
--- a/pkg/api/handlers/compat/containers_archive.go
+++ b/pkg/api/handlers/compat/containers_archive.go
@@ -3,11 +3,13 @@ package compat
 import (
 	"fmt"
 	"net/http"
+	"os"
 
 	"github.com/containers/podman/v2/libpod"
 	"github.com/containers/podman/v2/libpod/define"
 	"github.com/containers/podman/v2/pkg/api/handlers/utils"
 	"github.com/containers/podman/v2/pkg/copy"
+	"github.com/containers/podman/v2/pkg/domain/infra/abi"
 	"github.com/gorilla/schema"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -44,58 +46,47 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De
 	}
 
 	containerName := utils.GetName(r)
+	containerEngine := abi.ContainerEngine{Libpod: runtime}
+	statReport, err := containerEngine.ContainerStat(r.Context(), containerName, query.Path)
 
-	ctr, err := runtime.LookupContainer(containerName)
-	if errors.Cause(err) == define.ErrNoSuchCtr {
-		utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists"))
+	// NOTE
+	// The statReport may actually be set even in case of an error.  That's
+	// the case when we're looking at a symlink pointing to nirvana.  In
+	// such cases, we really need the FileInfo but we also need the error.
+	if statReport != nil {
+		statHeader, err := copy.EncodeFileInfo(&statReport.FileInfo)
+		if err != nil {
+			utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+			return
+		}
+		w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader)
+	}
+
+	if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == copy.ENOENT {
+		// 404 is returned for an absent container and path.  The
+		// clients must deal with it accordingly.
+		utils.Error(w, "Not found.", http.StatusNotFound, err)
 		return
 	} else if err != nil {
 		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
 		return
 	}
 
-	source, err := copy.CopyItemForContainer(ctr, query.Path, true, true)
-	defer source.CleanUp()
-	if err != nil {
-		utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path))
-		return
-	}
-
-	// NOTE: Docker always sets the header.
-	info, err := source.Stat()
-	if err != nil {
-		utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path))
-		return
-	}
-	statHeader, err := copy.EncodeFileInfo(info)
-	if err != nil {
-		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
-		return
-	}
-	w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader)
-
 	// Our work is done when the user is interested in the header only.
 	if r.Method == http.MethodHead {
 		w.WriteHeader(http.StatusOK)
 		return
 	}
 
-	// Alright, the users wants data from the container.
-	destination, err := copy.CopyItemForWriter(w)
-	if err != nil {
-		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
-		return
-	}
-
-	copier, err := copy.GetCopier(&source, &destination, false)
+	copyFunc, err := containerEngine.ContainerCopyToArchive(r.Context(), containerName, query.Path, w)
 	if err != nil {
 		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
 		return
 	}
+	w.Header().Set("Content-Type", "application/x-tar")
 	w.WriteHeader(http.StatusOK)
-	if err := copier.Copy(); err != nil {
-		logrus.Errorf("Error during copy: %v", err)
-		return
+	if err := copyFunc(); err != nil {
+		logrus.Error(err.Error())
 	}
 }
 
@@ -113,36 +104,22 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder,
 		return
 	}
 
-	ctrName := utils.GetName(r)
+	containerName := utils.GetName(r)
+	containerEngine := abi.ContainerEngine{Libpod: runtime}
 
-	ctr, err := runtime.LookupContainer(ctrName)
-	if err != nil {
-		utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName))
+	copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body)
+	if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) {
+		// 404 is returned for an absent container and path.  The
+		// clients must deal with it accordingly.
+		utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists"))
+		return
+	} else if err != nil {
+		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
 		return
 	}
 
-	destination, err := copy.CopyItemForContainer(ctr, query.Path, true, false)
-	defer destination.CleanUp()
-	if err != nil {
-		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
-		return
-	}
-
-	source, err := copy.CopyItemForReader(r.Body)
-	defer source.CleanUp()
-	if err != nil {
-		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
-		return
-	}
-
-	copier, err := copy.GetCopier(&source, &destination, false)
-	if err != nil {
-		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
-		return
-	}
 	w.WriteHeader(http.StatusOK)
-	if err := copier.Copy(); err != nil {
-		logrus.Errorf("Error during copy: %v", err)
-		return
+	if err := copyFunc(); err != nil {
+		logrus.Error(err.Error())
 	}
 }
diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go
index b2d2543c40..9ff0ad0eb6 100644
--- a/pkg/api/server/register_archive.go
+++ b/pkg/api/server/register_archive.go
@@ -4,7 +4,6 @@ import (
 	"net/http"
 
 	"github.com/containers/podman/v2/pkg/api/handlers/compat"
-	"github.com/containers/podman/v2/pkg/api/handlers/libpod"
 	"github.com/gorilla/mux"
 )
 
@@ -92,7 +91,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
 		Libpod
 	*/
 
-	// swagger:operation POST /libpod/containers/{name}/copy libpod libpodPutArchive
+	// swagger:operation POST /libpod/containers/{name}/archive libpod libpodPutArchive
 	// ---
 	//  summary: Copy files into a container
 	//  description: Copy a tar archive of files into a container
@@ -133,7 +132,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
 	//    500:
 	//      $ref: "#/responses/InternalError"
 
-	// swagger:operation GET /libpod/containers/{name}/copy libpod libpodGetArchive
+	// swagger:operation GET /libpod/containers/{name}/archive libpod libpodGetArchive
 	// ---
 	//  summary: Copy files from a container
 	//  description: Copy a tar archive of files from a container
@@ -164,8 +163,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
 	//      $ref: "#/responses/NoSuchContainer"
 	//    500:
 	//      $ref: "#/responses/InternalError"
-	r.HandleFunc(VersionedPath("/libpod/containers/{name}/copy"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost)
-	r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost)
+	r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPut, http.MethodHead)
 
 	return nil
 }
diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go
new file mode 100644
index 0000000000..d1bbc0b95c
--- /dev/null
+++ b/pkg/bindings/containers/archive.go
@@ -0,0 +1,92 @@
+package containers
+
+import (
+	"context"
+	"io"
+	"net/http"
+	"net/url"
+
+	"github.com/containers/podman/v2/pkg/bindings"
+	"github.com/containers/podman/v2/pkg/copy"
+	"github.com/containers/podman/v2/pkg/domain/entities"
+	"github.com/pkg/errors"
+)
+
+// Stat checks if the specified path is on the container.  Note that the stat
+// report may be set even in case of an error.  This happens when the path
+// resolves to symlink pointing to a non-existent path.
+func Stat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) {
+	conn, err := bindings.GetClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	params := url.Values{}
+	params.Set("path", path)
+
+	response, err := conn.DoRequest(nil, http.MethodHead, "/containers/%s/archive", params, nil, nameOrID)
+	if err != nil {
+		return nil, err
+	}
+
+	var finalErr error
+	if response.StatusCode == http.StatusNotFound {
+		finalErr = copy.ENOENT
+	} else if response.StatusCode != http.StatusOK {
+		finalErr = errors.New(response.Status)
+	}
+
+	var statReport *entities.ContainerStatReport
+
+	fileInfo, err := copy.ExtractFileInfoFromHeader(&response.Header)
+	if err != nil && finalErr == nil {
+		return nil, err
+	}
+
+	if fileInfo != nil {
+		statReport = &entities.ContainerStatReport{FileInfo: *fileInfo}
+	}
+
+	return statReport, finalErr
+}
+
+func CopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) {
+	conn, err := bindings.GetClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	params := url.Values{}
+	params.Set("path", path)
+
+	return func() error {
+		response, err := conn.DoRequest(reader, http.MethodPut, "/containers/%s/archive", params, nil, nameOrID)
+		if err != nil {
+			return err
+		}
+		if response.StatusCode != http.StatusOK {
+			return errors.New(response.Status)
+		}
+		return response.Process(nil)
+	}, nil
+}
+
+func CopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) {
+	conn, err := bindings.GetClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	params := url.Values{}
+	params.Set("path", path)
+
+	response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/archive", params, nil, nameOrID)
+	if err != nil {
+		return nil, err
+	}
+	if response.StatusCode != http.StatusOK {
+		return nil, response.Process(nil)
+	}
+
+	return func() error {
+		_, err := io.Copy(writer, response.Body)
+		return err
+	}, nil
+}
diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go
deleted file mode 100644
index 13893deb2a..0000000000
--- a/pkg/copy/copy.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package copy
-
-import (
-	"io"
-	"os"
-	"path/filepath"
-	"strings"
-
-	buildahCopiah "github.com/containers/buildah/copier"
-	"github.com/containers/storage/pkg/archive"
-	securejoin "github.com/cyphar/filepath-securejoin"
-	"github.com/pkg/errors"
-)
-
-// ********************************* NOTE *************************************
-//
-// Most security bugs are caused by attackers playing around with symlinks
-// trying to escape from the container onto the host and/or trick into data
-// corruption on the host.  Hence, file operations on containers (including
-// *stat) should always be handled by `github.com/containers/buildah/copier`
-// which makes sure to evaluate files in a chroot'ed environment.
-//
-// Please make sure to add verbose comments when changing code to make the
-// lives of future readers easier.
-//
-// ****************************************************************************
-
-// Copier copies data from a source to a destination CopyItem.
-type Copier struct {
-	copyFunc     func() error
-	cleanUpFuncs []deferFunc
-}
-
-// cleanUp releases resources the Copier may hold open.
-func (c *Copier) cleanUp() {
-	for _, f := range c.cleanUpFuncs {
-		f()
-	}
-}
-
-// Copy data from a source to a destination CopyItem.
-func (c *Copier) Copy() error {
-	defer c.cleanUp()
-	return c.copyFunc()
-}
-
-// GetCopiers returns a Copier to copy the source item to destination.  Use
-// extract to untar the source if it's a tar archive.
-func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) {
-	copier := &Copier{}
-
-	// First, do the man-page dance.  See podman-cp(1) for details.
-	if err := enforceCopyRules(source, destination); err != nil {
-		return nil, err
-	}
-
-	// Destination is a stream (e.g., stdout or an http body).
-	if destination.info.IsStream {
-		// Source is a stream (e.g., stdin or an http body).
-		if source.info.IsStream {
-			copier.copyFunc = func() error {
-				_, err := io.Copy(destination.writer, source.reader)
-				return err
-			}
-			return copier, nil
-		}
-		root, glob, err := source.buildahGlobs()
-		if err != nil {
-			return nil, err
-		}
-		copier.copyFunc = func() error {
-			return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer)
-		}
-		return copier, nil
-	}
-
-	// Destination is either a file or a directory.
-	if source.info.IsStream {
-		copier.copyFunc = func() error {
-			return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader)
-		}
-		return copier, nil
-	}
-
-	tarOptions := &archive.TarOptions{
-		Compression: archive.Uncompressed,
-		CopyPass:    true,
-	}
-
-	root := destination.root
-	dir := destination.resolved
-	if !source.info.IsDir {
-		// When copying a file, make sure to rename the
-		// destination base path.
-		nameMap := make(map[string]string)
-		nameMap[filepath.Base(source.resolved)] = filepath.Base(destination.resolved)
-		tarOptions.RebaseNames = nameMap
-		dir = filepath.Dir(dir)
-	}
-
-	var tarReader io.ReadCloser
-	if extract && archive.IsArchivePath(source.resolved) {
-		if !destination.info.IsDir {
-			return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original)
-		}
-
-		reader, err := os.Open(source.resolved)
-		if err != nil {
-			return nil, err
-		}
-		copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() })
-
-		// The stream from stdin may be compressed (e.g., via gzip).
-		decompressedStream, err := archive.DecompressStream(reader)
-		if err != nil {
-			return nil, err
-		}
-
-		copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() })
-		tarReader = decompressedStream
-	} else {
-		reader, err := archive.TarWithOptions(source.resolved, tarOptions)
-		if err != nil {
-			return nil, err
-		}
-		copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() })
-		tarReader = reader
-	}
-
-	copier.copyFunc = func() error {
-		return buildahCopiah.Put(root, dir, source.putOptions(), tarReader)
-	}
-	return copier, nil
-}
-
-// enforceCopyRules enforces the rules for copying from a source to a
-// destination as mentioned in the podman-cp(1) man page.  Please refer to the
-// man page and/or the inline comments for further details.  Note that source
-// and destination are passed by reference and the their data may be changed.
-func enforceCopyRules(source, destination *CopyItem) error {
-	if source.statError != nil {
-		return source.statError
-	}
-
-	// We can copy everything to a stream.
-	if destination.info.IsStream {
-		return nil
-	}
-
-	if source.info.IsStream {
-		if !(destination.info.IsDir || destination.info.IsStream) {
-			return errors.New("destination must be a directory or stream when copying from a stream")
-		}
-		return nil
-	}
-
-	// Source is a *directory*.
-	if source.info.IsDir {
-		if destination.statError != nil {
-			// It's okay if the destination does not exist.  We
-			// made sure before that it's parent exists, so it
-			// would be created while copying.
-			if os.IsNotExist(destination.statError) {
-				return nil
-			}
-			// Could be a permission error.
-			return destination.statError
-		}
-
-		// If the destination exists and is not a directory, we have a
-		// problem.
-		if !destination.info.IsDir {
-			return errors.Errorf("cannot copy directory %q to file %q", source.original, destination.original)
-		}
-
-		// If the destination exists and is a directory, we need to
-		// append the source base directory to it.  This makes sure
-		// that copying "/foo/bar" "/tmp" will copy to "/tmp/bar" (and
-		// not "/tmp").
-		newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved))
-		if err != nil {
-			return err
-		}
-		destination.resolved = newDestination
-		return nil
-	}
-
-	// Source is a *file*.
-	if destination.statError != nil {
-		// It's okay if the destination does not exist, unless it ends
-		// with "/".
-		if !os.IsNotExist(destination.statError) {
-			return destination.statError
-		} else if strings.HasSuffix(destination.resolved, "/") {
-			// Note: this is practically unreachable code as the
-			// existence of parent directories is enforced early
-			// on.  It's left here as an extra security net.
-			return errors.Errorf("destination directory %q must exist (trailing %q)", destination.original, "/")
-		}
-		// Does not exist and does not end with "/".
-		return nil
-	}
-
-	// If the destination is a file, we're good.  We will overwrite the
-	// contents while copying.
-	if !destination.info.IsDir {
-		return nil
-	}
-
-	// If the destination exists and is a directory, we need to append the
-	// source base directory to it.  This makes sure that copying
-	// "/foo/bar" "/tmp" will copy to "/tmp/bar" (and not "/tmp").
-	newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved))
-	if err != nil {
-		return err
-	}
-
-	destination.resolved = newDestination
-	return nil
-}
diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go
index 08b4eb377e..50b0ca9a11 100644
--- a/pkg/copy/fileinfo.go
+++ b/pkg/copy/fileinfo.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"net/http"
 	"os"
+	"path/filepath"
 	"strings"
 	"time"
 
@@ -15,6 +16,10 @@ import (
 // base64 encoded JSON payload of stating a path in a container.
 const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat"
 
+// ENOENT mimics the stdlib's ENONENT and can be used to implement custom logic
+// while preserving the user-visible error message.
+var ENOENT = errors.New("No such file or directory")
+
 // FileInfo describes a file or directory and is returned by
 // (*CopyItem).Stat().
 type FileInfo struct {
@@ -23,7 +28,6 @@ type FileInfo struct {
 	Mode       os.FileMode `json:"mode"`
 	ModTime    time.Time   `json:"mtime"`
 	IsDir      bool        `json:"isDir"`
-	IsStream   bool        `json:"isStream"`
 	LinkTarget string      `json:"linkTarget"`
 }
 
@@ -54,3 +58,54 @@ func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) {
 
 	return &info, nil
 }
+
+// ResolveHostPath resolves the specified, possibly relative, path on the host.
+func ResolveHostPath(path string) (*FileInfo, error) {
+	resolvedHostPath, err := filepath.Abs(path)
+	if err != nil {
+		return nil, err
+	}
+	resolvedHostPath = PreserveBasePath(path, resolvedHostPath)
+
+	statInfo, err := os.Stat(resolvedHostPath)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, ENOENT
+		}
+		return nil, err
+	}
+
+	return &FileInfo{
+		Name:       statInfo.Name(),
+		Size:       statInfo.Size(),
+		Mode:       statInfo.Mode(),
+		ModTime:    statInfo.ModTime(),
+		IsDir:      statInfo.IsDir(),
+		LinkTarget: resolvedHostPath,
+	}, nil
+}
+
+// PreserveBasePath makes sure that the original base path (e.g., "/" or "./")
+// is preserved.  The filepath API among tends to clean up a bit too much but
+// we *must* preserve this data by all means.
+func PreserveBasePath(original, resolved string) string {
+	// Handle "/"
+	if strings.HasSuffix(original, "/") {
+		if !strings.HasSuffix(resolved, "/") {
+			resolved += "/"
+		}
+		return resolved
+	}
+
+	// Handle "/."
+	if strings.HasSuffix(original, "/.") {
+		if strings.HasSuffix(resolved, "/") { // could be root!
+			resolved += "."
+		} else if !strings.HasSuffix(resolved, "/.") {
+			resolved += "/."
+		}
+		return resolved
+	}
+
+	return resolved
+}
diff --git a/pkg/copy/item.go b/pkg/copy/item.go
deleted file mode 100644
index df8bf30b9b..0000000000
--- a/pkg/copy/item.go
+++ /dev/null
@@ -1,588 +0,0 @@
-package copy
-
-import (
-	"io"
-	"os"
-	"path/filepath"
-	"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/libpod/define"
-	"github.com/containers/podman/v2/pkg/cgroups"
-	"github.com/containers/podman/v2/pkg/rootless"
-	"github.com/containers/storage"
-	"github.com/containers/storage/pkg/archive"
-	"github.com/containers/storage/pkg/idtools"
-	securejoin "github.com/cyphar/filepath-securejoin"
-	"github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
-)
-
-// ********************************* NOTE *************************************
-//
-// Most security bugs are caused by attackers playing around with symlinks
-// trying to escape from the container onto the host and/or trick into data
-// corruption on the host.  Hence, file operations on containers (including
-// *stat) should always be handled by `github.com/containers/buildah/copier`
-// which makes sure to evaluate files in a chroot'ed environment.
-//
-// Please make sure to add verbose comments when changing code to make the
-// lives of future readers easier.
-//
-// ****************************************************************************
-
-var (
-	_stdin  = os.Stdin.Name()
-	_stdout = os.Stdout.Name()
-)
-
-// CopyItem is the source or destination of a copy operation.  Use the
-// CopyItemFrom* functions to create one for the specific source/destination
-// item.
-type CopyItem struct {
-	// The original path provided by the caller.  Useful in error messages.
-	original string
-	// The resolved path on the host or container.  Maybe altered at
-	// multiple stages when copying.
-	resolved string
-	// The root for copying data in a chroot'ed environment.
-	root string
-
-	// IDPair of the resolved path.
-	idPair *idtools.IDPair
-	// Storage ID mappings.
-	idMappings *storage.IDMappingOptions
-
-	// Internal FileInfo.  We really don't want users to mess with a
-	// CopyItem but only plug and play with it.
-	info FileInfo
-	// Error when creating the upper FileInfo.  Some errors are non-fatal,
-	// for instance, when a destination *base* path does not exist.
-	statError error
-
-	writer io.Writer
-	reader io.Reader
-
-	// Needed to clean up resources (e.g., unmount a container).
-	cleanUpFuncs []deferFunc
-}
-
-// deferFunc allows for returning functions that must be deferred at call sites.
-type deferFunc func()
-
-// Stat returns the FileInfo.
-func (item *CopyItem) Stat() (*FileInfo, error) {
-	return &item.info, item.statError
-}
-
-// CleanUp releases resources such as the container mounts.  It *must* be
-// called even in case of errors.
-func (item *CopyItem) CleanUp() {
-	for _, f := range item.cleanUpFuncs {
-		f()
-	}
-}
-
-// CopyItemForWriter returns a CopyItem for the specified io.WriteCloser.  Note
-// that the returned item can only act as a copy destination.
-func CopyItemForWriter(writer io.Writer) (item CopyItem, _ error) {
-	item.writer = writer
-	item.info.IsStream = true
-	return item, nil
-}
-
-// CopyItemForReader returns a CopyItem for the specified io.ReaderCloser.  Note
-// that the returned item can only act as a copy source.
-//
-// Note that the specified reader will be auto-decompressed if needed.
-func CopyItemForReader(reader io.Reader) (item CopyItem, _ error) {
-	item.info.IsStream = true
-	decompressed, err := archive.DecompressStream(reader)
-	if err != nil {
-		return item, err
-	}
-	item.reader = decompressed
-	item.cleanUpFuncs = append(item.cleanUpFuncs, func() {
-		if err := decompressed.Close(); err != nil {
-			logrus.Errorf("Error closing decompressed reader of copy item: %v", err)
-		}
-	})
-	return item, nil
-}
-
-// CopyItemForHost creates a CopyItem for the specified host path.  It's a
-// destination by default.  Use isSource to set it as a destination.
-//
-// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors.
-func CopyItemForHost(hostPath string, isSource bool) (item CopyItem, _ error) {
-	if hostPath == "-" {
-		if isSource {
-			hostPath = _stdin
-		} else {
-			hostPath = _stdout
-		}
-	}
-
-	if hostPath == _stdin {
-		return CopyItemForReader(os.Stdin)
-	}
-
-	if hostPath == _stdout {
-		return CopyItemForWriter(os.Stdout)
-	}
-
-	// Now do the dance for the host data.
-	resolvedHostPath, err := filepath.Abs(hostPath)
-	if err != nil {
-		return item, err
-	}
-
-	resolvedHostPath = preserveBasePath(hostPath, resolvedHostPath)
-	item.original = hostPath
-	item.resolved = resolvedHostPath
-	item.root = "/"
-
-	statInfo, statError := os.Stat(resolvedHostPath)
-	item.statError = statError
-
-	// It exists, we're done.
-	if statError == nil {
-		item.info.Name = statInfo.Name()
-		item.info.Size = statInfo.Size()
-		item.info.Mode = statInfo.Mode()
-		item.info.ModTime = statInfo.ModTime()
-		item.info.IsDir = statInfo.IsDir()
-		item.info.LinkTarget = resolvedHostPath
-		return item, nil
-	}
-
-	// The source must exist, but let's try to give some human-friendly
-	// errors.
-	if isSource {
-		if os.IsNotExist(item.statError) {
-			return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", hostPath)
-		}
-		return item, item.statError // could be a permission error
-	}
-
-	// If we're a destination, we need to make sure that the parent
-	// directory exists.
-	parent := filepath.Dir(resolvedHostPath)
-	if _, err := os.Stat(parent); err != nil {
-		if os.IsNotExist(err) {
-			return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", parent)
-		}
-		return item, err
-	}
-
-	return item, nil
-}
-
-// CopyItemForContainer creates a CopyItem for the specified path on the
-// container.  It's a destination by default.  Use isSource to set it as a
-// destination.  Note that the container path may resolve to a path outside of
-// the container's mount point if the path hits a volume or mount on the
-// container.
-//
-// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors.
-func CopyItemForContainer(container *libpod.Container, containerPath string, pause bool, isSource bool) (item CopyItem, _ error) {
-	// Mount and pause the container.
-	containerMountPoint, err := item.mountAndPauseContainer(container, pause)
-	if err != nil {
-		return item, err
-	}
-
-	// Make sure that "/" copies the *contents* of the mount point and not
-	// the directory.
-	if containerPath == "/" {
-		containerPath += "/."
-	}
-
-	// Now resolve the container's path.  It may hit a volume, it may hit a
-	// bind mount, it may be relative.
-	resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath)
-	if err != nil {
-		return item, err
-	}
-	resolvedContainerPath = preserveBasePath(containerPath, resolvedContainerPath)
-
-	idMappings, idPair, err := getIDMappingsAndPair(container, containerMountPoint)
-	if err != nil {
-		return item, err
-	}
-
-	item.original = containerPath
-	item.resolved = resolvedContainerPath
-	item.root = resolvedRoot
-	item.idMappings = idMappings
-	item.idPair = idPair
-
-	statInfo, statError := secureStat(resolvedRoot, resolvedContainerPath)
-	item.statError = statError
-
-	// It exists, we're done.
-	if statError == nil {
-		item.info.IsDir = statInfo.IsDir
-		item.info.Name = filepath.Base(statInfo.Name)
-		item.info.Size = statInfo.Size
-		item.info.Mode = statInfo.Mode
-		item.info.ModTime = statInfo.ModTime
-		item.info.IsDir = statInfo.IsDir
-		item.info.LinkTarget = resolvedContainerPath
-		return item, nil
-	}
-
-	// The source must exist, but let's try to give some human-friendly
-	// errors.
-	if isSource {
-		if os.IsNotExist(statError) {
-			return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath)
-		}
-		return item, item.statError // could be a permission error
-	}
-
-	// If we're a destination, we need to make sure that the parent
-	// directory exists.
-	parent := filepath.Dir(resolvedContainerPath)
-	if _, err := secureStat(resolvedRoot, parent); err != nil {
-		if os.IsNotExist(err) {
-			return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath)
-		}
-		return item, err
-	}
-
-	return item, nil
-}
-
-// putOptions returns PUT options for buildah's copier package.
-func (item *CopyItem) putOptions() buildahCopiah.PutOptions {
-	options := buildahCopiah.PutOptions{}
-	if item.idMappings != nil {
-		options.UIDMap = item.idMappings.UIDMap
-		options.GIDMap = item.idMappings.GIDMap
-	}
-	if item.idPair != nil {
-		options.ChownDirs = item.idPair
-		options.ChownFiles = item.idPair
-	}
-	return options
-}
-
-// getOptions returns GET options for buildah's copier package.
-func (item *CopyItem) getOptions() buildahCopiah.GetOptions {
-	options := buildahCopiah.GetOptions{}
-	if item.idMappings != nil {
-		options.UIDMap = item.idMappings.UIDMap
-		options.GIDMap = item.idMappings.GIDMap
-	}
-	if item.idPair != nil {
-		options.ChownDirs = item.idPair
-		options.ChownFiles = item.idPair
-	}
-	return options
-
-}
-
-// mount and pause the container.  Also set the item's cleanUpFuncs.  Those
-// *must* be invoked by callers, even in case of errors.
-func (item *CopyItem) mountAndPauseContainer(container *libpod.Container, pause bool) (string, error) {
-	// Make sure to pause and unpause the container.  We cannot pause on
-	// cgroupsv1 as rootless user, in which case we turn off pausing.
-	if pause && rootless.IsRootless() {
-		cgroupv2, _ := cgroups.IsCgroup2UnifiedMode()
-		if !cgroupv2 {
-			logrus.Debugf("Cannot pause container for copying as a rootless user on cgroupsv1: default to not pause")
-			pause = false
-		}
-	}
-
-	// Mount and unmount the container.
-	mountPoint, err := container.Mount()
-	if err != nil {
-		return "", err
-	}
-
-	item.cleanUpFuncs = append(item.cleanUpFuncs, func() {
-		if err := container.Unmount(false); err != nil {
-			logrus.Errorf("Error unmounting container after copy operation: %v", err)
-		}
-	})
-
-	// Pause and unpause the container.
-	if pause {
-		if err := container.Pause(); err != nil {
-			// Ignore errors when the container isn't running.  No
-			// need to pause.
-			if errors.Cause(err) != define.ErrCtrStateInvalid {
-				return "", err
-			}
-		} else {
-			item.cleanUpFuncs = append(item.cleanUpFuncs, func() {
-				if err := container.Unpause(); err != nil {
-					logrus.Errorf("Error unpausing container after copy operation: %v", err)
-				}
-			})
-		}
-	}
-
-	return mountPoint, nil
-}
-
-// buildahGlobs returns the root, dir and glob used in buildah's copier
-// package.
-//
-// Note that dir is always empty.
-func (item *CopyItem) buildahGlobs() (root string, glob string, err error) {
-	root = item.root
-
-	// If the root and the resolved path are equal, then dir must be empty
-	// and the glob must be ".".
-	if filepath.Clean(root) == filepath.Clean(item.resolved) {
-		glob = "."
-		return
-	}
-
-	glob, err = filepath.Rel(root, item.resolved)
-	return
-}
-
-// preserveBasePath makes sure that the original base path (e.g., "/" or "./")
-// is preserved.  The filepath API among tends to clean up a bit too much but
-// we *must* preserve this data by all means.
-func preserveBasePath(original, resolved string) string {
-	// Handle "/"
-	if strings.HasSuffix(original, "/") {
-		if !strings.HasSuffix(resolved, "/") {
-			resolved += "/"
-		}
-		return resolved
-	}
-
-	// Handle "/."
-	if strings.HasSuffix(original, "/.") {
-		if strings.HasSuffix(resolved, "/") { // could be root!
-			resolved += "."
-		} else if !strings.HasSuffix(resolved, "/.") {
-			resolved += "/."
-		}
-		return resolved
-	}
-
-	return resolved
-}
-
-// secureStat extracts file info for path in a chroot'ed environment in root.
-func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) {
-	var glob string
-	var err error
-
-	// If root and path are equal, then dir must be empty and the glob must
-	// be ".".
-	if filepath.Clean(root) == filepath.Clean(path) {
-		glob = "."
-	} else {
-		glob, err = filepath.Rel(root, path)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob})
-	if err != nil {
-		return nil, err
-	}
-
-	if len(globStats) != 1 {
-		return nil, errors.Errorf("internal libpod 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 stat, os.ErrNotExist
-	}
-
-	var statErr error
-	if stat.Error != "" {
-		statErr = errors.New(stat.Error)
-	}
-	return stat, statErr
-}
-
-// resolveContainerPaths resolves the container's mount point and the container
-// path as specified by the user.  Both may resolve to paths outside of the
-// container's mount point when the container path hits a volume or bind mount.
-//
-// NOTE: We must take volumes and bind mounts into account as, regrettably, we
-// can copy to/from stopped containers.  In that case, the volumes and bind
-// mounts are not present.  For running containers, the runtime (e.g., runc or
-// crun) takes care of these mounts.  For stopped ones, we need to do quite
-// some dance, as done below.
-func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) {
-	// Let's first make sure we have a path relative to the mount point.
-	pathRelativeToContainerMountPoint := containerPath
-	if !filepath.IsAbs(containerPath) {
-		// If the containerPath is not absolute, it's relative to the
-		// container's working dir.  To be extra careful, let's first
-		// join the working dir with "/", and the add the containerPath
-		// to it.
-		pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath)
-	}
-	// NOTE: the secure join makes sure that we follow symlinks.  This way,
-	// we catch scenarios where the container path symlinks to a volume or
-	// bind mount.
-	resolvedPathOnTheContainerMountPoint, err := securejoin.SecureJoin(mountPoint, pathRelativeToContainerMountPoint)
-	if err != nil {
-		return "", "", err
-	}
-	pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
-	pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
-
-	// Now we have an "absolute container Path" but not yet resolved on the
-	// host (e.g., "/foo/bar/file.txt").  As mentioned above, we need to
-	// check if "/foo/bar/file.txt" is on a volume or bind mount.  To do
-	// that, we need to walk *down* the paths to the root.  Assuming
-	// volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar",
-	// we must select "/foo/bar".  Once selected, we need to rebase the
-	// remainder (i.e, "/file.txt") on the volume's mount point on the
-	// host.  Same applies to bind mounts.
-
-	searchPath := pathRelativeToContainerMountPoint
-	for {
-		volume, err := findVolume(container, searchPath)
-		if err != nil {
-			return "", "", err
-		}
-		if volume != nil {
-			logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath)
-			// We found a matching volume for searchPath.  We now
-			// need to first find the relative path of our input
-			// path to the searchPath, and then join it with the
-			// volume's mount point.
-			pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
-			absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume)
-			if err != nil {
-				return "", "", err
-			}
-			return volume.MountPoint(), absolutePathOnTheVolumeMount, nil
-		}
-
-		if mount := findBindMount(container, searchPath); mount != nil {
-			logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath)
-			// We found a matching bind mount for searchPath.  We
-			// now need to first find the relative path of our
-			// input path to the searchPath, and then join it with
-			// the source of the bind mount.
-			pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
-			absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount)
-			if err != nil {
-				return "", "", err
-			}
-			return mount.Source, absolutePathOnTheBindMount, nil
-
-		}
-
-		if searchPath == "/" {
-			// Cannot go beyond "/", so we're done.
-			break
-		}
-
-		// Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar").
-		searchPath = filepath.Dir(searchPath)
-	}
-
-	// No volume, no bind mount but just a normal path on the container.
-	return mountPoint, resolvedPathOnTheContainerMountPoint, nil
-}
-
-// findVolume checks if the specified container path matches a volume inside
-// the container.  It returns a matching volume or nil.
-func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) {
-	runtime := c.Runtime()
-	cleanedContainerPath := filepath.Clean(containerPath)
-	for _, vol := range c.Config().NamedVolumes {
-		if cleanedContainerPath == filepath.Clean(vol.Dest) {
-			return runtime.GetVolume(vol.Name)
-		}
-	}
-	return nil, nil
-}
-
-// findBindMount checks if the specified container path matches a bind mount
-// inside the container.  It returns a matching mount or nil.
-func findBindMount(c *libpod.Container, containerPath string) *specs.Mount {
-	cleanedPath := filepath.Clean(containerPath)
-	for _, m := range c.Config().Spec.Mounts {
-		if m.Type != "bind" {
-			continue
-		}
-		if cleanedPath == filepath.Clean(m.Destination) {
-			mount := m
-			return &mount
-		}
-	}
-	return 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
-}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 01086a2b33..4442c00301 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/containers/image/v5/types"
 	"github.com/containers/podman/v2/libpod/define"
+	"github.com/containers/podman/v2/pkg/copy"
 	"github.com/containers/podman/v2/pkg/specgen"
 	"github.com/cri-o/ocicni/pkg/ocicni"
 )
@@ -143,6 +144,10 @@ type ContainerInspectReport struct {
 	*define.InspectContainerData
 }
 
+type ContainerStatReport struct {
+	copy.FileInfo
+}
+
 type CommitOptions struct {
 	Author         string
 	Changes        []string
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index ac90734029..80127ea457 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -2,6 +2,7 @@ package entities
 
 import (
 	"context"
+	"io"
 
 	"github.com/containers/common/pkg/config"
 	"github.com/containers/podman/v2/libpod/define"
@@ -9,6 +10,8 @@ import (
 	"github.com/spf13/cobra"
 )
 
+type ContainerCopyFunc func() error
+
 type ContainerEngine interface {
 	AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error)
 	Config(ctx context.Context) (*config.Config, error)
@@ -16,7 +19,8 @@ type ContainerEngine interface {
 	ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
 	ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error)
 	ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error)
-	ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) error
+	ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error)
+	ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error)
 	ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
 	ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
 	ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
@@ -38,6 +42,7 @@ type ContainerEngine interface {
 	ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error)
 	ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error
 	ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
+	ContainerStat(ctx context.Context, nameOrDir string, path string) (*ContainerStatReport, error)
 	ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) (chan ContainerStatsReport, error)
 	ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
 	ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go
new file mode 100644
index 0000000000..809813756f
--- /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
+}
diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go
new file mode 100644
index 0000000000..c9610d1b91
--- /dev/null
+++ b/pkg/domain/infra/abi/containers_stat.go
@@ -0,0 +1,251 @@
+package abi
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"strings"
+
+	buildahCopiah "github.com/containers/buildah/copier"
+	"github.com/containers/podman/v2/libpod"
+	"github.com/containers/podman/v2/pkg/copy"
+	"github.com/containers/podman/v2/pkg/domain/entities"
+	securejoin "github.com/cyphar/filepath-securejoin"
+	"github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+)
+
+func (ic *ContainerEngine) containerStat(container *libpod.Container, containerPath string) (*entities.ContainerStatReport, string, string, error) {
+	containerMountPoint, err := container.Mount()
+	if err != nil {
+		return nil, "", "", err
+	}
+
+	// Make sure that "/" copies the *contents* of the mount point and not
+	// the directory.
+	if containerPath == "/" {
+		containerPath += "/."
+	}
+
+	// Now resolve the container's path.  It may hit a volume, it may hit a
+	// bind mount, it may be relative.
+	resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath)
+	if err != nil {
+		return nil, "", "", err
+	}
+
+	statInfo, statInfoErr := secureStat(resolvedRoot, resolvedContainerPath)
+	if statInfoErr != nil {
+		// Not all errors from secureStat map to ErrNotExist, so we
+		// have to look into the error string.  Turning it into an
+		// ENOENT let's the API handlers return the correct status code
+		// which is crucuial for the remote client.
+		if os.IsNotExist(err) || strings.Contains(statInfoErr.Error(), "o such file or directory") {
+			statInfoErr = copy.ENOENT
+		}
+		//  If statInfo is nil, there's nothing we can do anymore.  A
+		//  non-nil statInfo may indicate a symlink where we must have
+		//  a closer look.
+		if statInfo == nil {
+			return nil, "", "", statInfoErr
+		}
+	}
+
+	// Now make sure that the info's LinkTarget is relative to the
+	// container's mount.
+	var absContainerPath string
+
+	if statInfo.IsSymlink {
+		// Evaluated symlinks are always relative to the container's mount point.
+		absContainerPath = statInfo.ImmediateTarget
+	} else if strings.HasPrefix(resolvedContainerPath, containerMountPoint) {
+		// If the path is on the container's mount point, strip it off.
+		absContainerPath = strings.TrimPrefix(resolvedContainerPath, containerMountPoint)
+		absContainerPath = filepath.Join("/", absContainerPath)
+	} else {
+		// No symlink and not on the container's mount point, so let's
+		// move it back to the original input.  It must have evaluated
+		// to a volume or bind mount but we cannot return host paths.
+		absContainerPath = containerPath
+	}
+
+	// Now we need to make sure to preseve the base path as specified by
+	// the user.  The `filepath` packages likes to remove trailing slashes
+	// and dots that are crucial to the copy logic.
+	absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath)
+	resolvedContainerPath = copy.PreserveBasePath(containerPath, resolvedContainerPath)
+
+	info := copy.FileInfo{
+		IsDir:      statInfo.IsDir,
+		Name:       filepath.Base(absContainerPath),
+		Size:       statInfo.Size,
+		Mode:       statInfo.Mode,
+		ModTime:    statInfo.ModTime,
+		LinkTarget: absContainerPath,
+	}
+
+	return &entities.ContainerStatReport{FileInfo: info}, resolvedRoot, resolvedContainerPath, statInfoErr
+}
+
+func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, containerPath string) (*entities.ContainerStatReport, error) {
+	container, err := ic.Libpod.LookupContainer(nameOrID)
+	if err != nil {
+		return nil, err
+	}
+
+	defer func() {
+		if err := container.Unmount(false); err != nil {
+			logrus.Errorf("Error unmounting container: %v", err)
+		}
+	}()
+
+	statReport, _, _, err := ic.containerStat(container, containerPath)
+	return statReport, err
+}
+
+// resolveContainerPaths resolves the container's mount point and the container
+// path as specified by the user.  Both may resolve to paths outside of the
+// container's mount point when the container path hits a volume or bind mount.
+//
+// NOTE: We must take volumes and bind mounts into account as, regrettably, we
+// can copy to/from stopped containers.  In that case, the volumes and bind
+// mounts are not present.  For running containers, the runtime (e.g., runc or
+// crun) takes care of these mounts.  For stopped ones, we need to do quite
+// some dance, as done below.
+func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) {
+	// Let's first make sure we have a path relative to the mount point.
+	pathRelativeToContainerMountPoint := containerPath
+	if !filepath.IsAbs(containerPath) {
+		// If the containerPath is not absolute, it's relative to the
+		// container's working dir.  To be extra careful, let's first
+		// join the working dir with "/", and the add the containerPath
+		// to it.
+		pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath)
+	}
+	resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint)
+	pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
+	pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
+
+	// Now we have an "absolute container Path" but not yet resolved on the
+	// host (e.g., "/foo/bar/file.txt").  As mentioned above, we need to
+	// check if "/foo/bar/file.txt" is on a volume or bind mount.  To do
+	// that, we need to walk *down* the paths to the root.  Assuming
+	// volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar",
+	// we must select "/foo/bar".  Once selected, we need to rebase the
+	// remainder (i.e, "/file.txt") on the volume's mount point on the
+	// host.  Same applies to bind mounts.
+
+	searchPath := pathRelativeToContainerMountPoint
+	for {
+		volume, err := findVolume(container, searchPath)
+		if err != nil {
+			return "", "", err
+		}
+		if volume != nil {
+			logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath)
+			// We found a matching volume for searchPath.  We now
+			// need to first find the relative path of our input
+			// path to the searchPath, and then join it with the
+			// volume's mount point.
+			pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
+			absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume)
+			if err != nil {
+				return "", "", err
+			}
+			return volume.MountPoint(), absolutePathOnTheVolumeMount, nil
+		}
+
+		if mount := findBindMount(container, searchPath); mount != nil {
+			logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath)
+			// We found a matching bind mount for searchPath.  We
+			// now need to first find the relative path of our
+			// input path to the searchPath, and then join it with
+			// the source of the bind mount.
+			pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
+			absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount)
+			if err != nil {
+				return "", "", err
+			}
+			return mount.Source, absolutePathOnTheBindMount, nil
+
+		}
+
+		if searchPath == "/" {
+			// Cannot go beyond "/", so we're done.
+			break
+		}
+
+		// Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar").
+		searchPath = filepath.Dir(searchPath)
+	}
+
+	// No volume, no bind mount but just a normal path on the container.
+	return mountPoint, resolvedPathOnTheContainerMountPoint, nil
+}
+
+// findVolume checks if the specified container path matches a volume inside
+// the container.  It returns a matching volume or nil.
+func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) {
+	runtime := c.Runtime()
+	cleanedContainerPath := filepath.Clean(containerPath)
+	for _, vol := range c.Config().NamedVolumes {
+		if cleanedContainerPath == filepath.Clean(vol.Dest) {
+			return runtime.GetVolume(vol.Name)
+		}
+	}
+	return nil, nil
+}
+
+// findBindMount checks if the specified container path matches a bind mount
+// inside the container.  It returns a matching mount or nil.
+func findBindMount(c *libpod.Container, containerPath string) *specs.Mount {
+	cleanedPath := filepath.Clean(containerPath)
+	for _, m := range c.Config().Spec.Mounts {
+		if m.Type != "bind" {
+			continue
+		}
+		if cleanedPath == filepath.Clean(m.Destination) {
+			mount := m
+			return &mount
+		}
+	}
+	return nil
+}
+
+// secureStat extracts file info for path in a chroot'ed environment in root.
+func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) {
+	var glob string
+	var err error
+
+	// If root and path are equal, then dir must be empty and the glob must
+	// be ".".
+	if filepath.Clean(root) == filepath.Clean(path) {
+		glob = "."
+	} else {
+		glob, err = filepath.Rel(root, path)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob})
+	if err != nil {
+		return nil, err
+	}
+
+	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.ENOENT
+	}
+
+	var statErr error
+	if stat.Error != "" {
+		statErr = errors.New(stat.Error)
+	}
+	return stat, statErr
+}
diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go
deleted file mode 100644
index 362053cceb..0000000000
--- a/pkg/domain/infra/abi/cp.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package abi
-
-import (
-	"context"
-
-	"github.com/containers/podman/v2/libpod"
-	"github.com/containers/podman/v2/pkg/copy"
-	"github.com/containers/podman/v2/pkg/domain/entities"
-)
-
-func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error {
-	// Parse user input.
-	sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest)
-	if err != nil {
-		return err
-	}
-
-	// Look up containers.
-	var sourceContainer, destContainer *libpod.Container
-	if len(sourceContainerStr) > 0 {
-		sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr)
-		if err != nil {
-			return err
-		}
-	}
-	if len(destContainerStr) > 0 {
-		destContainer, err = ic.Libpod.LookupContainer(destContainerStr)
-		if err != nil {
-			return err
-		}
-	}
-
-	var sourceItem, destinationItem copy.CopyItem
-
-	// Source ... container OR host.
-	if sourceContainer != nil {
-		sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true)
-		defer sourceItem.CleanUp()
-		if err != nil {
-			return err
-		}
-	} else {
-		sourceItem, err = copy.CopyItemForHost(sourcePath, true)
-		if err != nil {
-			return err
-		}
-	}
-
-	// Destination ... container OR host.
-	if destContainer != nil {
-		destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false)
-		defer destinationItem.CleanUp()
-		if err != nil {
-			return err
-		}
-	} else {
-		destinationItem, err = copy.CopyItemForHost(destPath, false)
-		defer destinationItem.CleanUp()
-		if err != nil {
-			return err
-		}
-	}
-
-	// Copy from the host to the container.
-	copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract)
-	if err != nil {
-		return err
-	}
-	return copier.Copy()
-}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index ad7688f626..855339fefd 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -772,9 +772,16 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o
 	return reports, nil
 }
 
-func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error {
-	return nil
-	//	return containers.Copy(ic.ClientCxt, source, dest, options)
+func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) {
+	return containers.CopyFromArchive(ic.ClientCxt, nameOrID, path, reader)
+}
+
+func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) {
+	return containers.CopyToArchive(ic.ClientCxt, nameOrID, path, writer)
+}
+
+func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) {
+	return containers.Stat(ic.ClientCxt, nameOrID, path)
 }
 
 // Shutdown Libpod engine
diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go
index ca6b60bc5a..21df261fb3 100644
--- a/pkg/errorhandling/errorhandling.go
+++ b/pkg/errorhandling/errorhandling.go
@@ -19,7 +19,12 @@ func JoinErrors(errs []error) error {
 	// blank lines when printing the error.
 	var multiE *multierror.Error
 	multiE = multierror.Append(multiE, errs...)
-	return errors.New(strings.TrimSpace(multiE.ErrorOrNil().Error()))
+
+	finalErr := multiE.ErrorOrNil()
+	if finalErr == nil {
+		return finalErr
+	}
+	return errors.New(strings.TrimSpace(finalErr.Error()))
 }
 
 // ErrorsToString converts the slice of errors into a slice of corresponding
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index 33908b60ec..6fe26c444f 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -24,7 +24,6 @@ var _ = Describe("Podman cp", func() {
 	)
 
 	BeforeEach(func() {
-		SkipIfRemote("FIXME: Podman-remote cp needs to work")
 		tempdir, err = CreateTempDirInTempDir()
 		if err != nil {
 			os.Exit(1)
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index 43bdf217d4..73b07ea45b 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -8,8 +8,6 @@
 load helpers
 
 @test "podman cp file from host to container" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr
     mkdir -p $srcdir
     local -a randomcontent=(
@@ -57,60 +55,16 @@ load helpers
     is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \
        "copy nonexistent host path"
 
-    # Container path does not exist.  Notice that the error message shows how
-    # the specified container is resolved.
+    # Container (parent) path does not exist.
     run_podman 125 cp $srcdir/hostfile0 cpcontainer:/IdoNotExist/
-    is "$output" 'Error: "/IdoNotExist/" could not be found on container.*(resolved to .*/IdoNotExist.*' \
+    is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: No such file or directory' \
        "copy into nonexistent path in container"
 
     run_podman rm -f cpcontainer
 }
 
 
-@test "podman cp --extract=true tar archive to container" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
-    # Create tempfile with random name and content
-    dirname=cp-test-extract
-    srcdir=$PODMAN_TMPDIR/$dirname
-    mkdir -p $srcdir
-    rand_filename=$(random_string 20)
-    rand_content=$(random_string 50)
-    echo $rand_content > $srcdir/$rand_filename
-    chmod 644 $srcdir/$rand_filename
-
-    # Now tar it up!
-    tar_file=$PODMAN_TMPDIR/archive.tar.gz
-    tar -C $PODMAN_TMPDIR -zvcf $tar_file $dirname
-
-    run_podman run -d --name cpcontainer $IMAGE sleep infinity
-
-    # First just copy without extracting the archive.
-    run_podman cp $tar_file cpcontainer:/tmp
-    # Now remove the archive which will also test if it exists and is a file.
-    # To save expensive exec'ing, create a file for the next tests.
-    run_podman exec cpcontainer sh -c "rm /tmp/archive.tar.gz; touch /tmp/file.txt"
-
-    # Now copy with extracting the archive. NOTE that Podman should
-    # auto-decompress the file if needed.
-    run_podman cp --extract=true $tar_file cpcontainer:/tmp
-    run_podman exec cpcontainer cat /tmp/$dirname/$rand_filename
-    is "$output" "$rand_content"
-
-    # Test extract on non archive.
-    run_podman cp --extract=true $srcdir/$rand_filename cpcontainer:/foo.txt
-
-    # Cannot extract an archive to a file!
-    run_podman 125 cp --extract=true $tar_file cpcontainer:/tmp/file.txt
-    is "$output" 'Error: cannot extract archive .* to file "/tmp/file.txt"'
-
-    run_podman rm -f cpcontainer
-}
-
-
 @test "podman cp file from container to host" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host
     mkdir -p $srcdir
 
@@ -153,8 +107,6 @@ load helpers
 
 
 @test "podman cp dir from host to container" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     dirname=dir-test
     srcdir=$PODMAN_TMPDIR/$dirname
     mkdir -p $srcdir
@@ -195,8 +147,6 @@ load helpers
 
 
 @test "podman cp dir from container to host" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/dir-test
     mkdir -p $srcdir
 
@@ -230,8 +180,6 @@ load helpers
 
 
 @test "podman cp file from host to container volume" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-volume
     mkdir -p $srcdir
     echo "This file should be in volume2" > $srcdir/hostfile
@@ -268,8 +216,6 @@ load helpers
 
 
 @test "podman cp file from host to container mount" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-mount-src
     mountdir=$PODMAN_TMPDIR/cp-test-mount
     mkdir -p $srcdir $mountdir
@@ -296,8 +242,6 @@ load helpers
 # perform wildcard expansion in the container. We should get both
 # files copied into the host.
 @test "podman cp * - wildcard copy multiple files from container to host" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-in
     dstdir=$PODMAN_TMPDIR/cp-test-out
     mkdir -p $srcdir $dstdir
@@ -321,8 +265,6 @@ load helpers
 # Create a file on the host; make a symlink in the container pointing
 # into host-only space. Try to podman-cp that symlink. It should fail.
 @test "podman cp - will not recognize symlink pointing into host space" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-in
     dstdir=$PODMAN_TMPDIR/cp-test-out
     mkdir -p $srcdir $dstdir
@@ -350,8 +292,6 @@ load helpers
 # in the container pointing to 'file*' (file star). Try to podman-cp
 # this invalid double symlink. It must fail.
 @test "podman cp - will not expand globs in host space (#3829)" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-in
     dstdir=$PODMAN_TMPDIR/cp-test-out
     mkdir -p $srcdir $dstdir
@@ -372,8 +312,6 @@ load helpers
 
 # Another symlink into host space, this one named '*' (star). cp should fail.
 @test "podman cp - will not expand wildcard" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-in
     dstdir=$PODMAN_TMPDIR/cp-test-out
     mkdir -p $srcdir $dstdir
@@ -394,8 +332,6 @@ load helpers
 
 # THIS IS EXTREMELY WEIRD. Podman expands symlinks in weird ways.
 @test "podman cp into container: weird symlink expansion" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-in
     dstdir=$PODMAN_TMPDIR/cp-test-out
     mkdir -p $srcdir $dstdir
@@ -427,7 +363,7 @@ load helpers
     is "$output" "" "output from podman cp 1"
 
     run_podman 125 cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/
-    is "$output" 'Error: "/tmp/d2/x/" could not be found on container.*' "cp will not create nonexistent destination directory"
+    is "$output" 'Error: "/tmp/d2/x/" could not be found on container cpcontainer: No such file or directory' "cp will not create nonexistent destination directory"
 
     run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x
     is "$output" "" "output from podman cp 3"
@@ -454,8 +390,6 @@ load helpers
 # rhbz1741718 : file copied into container:/var/lib/foo appears as /foo
 # (docker only, never seems to have affected podman. Make sure it never does).
 @test "podman cp into a subdirectory matching GraphRoot" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     # Create tempfile with random name and content
     srcdir=$PODMAN_TMPDIR/cp-test-in
     mkdir -p $srcdir
@@ -491,8 +425,6 @@ load helpers
 
 
 @test "podman cp from stdin to container" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     # Create tempfile with random name and content
     srcdir=$PODMAN_TMPDIR/cp-test-stdin
     mkdir -p $srcdir
@@ -525,24 +457,22 @@ load helpers
 
     # Input stream must be a (compressed) tar archive.
     run_podman 125 cp - cpcontainer:/tmp < $srcdir/$rand_filename
-    is "$output" "Error:.*: error reading tar stream.*" "input stream must be a (compressed) tar archive"
+    is "$output" "Error: source must be a (compressed) tar archive when copying from stdin"
 
     # Destination must be a directory (on an existing file).
     run_podman exec cpcontainer touch /tmp/file.txt
     run_podman 125 cp /dev/stdin cpcontainer:/tmp/file.txt < $tar_file
-    is "$output" 'Error: destination must be a directory or stream when copying from a stream'
+    is "$output" 'Error: destination must be a directory when copying from stdin'
 
     # Destination must be a directory (on an absent path).
     run_podman 125 cp /dev/stdin cpcontainer:/tmp/IdoNotExist < $tar_file
-    is "$output" 'Error: destination must be a directory or stream when copying from a stream'
+    is "$output" 'Error: destination must be a directory when copying from stdin'
 
     run_podman rm -f cpcontainer
 }
 
 
 @test "podman cp from container to stdout" {
-    skip_if_remote "podman-remote does not yet handle cp"
-
     srcdir=$PODMAN_TMPDIR/cp-test-stdout
     mkdir -p $srcdir
     rand_content=$(random_string 50)
@@ -579,9 +509,9 @@ load helpers
     fi
 
     tar xvf $srcdir/stdout.tar -C $srcdir
-    run cat $srcdir/file.txt
+    run cat $srcdir/tmp/file.txt
     is "$output" "$rand_content"
-    run cat $srcdir/empty.txt
+    run cat $srcdir/tmp/empty.txt
     is "$output" ""
 
     run_podman rm -f cpcontainer
diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml
index 65ce260803..589de9c61a 100644
--- a/vendor/github.com/containers/buildah/.cirrus.yml
+++ b/vendor/github.com/containers/buildah/.cirrus.yml
@@ -178,7 +178,7 @@ gce_instance:
             image_name: "${FEDORA_CACHE_IMAGE_NAME}"
             image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
             image_name: "${UBUNTU_CACHE_IMAGE_NAME}"
-            image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}"
+#            image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}"
 
     # Separate scripts for separate outputs, makes debugging easier.
     setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'
diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile
index e70dd161da..45f8a8ec85 100644
--- a/vendor/github.com/containers/buildah/Makefile
+++ b/vendor/github.com/containers/buildah/Makefile
@@ -39,6 +39,14 @@ SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go cmd/buildah/*.go copier/*.g
 
 LINTFLAGS ?=
 
+ifeq ($(DEBUG), 1)
+  override GOGCFLAGS += -N -l
+endif
+
+#   make all DEBUG=1
+#     Note: Uses the -N -l go compiler options to disable compiler optimizations
+#           and inlining. Using these build options allows you to subsequently
+#           use source debugging tools like delve.
 all: bin/buildah bin/imgtype docs
 
 # Update nix/nixpkgs.json its latest stable commit
@@ -56,7 +64,7 @@ static:
 
 .PHONY: bin/buildah
 bin/buildah:  $(SOURCES)
-	$(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./cmd/buildah
+	$(GO_BUILD) $(BUILDAH_LDFLAGS) -gcflags "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah
 
 .PHONY: buildah
 buildah: bin/buildah
diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go
index 84b636202a..ef0e4778d7 100644
--- a/vendor/github.com/containers/buildah/copier/copier.go
+++ b/vendor/github.com/containers/buildah/copier/copier.go
@@ -10,6 +10,7 @@ import (
 	"net"
 	"os"
 	"os/user"
+	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -202,11 +203,11 @@ type StatOptions struct {
 // If root and directory are both not specified, the current root directory is
 // used, and relative names in the globs list are treated as being relative to
 // the current working directory.
-// If root is specified and the current OS supports it, the stat() is performed
-// in a chrooted context.  If the directory is specified as an absolute path,
-// it should either be the root directory or a subdirectory of the root
-// directory.  Otherwise, the directory is treated as a path relative to the
-// root directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the stat() is performed in a chrooted context.
+// If the directory is specified as an absolute path, it should either be the
+// root directory or a subdirectory of the root directory.  Otherwise, the
+// directory is treated as a path relative to the root directory.
 // Relative names in the glob list are treated as being relative to the
 // directory.
 func Stat(root string, directory string, options StatOptions, globs []string) ([]*StatsForGlob, error) {
@@ -229,18 +230,19 @@ func Stat(root string, directory string, options StatOptions, globs []string) ([
 
 // GetOptions controls parts of Get()'s behavior.
 type GetOptions struct {
-	UIDMap, GIDMap     []idtools.IDMap // map from hostIDs to containerIDs in the output archive
-	Excludes           []string        // contents to pretend don't exist, using the OS-specific path separator
-	ExpandArchives     bool            // extract the contents of named items that are archives
-	ChownDirs          *idtools.IDPair // set ownership on directories. no effect on archives being extracted
-	ChmodDirs          *os.FileMode    // set permissions on directories. no effect on archives being extracted
-	ChownFiles         *idtools.IDPair // set ownership of files. no effect on archives being extracted
-	ChmodFiles         *os.FileMode    // set permissions on files. no effect on archives being extracted
-	StripSetuidBit     bool            // strip the setuid bit off of items being copied. no effect on archives being extracted
-	StripSetgidBit     bool            // strip the setgid bit off of items being copied. no effect on archives being extracted
-	StripStickyBit     bool            // strip the sticky bit off of items being copied. no effect on archives being extracted
-	StripXattrs        bool            // don't record extended attributes of items being copied. no effect on archives being extracted
-	KeepDirectoryNames bool            // don't strip the top directory's basename from the paths of items in subdirectories
+	UIDMap, GIDMap     []idtools.IDMap   // map from hostIDs to containerIDs in the output archive
+	Excludes           []string          // contents to pretend don't exist, using the OS-specific path separator
+	ExpandArchives     bool              // extract the contents of named items that are archives
+	ChownDirs          *idtools.IDPair   // set ownership on directories. no effect on archives being extracted
+	ChmodDirs          *os.FileMode      // set permissions on directories. no effect on archives being extracted
+	ChownFiles         *idtools.IDPair   // set ownership of files. no effect on archives being extracted
+	ChmodFiles         *os.FileMode      // set permissions on files. no effect on archives being extracted
+	StripSetuidBit     bool              // strip the setuid bit off of items being copied. no effect on archives being extracted
+	StripSetgidBit     bool              // strip the setgid bit off of items being copied. no effect on archives being extracted
+	StripStickyBit     bool              // strip the sticky bit off of items being copied. no effect on archives being extracted
+	StripXattrs        bool              // don't record extended attributes of items being copied. no effect on archives being extracted
+	KeepDirectoryNames bool              // don't strip the top directory's basename from the paths of items in subdirectories
+	Rename             map[string]string // rename items with the specified names, or under the specified names
 }
 
 // Get produces an archive containing items that match the specified glob
@@ -248,11 +250,11 @@ type GetOptions struct {
 // If root and directory are both not specified, the current root directory is
 // used, and relative names in the globs list are treated as being relative to
 // the current working directory.
-// If root is specified and the current OS supports it, the contents are read
-// in a chrooted context.  If the directory is specified as an absolute path,
-// it should either be the root directory or a subdirectory of the root
-// directory.  Otherwise, the directory is treated as a path relative to the
-// root directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the contents are read in a chrooted context.
+// If the directory is specified as an absolute path, it should either be the
+// root directory or a subdirectory of the root directory.  Otherwise, the
+// directory is treated as a path relative to the root directory.
 // Relative names in the glob list are treated as being relative to the
 // directory.
 func Get(root string, directory string, options GetOptions, globs []string, bulkWriter io.Writer) error {
@@ -278,25 +280,28 @@ func Get(root string, directory string, options GetOptions, globs []string, bulk
 
 // PutOptions controls parts of Put()'s behavior.
 type PutOptions struct {
-	UIDMap, GIDMap    []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk
-	DefaultDirOwner   *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
-	DefaultDirMode    *os.FileMode    // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
-	ChownDirs         *idtools.IDPair // set ownership of newly-created directories
-	ChmodDirs         *os.FileMode    // set permissions on newly-created directories
-	ChownFiles        *idtools.IDPair // set ownership of newly-created files
-	ChmodFiles        *os.FileMode    // set permissions on newly-created files
-	StripXattrs       bool            // don't bother trying to set extended attributes of items being copied
-	IgnoreXattrErrors bool            // ignore any errors encountered when attempting to set extended attributes
+	UIDMap, GIDMap       []idtools.IDMap   // map from containerIDs to hostIDs when writing contents to disk
+	DefaultDirOwner      *idtools.IDPair   // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
+	DefaultDirMode       *os.FileMode      // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
+	ChownDirs            *idtools.IDPair   // set ownership of newly-created directories
+	ChmodDirs            *os.FileMode      // set permissions on newly-created directories
+	ChownFiles           *idtools.IDPair   // set ownership of newly-created files
+	ChmodFiles           *os.FileMode      // set permissions on newly-created files
+	StripXattrs          bool              // don't bother trying to set extended attributes of items being copied
+	IgnoreXattrErrors    bool              // ignore any errors encountered when attempting to set extended attributes
+	NoOverwriteDirNonDir bool              // instead of quietly overwriting directories with non-directories, return an error
+	Rename               map[string]string // rename items with the specified names, or under the specified names
 }
 
 // Put extracts an archive from the bulkReader at the specified directory.
 // If root and directory are both not specified, the current root directory is
 // used.
-// If root is specified and the current OS supports it, the contents are written
-// in a chrooted context.  If the directory is specified as an absolute path,
-// it should either be the root directory or a subdirectory of the root
-// directory.  Otherwise, the directory is treated as a path relative to the
-// root directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the contents are written in a chrooted
+// context.  If the directory is specified as an absolute path, it should
+// either be the root directory or a subdirectory of the root directory.
+// Otherwise, the directory is treated as a path relative to the root
+// directory.
 func Put(root string, directory string, options PutOptions, bulkReader io.Reader) error {
 	req := request{
 		Request:    requestPut,
@@ -325,11 +330,12 @@ type MkdirOptions struct {
 // need to be created will be given the specified ownership and permissions.
 // If root and directory are both not specified, the current root directory is
 // used.
-// If root is specified and the current OS supports it, the directory is
-// created in a chrooted context.  If the directory is specified as an absolute
-// path, it should either be the root directory or a subdirectory of the root
-// directory.  Otherwise, the directory is treated as a path relative to the
-// root directory.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, the directory is created in a chrooted
+// context.  If the directory is specified as an absolute path, it should
+// either be the root directory or a subdirectory of the root directory.
+// Otherwise, the directory is treated as a path relative to the root
+// directory.
 func Mkdir(root string, directory string, options MkdirOptions) error {
 	req := request{
 		Request:      requestMkdir,
@@ -547,13 +553,13 @@ func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req reques
 		return nil, errors.Wrap(err, step)
 	}
 	if err = encoder.Encode(req); err != nil {
-		return killAndReturn(err, "error encoding request")
+		return killAndReturn(err, "error encoding request for copier subprocess")
 	}
 	if err = decoder.Decode(&resp); err != nil {
-		return killAndReturn(err, "error decoding response")
+		return killAndReturn(err, "error decoding response from copier subprocess")
 	}
 	if err = encoder.Encode(&request{Request: requestQuit}); err != nil {
-		return killAndReturn(err, "error encoding request")
+		return killAndReturn(err, "error encoding request for copier subprocess")
 	}
 	stdinWrite.Close()
 	stdinWrite = nil
@@ -626,7 +632,7 @@ func copierMain() {
 		// Read a request.
 		req := new(request)
 		if err := decoder.Decode(req); err != nil {
-			fmt.Fprintf(os.Stderr, "error decoding request: %v", err)
+			fmt.Fprintf(os.Stderr, "error decoding request from copier parent process: %v", err)
 			os.Exit(1)
 		}
 		if req.Request == requestQuit {
@@ -717,12 +723,12 @@ func copierMain() {
 		}
 		resp, cb, err := copierHandler(bulkReader, bulkWriter, *req)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "error handling request %#v: %v", *req, err)
+			fmt.Fprintf(os.Stderr, "error handling request %#v from copier parent process: %v", *req, err)
 			os.Exit(1)
 		}
 		// Encode the response.
 		if err := encoder.Encode(resp); err != nil {
-			fmt.Fprintf(os.Stderr, "error encoding response %#v: %v", *req, err)
+			fmt.Fprintf(os.Stderr, "error encoding response %#v for copier parent process: %v", *req, err)
 			os.Exit(1)
 		}
 		// If there's bulk data to transfer, run the callback to either
@@ -1118,6 +1124,34 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
 	return &response{Stat: statResponse.Stat, Get: getResponse{}}, cb, nil
 }
 
+func handleRename(rename map[string]string, name string) string {
+	if rename == nil {
+		return name
+	}
+	// header names always use '/', so use path instead of filepath to manipulate it
+	if directMapping, ok := rename[name]; ok {
+		return directMapping
+	}
+	prefix, remainder := path.Split(name)
+	for prefix != "" {
+		if mappedPrefix, ok := rename[prefix]; ok {
+			return path.Join(mappedPrefix, remainder)
+		}
+		if prefix[len(prefix)-1] == '/' {
+			if mappedPrefix, ok := rename[prefix[:len(prefix)-1]]; ok {
+				return path.Join(mappedPrefix, remainder)
+			}
+		}
+		newPrefix, middlePart := path.Split(prefix)
+		if newPrefix == prefix {
+			return name
+		}
+		prefix = newPrefix
+		remainder = path.Join(middlePart, remainder)
+	}
+	return name
+}
+
 func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath string, options GetOptions, tw *tar.Writer, hardlinkChecker *util.HardlinkChecker, idMappings *idtools.IDMappings) error {
 	// build the header using the name provided
 	hdr, err := tar.FileInfoHeader(srcfi, symlinkTarget)
@@ -1127,6 +1161,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
 	if name != "" {
 		hdr.Name = filepath.ToSlash(name)
 	}
+	if options.Rename != nil {
+		hdr.Name = handleRename(options.Rename, hdr.Name)
+	}
 	if options.StripSetuidBit {
 		hdr.Mode &^= cISUID
 	}
@@ -1164,6 +1201,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
 			tr := tar.NewReader(rc)
 			hdr, err := tr.Next()
 			for err == nil {
+				if options.Rename != nil {
+					hdr.Name = handleRename(options.Rename, hdr.Name)
+				}
 				if err = tw.WriteHeader(hdr); err != nil {
 					return errors.Wrapf(err, "error writing tar header from %q to pipe", contentPath)
 				}
@@ -1311,8 +1351,13 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
 	createFile := func(path string, tr *tar.Reader) (int64, error) {
 		f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600)
 		if err != nil && os.IsExist(err) {
-			if err = os.Remove(path); err != nil {
-				return 0, errors.Wrapf(err, "copier: put: error removing file to be overwritten %q", path)
+			if req.PutOptions.NoOverwriteDirNonDir {
+				if st, err2 := os.Lstat(path); err2 == nil && st.IsDir() {
+					return 0, errors.Wrapf(err, "copier: put: error creating file at %q", path)
+				}
+			}
+			if err = os.RemoveAll(path); err != nil {
+				return 0, errors.Wrapf(err, "copier: put: error removing item to be overwritten %q", path)
 			}
 			f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600)
 		}
@@ -1360,6 +1405,14 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
 		tr := tar.NewReader(bulkReader)
 		hdr, err := tr.Next()
 		for err == nil {
+			if len(hdr.Name) == 0 {
+				// no name -> ignore the entry
+				hdr, err = tr.Next()
+				continue
+			}
+			if req.PutOptions.Rename != nil {
+				hdr.Name = handleRename(req.PutOptions.Rename, hdr.Name)
+			}
 			// figure out who should own this new item
 			if idMappings != nil && !idMappings.Empty() {
 				containerPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
@@ -1412,35 +1465,70 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
 				}
 			case tar.TypeLink:
 				var linkTarget string
+				if req.PutOptions.Rename != nil {
+					hdr.Linkname = handleRename(req.PutOptions.Rename, hdr.Linkname)
+				}
 				if linkTarget, err = resolvePath(targetDirectory, filepath.Join(req.Root, filepath.FromSlash(hdr.Linkname)), nil); err != nil {
 					return errors.Errorf("error resolving hardlink target path %q under root %q", hdr.Linkname, req.Root)
 				}
 				if err = os.Link(linkTarget, path); err != nil && os.IsExist(err) {
+					if req.PutOptions.NoOverwriteDirNonDir {
+						if st, err := os.Lstat(path); err == nil && st.IsDir() {
+							break
+						}
+					}
 					if err = os.Remove(path); err == nil {
 						err = os.Link(linkTarget, path)
 					}
 				}
 			case tar.TypeSymlink:
+				// if req.PutOptions.Rename != nil {
+				//	todo: the general solution requires resolving to an absolute path, handling
+				//	renaming, and then possibly converting back to a relative symlink
+				// }
 				if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && os.IsExist(err) {
+					if req.PutOptions.NoOverwriteDirNonDir {
+						if st, err := os.Lstat(path); err == nil && st.IsDir() {
+							break
+						}
+					}
 					if err = os.Remove(path); err == nil {
 						err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path))
 					}
 				}
 			case tar.TypeChar:
 				if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) {
+					if req.PutOptions.NoOverwriteDirNonDir {
+						if st, err := os.Lstat(path); err == nil && st.IsDir() {
+							break
+						}
+					}
 					if err = os.Remove(path); err == nil {
 						err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor)))
 					}
 				}
 			case tar.TypeBlock:
 				if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) {
+					if req.PutOptions.NoOverwriteDirNonDir {
+						if st, err := os.Lstat(path); err == nil && st.IsDir() {
+							break
+						}
+					}
 					if err = os.Remove(path); err == nil {
 						err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor)))
 					}
 				}
 			case tar.TypeDir:
 				if err = os.Mkdir(path, 0700); err != nil && os.IsExist(err) {
-					err = nil
+					var st os.FileInfo
+					if st, err = os.Stat(path); err == nil && !st.IsDir() {
+						// it's not a directory, so remove it and mkdir
+						if err = os.Remove(path); err == nil {
+							err = os.Mkdir(path, 0700)
+						}
+					}
+					// either we removed it and retried, or it was a directory,
+					// in which case we want to just add the new stuff under it
 				}
 				// make a note of the directory's times.  we
 				// might create items under it, which will
@@ -1453,6 +1541,11 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
 				})
 			case tar.TypeFifo:
 				if err = mkfifo(path, 0600); err != nil && os.IsExist(err) {
+					if req.PutOptions.NoOverwriteDirNonDir {
+						if st, err := os.Lstat(path); err == nil && st.IsDir() {
+							break
+						}
+					}
 					if err = os.Remove(path); err == nil {
 						err = mkfifo(path, 0600)
 					}
diff --git a/vendor/github.com/containers/buildah/copier/syscall_unix.go b/vendor/github.com/containers/buildah/copier/syscall_unix.go
index 55f2f368a9..2c2806d0a3 100644
--- a/vendor/github.com/containers/buildah/copier/syscall_unix.go
+++ b/vendor/github.com/containers/buildah/copier/syscall_unix.go
@@ -10,7 +10,7 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-var canChroot = true
+var canChroot = os.Getuid() == 0
 
 func chroot(root string) (bool, error) {
 	if canChroot {
diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod
index 0d795f6b6a..ea9a956be1 100644
--- a/vendor/github.com/containers/buildah/go.mod
+++ b/vendor/github.com/containers/buildah/go.mod
@@ -5,10 +5,10 @@ go 1.12
 require (
 	github.com/containerd/containerd v1.4.1 // indirect
 	github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
-	github.com/containers/common v0.29.0
+	github.com/containers/common v0.31.0
 	github.com/containers/image/v5 v5.8.1
 	github.com/containers/ocicrypt v1.0.3
-	github.com/containers/storage v1.24.1
+	github.com/containers/storage v1.24.3
 	github.com/docker/distribution v2.7.1+incompatible
 	github.com/docker/go-units v0.4.0
 	github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316
@@ -21,7 +21,7 @@ require (
 	github.com/moby/sys/mount v0.1.1 // indirect
 	github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 // indirect
 	github.com/onsi/ginkgo v1.14.2
-	github.com/onsi/gomega v1.10.3
+	github.com/onsi/gomega v1.10.4
 	github.com/opencontainers/go-digest v1.0.0
 	github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
 	github.com/opencontainers/runc v1.0.0-rc91
diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum
index e3413bc68f..c2082c5ef2 100644
--- a/vendor/github.com/containers/buildah/go.sum
+++ b/vendor/github.com/containers/buildah/go.sum
@@ -73,8 +73,8 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
 github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
 github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc=
 github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ=
-github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
+github.com/containers/common v0.31.0 h1:SRnjfoqbjfaojpY9YJq9JBPEslwB5hoXJbaE+5zMFwM=
+github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
 github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q=
 github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ=
 github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
@@ -84,6 +84,8 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ
 github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI=
 github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc=
 github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
+github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E=
+github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -303,6 +305,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -482,6 +486,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
 golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md
index 119315d1f8..90e844c3eb 100644
--- a/vendor/github.com/containers/buildah/install.md
+++ b/vendor/github.com/containers/buildah/install.md
@@ -69,15 +69,35 @@ sudo apt-get update
 sudo apt-get -y install buildah
 ```
 
-The [Kubic project](https://build.opensuse.org/project/show/devel:kubic:libcontainers:stable)
-provides packages for Debian 10. The Kubic packages for Debian Testing/Bullseye and Debian Unstable/Sid
-have been discontinued to avoid
-[conflicts](https://github.com/containers/buildah/issues/2797) with the official packages.
+If you would prefer newer (though not as well-tested) packages,
+the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah)
+provides packages for Debian 10 and newer. The packages in Kubic project repos are more frequently
+updated than the one in Debian's official repositories, due to how Debian works.
+The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
+
+CAUTION: On Debian 11 and newer, including Testing and Sid/Unstable, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo
+OR the official Debian repos. Mixing and matching may lead to unpredictable situations including installation conflicts.
+
+```bash
+# Debian 10
+echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
+curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/Release.key | sudo apt-key add -
+sudo apt-get update
+sudo apt-get -y install buildah
+
+# Debian Testing
+echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
+curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/Release.key | sudo apt-key add -
+sudo apt-get update
+sudo apt-get -y install buildah
+
+# Debian Sid/Unstable
+echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
+curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/Release.key | sudo apt-key add -
+sudo apt-get update
+sudo apt-get -y install buildah
+```
 
-Caution: If you upgrade from Debian 10 to Testing/Bullseye or
-Unstable/Sid you would likely end up downgrading Buildah because the version in
-OBS is more frequently updated than the one in Debian's official repositories,
-due to how Debian works.
 
 
 ### [Fedora](https://www.fedoraproject.org)
@@ -125,7 +145,8 @@ sudo yum -y install buildah
 
 #### [Raspberry Pi OS armhf (ex Raspbian)](https://www.raspberrypi.org/downloads/raspberry-pi-os/)
 
-The Kubic project provides packages for Raspbian 10.
+The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides
+packages for Raspbian 10.
 
 ```bash
 # Raspbian 10
@@ -135,6 +156,8 @@ sudo apt-get update -qq
 sudo apt-get -qq -y install buildah
 ```
 
+The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
+
 #### [Raspberry Pi OS arm64 (beta)](https://downloads.raspberrypi.org/raspios_arm64/images/)
 
 Raspberry Pi OS use the standard Debian's repositories,
@@ -160,7 +183,16 @@ sudo apt-get -y update
 sudo apt-get -y install buildah
 ```
 
-The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides packages for some older but supported Ubuntu versions (it should also work with direct derivatives like Pop!\_OS).
+If you would prefer newer (though not as well-tested) packages,
+the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah)
+provides packages for active Ubuntu releases 18.04 and newer (it should also work with direct derivatives like Pop!\_OS).
+The packages in Kubic project repos are more frequently updated than the one in Ubuntu's official repositories, due to how Debian/Ubuntu works.
+Checkout the Kubic project page for a list of supported Ubuntu version and architecture combinations.
+The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
+
+CAUTION: On Ubuntu 20.10 and newer, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo
+OR the official Ubuntu repos. Mixing and matching may lead to unpredictable situations including installation conflicts.
+
 
 ```bash
 . /etc/os-release
@@ -473,6 +505,13 @@ cat /etc/containers/policy.json
 }
 ```
 
+## Debug with Delve and the like
+
+To make a source debug build without optimizations use `DEBUG=1`, like:
+```
+make all DEBUG=1
+```
+
 ## Vendoring
 
 Buildah uses Go Modules for vendoring purposes.  If you need to update or add a vendored package into Buildah, please follow this procedure:
diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go
index c1abb1cdbf..aab17fea27 100644
--- a/vendor/github.com/containers/buildah/new.go
+++ b/vendor/github.com/containers/buildah/new.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"math/rand"
-	"os"
 	"strings"
 
 	"github.com/containers/buildah/util"
@@ -127,27 +126,10 @@ func resolveLocalImage(systemContext *types.SystemContext, store storage.Store,
 	return nil, "", nil, nil
 }
 
-// getShortNameMode looks up the `CONTAINERS_SHORT_NAME_ALIASING` environment
-// variable.  If it's "on", return `nil` to use the defaults from
-// containers/image and the registries.conf files on the system.  If it's
-// "off", empty or unset, return types.ShortNameModeDisabled to turn off
-// short-name aliasing by default.
-//
-// TODO: remove this function once we want to default to short-name aliasing.
-func getShortNameMode() *types.ShortNameMode {
-	env := os.Getenv("CONTAINERS_SHORT_NAME_ALIASING")
-	if strings.ToLower(env) == "on" {
-		return nil // default to whatever registries.conf and c/image decide
-	}
-	mode := types.ShortNameModeDisabled
-	return &mode
-}
-
 func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) {
 	if systemContext == nil {
 		systemContext = &types.SystemContext{}
 	}
-	systemContext.ShortNameMode = getShortNameMode()
 
 	fromImage := options.FromImage
 	// If the image name includes a transport we can use it as it.  Special
diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go
index 123548d97b..1e2db58c4e 100644
--- a/vendor/github.com/containers/buildah/pkg/cli/common.go
+++ b/vendor/github.com/containers/buildah/pkg/cli/common.go
@@ -17,6 +17,7 @@ import (
 	"github.com/containers/common/pkg/auth"
 	commonComp "github.com/containers/common/pkg/completion"
 	"github.com/containers/common/pkg/config"
+	"github.com/containers/storage/pkg/unshare"
 	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
@@ -366,6 +367,9 @@ func DefaultIsolation() string {
 	if isolation != "" {
 		return isolation
 	}
+	if unshare.IsRootless() {
+		return "rootless"
+	}
 	return buildah.OCI
 }
 
diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go
index fb348b252d..f256e6c2a9 100644
--- a/vendor/github.com/containers/buildah/pkg/parse/parse.go
+++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go
@@ -486,7 +486,7 @@ func ValidateVolumeCtrDir(ctrDir string) error {
 
 // ValidateVolumeOpts validates a volume's options
 func ValidateVolumeOpts(options []string) ([]string, error) {
-	var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid int
+	var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown int
 	finalOpts := make([]string, 0, len(options))
 	for _, opt := range options {
 		switch opt {
@@ -515,6 +515,11 @@ func ValidateVolumeOpts(options []string) ([]string, error) {
 			if foundLabelChange > 1 {
 				return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", "))
 			}
+		case "U":
+			foundChown++
+			if foundChown > 1 {
+				return nil, errors.Errorf("invalid options %q, can only specify 1 'U' option", strings.Join(options, ", "))
+			}
 		case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable":
 			foundRootPropagation++
 			if foundRootPropagation > 1 {
@@ -878,20 +883,12 @@ func NamespaceOptions(c *cobra.Command) (namespaceOptions buildah.NamespaceOptio
 						logrus.Debugf("setting network to disabled")
 						break
 					}
-					if !filepath.IsAbs(how) {
-						options.AddOrReplace(buildah.NamespaceOption{
-							Name: what,
-							Path: how,
-						})
-						policy = buildah.NetworkEnabled
-						logrus.Debugf("setting network configuration to %q", how)
-						break
-					}
 				}
 				how = strings.TrimPrefix(how, "ns:")
 				if _, err := os.Stat(how); err != nil {
-					return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace at %q", what, how)
+					return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace", what)
 				}
+				policy = buildah.NetworkEnabled
 				logrus.Debugf("setting %q namespace to %q", what, how)
 				options.AddOrReplace(buildah.NamespaceOption{
 					Name: what,
diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go
index d20d39423a..dc2f5c5ada 100644
--- a/vendor/github.com/containers/buildah/run_linux.go
+++ b/vendor/github.com/containers/buildah/run_linux.go
@@ -506,8 +506,14 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
 		return err
 	}
 
+	// Get host UID and GID of the container process.
+	processUID, processGID, err := util.GetHostIDs(spec.Linux.UIDMappings, spec.Linux.GIDMappings, spec.Process.User.UID, spec.Process.User.GID)
+	if err != nil {
+		return err
+	}
+
 	// Get the list of explicitly-specified volume mounts.
-	volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID))
+	volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID), int(processUID), int(processGID))
 	if err != nil {
 		return err
 	}
@@ -1687,7 +1693,7 @@ func (b *Builder) cleanupTempVolumes() {
 	}
 }
 
-func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID int) (mounts []specs.Mount, Err error) {
+func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID, processUID, processGID int) (mounts []specs.Mount, Err error) {
 
 	// Make sure the overlay directory is clean before running
 	containerDir, err := b.store.ContainerDirectory(b.ContainerID)
@@ -1699,7 +1705,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
 	}
 
 	parseMount := func(mountType, host, container string, options []string) (specs.Mount, error) {
-		var foundrw, foundro, foundz, foundZ, foundO bool
+		var foundrw, foundro, foundz, foundZ, foundO, foundU bool
 		var rootProp string
 		for _, opt := range options {
 			switch opt {
@@ -1713,6 +1719,8 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
 				foundZ = true
 			case "O":
 				foundO = true
+			case "U":
+				foundU = true
 			case "private", "rprivate", "slave", "rslave", "shared", "rshared":
 				rootProp = opt
 			}
@@ -1730,6 +1738,11 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
 				return specs.Mount{}, err
 			}
 		}
+		if foundU {
+			if err := chownSourceVolume(host, processUID, processGID); err != nil {
+				return specs.Mount{}, err
+			}
+		}
 		if foundO {
 			containerDir, err := b.store.ContainerDirectory(b.ContainerID)
 			if err != nil {
@@ -1746,6 +1759,14 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
 
 				b.TempVolumes[contentDir] = true
 			}
+
+			// If chown true, add correct ownership to the overlay temp directories.
+			if foundU {
+				if err := chownSourceVolume(contentDir, processUID, processGID); err != nil {
+					return specs.Mount{}, err
+				}
+			}
+
 			return overlayMount, err
 		}
 		if rootProp == "" {
@@ -1789,6 +1810,39 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
 	return mounts, nil
 }
 
+// chownSourceVolume changes the ownership of a volume source directory or file within the host.
+func chownSourceVolume(path string, UID, GID int) error {
+	fi, err := os.Lstat(path)
+	if err != nil {
+		// Skip if path does not exist
+		if os.IsNotExist(err) {
+			logrus.Debugf("error returning file info of %q: %v", path, err)
+			return nil
+		}
+		return err
+	}
+
+	currentUID := int(fi.Sys().(*syscall.Stat_t).Uid)
+	currentGID := int(fi.Sys().(*syscall.Stat_t).Gid)
+
+	if UID != currentUID || GID != currentGID {
+		err := filepath.Walk(path, func(filePath string, f os.FileInfo, err error) error {
+			return os.Lchown(filePath, UID, GID)
+		})
+
+		if err != nil {
+			// Skip if path does not exist
+			if os.IsNotExist(err) {
+				logrus.Debugf("error changing the uid and gid of %q: %v", path, err)
+				return nil
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
 func setupMaskedPaths(g *generate.Generator) {
 	for _, mp := range []string{
 		"/proc/acpi",
diff --git a/vendor/github.com/containers/buildah/troubleshooting.md b/vendor/github.com/containers/buildah/troubleshooting.md
index afd9c640a5..02631ae134 100644
--- a/vendor/github.com/containers/buildah/troubleshooting.md
+++ b/vendor/github.com/containers/buildah/troubleshooting.md
@@ -154,5 +154,5 @@ Choose one of the following:
   * Complete the build operation as a privileged user.
   * Install and configure fuse-overlayfs.
     * Install the fuse-overlayfs package for your Linux Distribution.
-    * Add `mount_program = "/usr/bin/fuse-overlayfs` under `[storage.options]` in your `~/.config/containers/storage.conf` file.
+    * Add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in your `~/.config/containers/storage.conf` file.
 ---
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 26b782b85b..4ef7fccd12 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -67,7 +67,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr
 github.com/containernetworking/plugins/pkg/utils/sysctl
 github.com/containernetworking/plugins/plugins/ipam/host-local/backend
 github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator
-# github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c
+# github.com/containers/buildah v1.18.1-0.20201217112226-67470615779c
 github.com/containers/buildah
 github.com/containers/buildah/bind
 github.com/containers/buildah/chroot