summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/volumes.go36
-rw-r--r--cmd/podman/containers/cp.go5
-rw-r--r--docs/source/markdown/podman-build.1.md10
-rw-r--r--go.mod4
-rw-r--r--go.sum10
-rw-r--r--libpod/container.go6
-rw-r--r--libpod/container_api.go64
-rw-r--r--libpod/container_copy_linux.go266
-rw-r--r--libpod/container_copy_unsupported.go16
-rw-r--r--libpod/container_internal.go20
-rw-r--r--libpod/container_path_resolution.go23
-rw-r--r--libpod/container_stat_linux.go157
-rw-r--r--libpod/container_stat_unsupported.go13
-rw-r--r--libpod/define/fileinfo.go16
-rw-r--r--libpod/define/mount.go12
-rw-r--r--pkg/api/handlers/compat/containers_stop.go4
-rw-r--r--pkg/copy/fileinfo.go11
-rw-r--r--pkg/domain/entities/containers.go3
-rw-r--r--pkg/domain/infra/abi/archive.go163
-rw-r--r--pkg/domain/infra/abi/containers_stat.go127
-rw-r--r--pkg/domain/infra/abi/network.go10
-rw-r--r--pkg/specgen/generate/config_linux.go5
-rw-r--r--pkg/specgen/generate/oci.go2
-rw-r--r--pkg/specgen/generate/storage.go27
-rw-r--r--test/e2e/cp_test.go1
-rw-r--r--test/e2e/network_test.go23
-rw-r--r--test/e2e/stop_test.go4
-rw-r--r--test/system/065-cp.bats220
-rw-r--r--test/system/410-selinux.bats11
-rw-r--r--vendor/github.com/containers/buildah/add.go26
-rw-r--r--vendor/github.com/containers/buildah/buildah.go2
-rw-r--r--vendor/github.com/containers/buildah/copier/copier.go65
-rw-r--r--vendor/github.com/containers/buildah/copier/syscall_unix.go8
-rw-r--r--vendor/github.com/containers/buildah/pkg/overlay/overlay.go10
-rw-r--r--vendor/github.com/coreos/go-iptables/iptables/iptables.go112
-rw-r--r--vendor/modules.txt6
36 files changed, 1004 insertions, 494 deletions
diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go
index 19a49a6f2..aff323936 100644
--- a/cmd/podman/common/volumes.go
+++ b/cmd/podman/common/volumes.go
@@ -6,23 +6,13 @@ import (
"strings"
"github.com/containers/common/pkg/parse"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
-const (
- // TypeBind is the type for mounting host dir
- TypeBind = "bind"
- // TypeVolume is the type for named volumes
- TypeVolume = "volume"
- // TypeTmpfs is the type for mounting tmpfs
- TypeTmpfs = "tmpfs"
- // TypeDevpts is the type for creating a devpts
- TypeDevpts = "devpts"
-)
-
var (
errDuplicateDest = errors.Errorf("duplicate mount destination")
optionArgError = errors.Errorf("must provide an argument for option")
@@ -90,7 +80,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
}
unifiedMounts[dest] = spec.Mount{
Destination: dest,
- Type: TypeTmpfs,
+ Type: define.TypeTmpfs,
Source: "tmpfs",
Options: options,
}
@@ -131,7 +121,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
// Final step: maps to arrays
finalMounts := make([]spec.Mount, 0, len(unifiedMounts))
for _, mount := range unifiedMounts {
- if mount.Type == TypeBind {
+ if mount.Type == define.TypeBind {
absSrc, err := filepath.Abs(mount.Source)
if err != nil {
return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
@@ -194,7 +184,7 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N
return nil, nil, nil, err
}
switch mountType {
- case TypeBind:
+ case define.TypeBind:
mount, err := getBindMount(tokens)
if err != nil {
return nil, nil, nil, err
@@ -203,7 +193,7 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N
return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
}
finalMounts[mount.Destination] = mount
- case TypeTmpfs:
+ case define.TypeTmpfs:
mount, err := getTmpfsMount(tokens)
if err != nil {
return nil, nil, nil, err
@@ -212,7 +202,7 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N
return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
}
finalMounts[mount.Destination] = mount
- case TypeDevpts:
+ case define.TypeDevpts:
mount, err := getDevptsMount(tokens)
if err != nil {
return nil, nil, nil, err
@@ -250,7 +240,7 @@ func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.N
// Parse a single bind mount entry from the --mount flag.
func getBindMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
- Type: TypeBind,
+ Type: define.TypeBind,
}
var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
@@ -381,8 +371,8 @@ func getBindMount(args []string) (spec.Mount, error) {
// Parse a single tmpfs mount entry from the --mount flag
func getTmpfsMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
- Type: TypeTmpfs,
- Source: TypeTmpfs,
+ Type: define.TypeTmpfs,
+ Source: define.TypeTmpfs,
}
var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
@@ -460,8 +450,8 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
// Parse a single devpts mount entry from the --mount flag
func getDevptsMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
- Type: TypeDevpts,
- Source: TypeDevpts,
+ Type: define.TypeDevpts,
+ Source: define.TypeDevpts,
}
var setDest bool
@@ -630,9 +620,9 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
mount := spec.Mount{
Destination: filepath.Clean(destPath),
- Type: string(TypeTmpfs),
+ Type: string(define.TypeTmpfs),
Options: options,
- Source: string(TypeTmpfs),
+ Source: string(define.TypeTmpfs),
}
m[destPath] = mount
}
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index 7887e9539..7a62d982c 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -189,8 +189,9 @@ func copyFromContainer(container string, containerPath string, hostPath string)
}
putOptions := buildahCopiah.PutOptions{
- ChownDirs: &idPair,
- ChownFiles: &idPair,
+ ChownDirs: &idPair,
+ ChownFiles: &idPair,
+ IgnoreDevices: true,
}
if !containerInfo.IsDir && (!hostInfo.IsDir || hostInfoErr != nil) {
// If we're having a file-to-file copy, make sure to
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index e05678e2c..84a624e53 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -821,9 +821,13 @@ $ podman build --no-cache --rm=false -t imageName .
### Building an multi-architecture image using a --manifest option (Requires emulation software)
-podman build --arch arm --manifest myimage /tmp/mysrc
-podman build --arch amd64 --manifest myimage /tmp/mysrc
-podman build --arch s390x --manifest myimage /tmp/mysrc
+```
+$ podman build --arch arm --manifest myimage /tmp/mysrc
+
+$ podman build --arch amd64 --manifest myimage /tmp/mysrc
+
+$ podman build --arch s390x --manifest myimage /tmp/mysrc
+```
### Building an image using a URL, Git repo, or archive
diff --git a/go.mod b/go.mod
index 1972eb2d2..e01d2d672 100644
--- a/go.mod
+++ b/go.mod
@@ -10,8 +10,8 @@ require (
github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/containernetworking/cni v0.8.1
- github.com/containernetworking/plugins v0.9.0
- github.com/containers/buildah v1.19.6
+ github.com/containernetworking/plugins v0.9.1
+ github.com/containers/buildah v1.19.7
github.com/containers/common v0.35.0
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.10.2
diff --git a/go.sum b/go.sum
index 5dc568719..cc2b4c90a 100644
--- a/go.sum
+++ b/go.sum
@@ -95,10 +95,10 @@ github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ
github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=
github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
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.19.6 h1:8mPysB7QzHxX9okR+Bwq/lsKAZA/FjDcqB+vebgwI1g=
-github.com/containers/buildah v1.19.6/go.mod h1:VnyHWgNmfR1d89/zJ/F4cbwOzaQS+6sBky46W7dCo3E=
+github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8=
+github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
+github.com/containers/buildah v1.19.7 h1:/g11GlhTo177xFex+5GHlF22hq01SyWaJuSA26UGFNU=
+github.com/containers/buildah v1.19.7/go.mod h1:VnyHWgNmfR1d89/zJ/F4cbwOzaQS+6sBky46W7dCo3E=
github.com/containers/common v0.33.4/go.mod h1:PhgL71XuC4jJ/1BIqeP7doke3aMFkCP90YBXwDeUr9g=
github.com/containers/common v0.35.0 h1:1OLZ2v+Tj/CN9BTQkKZ5VOriOiArJedinMMqfJRUI38=
github.com/containers/common v0.35.0/go.mod h1:gs1th7XFTOvVUl4LDPdQjOfOeNiVRDbQ7CNrZ0wS6F8=
@@ -124,6 +124,8 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
diff --git a/libpod/container.go b/libpod/container.go
index ee6e243ac..65abbfd5e 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -904,6 +904,12 @@ func (c *Container) NamespacePath(linuxNS LinuxNS) (string, error) { //nolint:in
}
}
+ return c.namespacePath(linuxNS)
+}
+
+// namespacePath returns the path of one of the container's namespaces
+// If the container is not running, an error will be returned
+func (c *Container) namespacePath(linuxNS LinuxNS) (string, error) { //nolint:interfacer
if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {
return "", errors.Wrapf(define.ErrCtrStopped, "cannot get namespace path unless container %s is running", c.ID())
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 2818ac841..6fa8b27cd 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -2,6 +2,7 @@ package libpod
import (
"context"
+ "io"
"io/ioutil"
"net/http"
"os"
@@ -349,10 +350,6 @@ func (c *Container) Mount() (string, error) {
}
}
- if c.state.State == define.ContainerStateRemoving {
- return "", errors.Wrapf(define.ErrCtrStateInvalid, "cannot mount container %s as it is being removed", c.ID())
- }
-
defer c.newContainerEvent(events.Mount)
return c.mount()
}
@@ -367,7 +364,6 @@ func (c *Container) Unmount(force bool) error {
return err
}
}
-
if c.state.Mounted {
mounted, err := c.runtime.storageService.MountedContainerImage(c.ID())
if err != nil {
@@ -847,31 +843,59 @@ func (c *Container) ShouldRestart(ctx context.Context) bool {
return c.shouldRestart()
}
-// ResolvePath resolves the specified path on the root for the container. The
-// root must either be the mounted image of the container or the already
-// mounted container storage.
-//
-// It returns the resolved root and the resolved path. Note that the path may
-// resolve to the container's mount point or to a volume or bind mount.
-func (c *Container) ResolvePath(ctx context.Context, root string, path string) (string, string, error) {
- logrus.Debugf("Resolving path %q (root %q) on container %s", path, root, c.ID())
+// CopyFromArchive copies the contents from the specified tarStream to path
+// *inside* the container.
+func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, tarStream io.Reader) (func() error, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
- // Minimal sanity checks.
- if len(root)*len(path) == 0 {
- return "", "", errors.Wrapf(define.ErrInternal, "ResolvePath: root (%q) and path (%q) must be non empty", root, path)
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
}
- if _, err := os.Stat(root); err != nil {
- return "", "", errors.Wrapf(err, "cannot locate root to resolve path on container %s", c.ID())
+
+ return c.copyFromArchive(ctx, containerPath, tarStream)
+}
+
+// CopyToArchive copies the contents from the specified path *inside* the
+// container to the tarStream.
+func (c *Container) CopyToArchive(ctx context.Context, containerPath string, tarStream io.Writer) (func() error, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
}
+ return c.copyToArchive(ctx, containerPath, tarStream)
+}
+
+// Stat the specified path *inside* the container and return a file info.
+func (c *Container) Stat(ctx context.Context, containerPath string) (*define.FileInfo, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
- return "", "", err
+ return nil, err
+ }
+ }
+
+ var mountPoint string
+ var err error
+ if c.state.Mounted {
+ mountPoint = c.state.Mountpoint
+ } else {
+ mountPoint, err = c.mount()
+ if err != nil {
+ return nil, err
}
+ defer c.unmount(false)
}
- return c.resolvePath(root, path)
+ info, _, _, err := c.stat(ctx, mountPoint, containerPath)
+ return info, err
}
diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go
new file mode 100644
index 000000000..66ccd2f1f
--- /dev/null
+++ b/libpod/container_copy_linux.go
@@ -0,0 +1,266 @@
+// +build linux
+
+package libpod
+
+import (
+ "context"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ buildahCopiah "github.com/containers/buildah/copier"
+ "github.com/containers/buildah/pkg/chrootuser"
+ "github.com/containers/buildah/util"
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/docker/docker/pkg/archive"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+func (c *Container) copyFromArchive(ctx context.Context, path string, reader io.Reader) (func() error, error) {
+ var (
+ mountPoint string
+ resolvedRoot string
+ resolvedPath string
+ unmount func()
+ err error
+ )
+
+ // Make sure that "/" copies the *contents* of the mount point and not
+ // the directory.
+ if path == "/" {
+ path = "/."
+ }
+
+ // Optimization: only mount if the container is not already.
+ if c.state.Mounted {
+ mountPoint = c.state.Mountpoint
+ unmount = func() {}
+ } else {
+ // NOTE: make sure to unmount in error paths.
+ mountPoint, err = c.mount()
+ if err != nil {
+ return nil, err
+ }
+ unmount = func() { c.unmount(false) }
+ }
+
+ if c.state.State == define.ContainerStateRunning {
+ resolvedRoot = "/"
+ resolvedPath = c.pathAbs(path)
+ } else {
+ resolvedRoot, resolvedPath, err = c.resolvePath(mountPoint, path)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+ }
+
+ decompressed, err := archive.DecompressStream(reader)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ idMappings, idPair, err := getIDMappingsAndPair(c, mountPoint)
+ if err != nil {
+ decompressed.Close()
+ unmount()
+ return nil, err
+ }
+
+ logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID())
+
+ return func() error {
+ defer unmount()
+ defer decompressed.Close()
+ putOptions := buildahCopiah.PutOptions{
+ UIDMap: idMappings.UIDMap,
+ GIDMap: idMappings.GIDMap,
+ ChownDirs: idPair,
+ ChownFiles: idPair,
+ }
+
+ return c.joinMountAndExec(ctx,
+ func() error {
+ return buildahCopiah.Put(resolvedRoot, resolvedPath, putOptions, decompressed)
+ },
+ )
+ }, nil
+}
+
+func (c *Container) copyToArchive(ctx context.Context, path string, writer io.Writer) (func() error, error) {
+ var (
+ mountPoint string
+ unmount func()
+ err error
+ )
+
+ // Optimization: only mount if the container is not already.
+ if c.state.Mounted {
+ mountPoint = c.state.Mountpoint
+ unmount = func() {}
+ } else {
+ // NOTE: make sure to unmount in error paths.
+ mountPoint, err = c.mount()
+ if err != nil {
+ return nil, err
+ }
+ unmount = func() { c.unmount(false) }
+ }
+
+ statInfo, resolvedRoot, resolvedPath, err := c.stat(ctx, mountPoint, path)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ idMappings, idPair, err := getIDMappingsAndPair(c, mountPoint)
+ if err != nil {
+ unmount()
+ return nil, err
+ }
+
+ logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID())
+
+ return func() error {
+ defer unmount()
+ getOptions := buildahCopiah.GetOptions{
+ // Unless the specified points to ".", we want to copy the base directory.
+ KeepDirectoryNames: statInfo.IsDir && filepath.Base(path) != ".",
+ UIDMap: idMappings.UIDMap,
+ GIDMap: idMappings.GIDMap,
+ ChownDirs: idPair,
+ ChownFiles: idPair,
+ Excludes: []string{"dev", "proc", "sys"},
+ }
+ return c.joinMountAndExec(ctx,
+ func() error {
+ return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedPath}, writer)
+ },
+ )
+ }, nil
+}
+
+// getIDMappingsAndPair returns the ID mappings for the container and the host
+// ID pair.
+func getIDMappingsAndPair(container *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 *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
+}
+
+// joinMountAndExec executes the specified function `f` inside the container's
+// mount and PID namespace. That allows for having the exact view on the
+// container's file system.
+//
+// Note, if the container is not running `f()` will be executed as is.
+func (c *Container) joinMountAndExec(ctx context.Context, f func() error) error {
+ if c.state.State != define.ContainerStateRunning {
+ return f()
+ }
+
+ // Container's running, so we need to execute `f()` inside its mount NS.
+ errChan := make(chan error)
+ go func() {
+ runtime.LockOSThread()
+
+ // Join the mount and PID NS of the container.
+ getFD := func(ns LinuxNS) (*os.File, error) {
+ nsPath, err := c.namespacePath(ns)
+ if err != nil {
+ return nil, err
+ }
+ return os.Open(nsPath)
+ }
+
+ mountFD, err := getFD(MountNS)
+ if err != nil {
+ errChan <- err
+ return
+ }
+ defer mountFD.Close()
+
+ pidFD, err := getFD(PIDNS)
+ if err != nil {
+ errChan <- err
+ return
+ }
+ defer pidFD.Close()
+ if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
+ errChan <- err
+ return
+ }
+ if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil {
+ errChan <- err
+ return
+ }
+
+ if err := unix.Setns(int(mountFD.Fd()), unix.CLONE_NEWNS); err != nil {
+ errChan <- err
+ return
+ }
+
+ // Last but not least, execute the workload.
+ errChan <- f()
+ }()
+ return <-errChan
+}
diff --git a/libpod/container_copy_unsupported.go b/libpod/container_copy_unsupported.go
new file mode 100644
index 000000000..b2bdd3e3d
--- /dev/null
+++ b/libpod/container_copy_unsupported.go
@@ -0,0 +1,16 @@
+// +build !linux
+
+package libpod
+
+import (
+ "context"
+ "io"
+)
+
+func (c *Container) copyFromArchive(ctx context.Context, path string, reader io.Reader) (func() error, error) {
+ return nil, nil
+}
+
+func (c *Container) copyToArchive(ctx context.Context, path string, writer io.Writer) (func() error, error) {
+ return nil, nil
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 7e8226de4..af57aaca0 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1307,9 +1307,7 @@ func (c *Container) stop(timeout uint) error {
c.lock.Unlock()
}
- if err := c.ociRuntime.StopContainer(c, timeout, all); err != nil {
- return err
- }
+ stopErr := c.ociRuntime.StopContainer(c, timeout, all)
if !c.batched {
c.lock.Lock()
@@ -1318,13 +1316,23 @@ func (c *Container) stop(timeout uint) error {
// If the container has already been removed (e.g., via
// the cleanup process), there's nothing left to do.
case define.ErrNoSuchCtr, define.ErrCtrRemoved:
- return nil
+ return stopErr
default:
+ if stopErr != nil {
+ logrus.Errorf("Error syncing container %s status: %v", c.ID(), err)
+ return stopErr
+ }
return err
}
}
}
+ // We have to check stopErr *after* we lock again - otherwise, we have a
+ // change of panicing on a double-unlock. Ref: GH Issue 9615
+ if stopErr != nil {
+ return stopErr
+ }
+
// Since we're now subject to a race condition with other processes who
// may have altered the state (and other data), let's check if the
// state has changed. If so, we should return immediately and log a
@@ -2086,6 +2094,10 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (map[s
// mount mounts the container's root filesystem
func (c *Container) mount() (string, error) {
+ if c.state.State == define.ContainerStateRemoving {
+ return "", errors.Wrapf(define.ErrCtrStateInvalid, "cannot mount container %s as it is being removed", c.ID())
+ }
+
mountPoint, err := c.runtime.storageService.MountContainerImage(c.ID())
if err != nil {
return "", errors.Wrapf(err, "error mounting storage for container %s", c.ID())
diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go
index 5245314ae..d798963b1 100644
--- a/libpod/container_path_resolution.go
+++ b/libpod/container_path_resolution.go
@@ -1,3 +1,4 @@
+// +linux
package libpod
import (
@@ -10,6 +11,19 @@ import (
"github.com/sirupsen/logrus"
)
+// pathAbs returns an absolute path. If the specified path is
+// relative, it will be resolved relative to the container's working dir.
+func (c *Container) pathAbs(path string) string {
+ if !filepath.IsAbs(path) {
+ // 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.
+ path = filepath.Join(filepath.Join("/", c.WorkingDir()), path)
+ }
+ return path
+}
+
// 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.
@@ -20,14 +34,7 @@ import (
// the host).
func (c *Container) resolvePath(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("/", c.WorkingDir()), containerPath)
- }
+ pathRelativeToContainerMountPoint := c.pathAbs(containerPath)
resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint)
pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go
new file mode 100644
index 000000000..307b75c14
--- /dev/null
+++ b/libpod/container_stat_linux.go
@@ -0,0 +1,157 @@
+// +build linux
+
+package libpod
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/copier"
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/pkg/copy"
+ "github.com/pkg/errors"
+)
+
+// statInsideMount stats the specified path *inside* the container's mount and PID
+// namespace. It returns the file info along with the resolved root ("/") and
+// the resolved path (relative to the root).
+func (c *Container) statInsideMount(ctx context.Context, containerPath string) (*copier.StatForItem, string, string, error) {
+ resolvedRoot := "/"
+ resolvedPath := c.pathAbs(containerPath)
+ var statInfo *copier.StatForItem
+
+ err := c.joinMountAndExec(ctx,
+ func() error {
+ var statErr error
+ statInfo, statErr = secureStat(resolvedRoot, resolvedPath)
+ return statErr
+ },
+ )
+
+ return statInfo, resolvedRoot, resolvedPath, err
+}
+
+// statOnHost stats the specified path *on the host*. It returns the file info
+// along with the resolved root and the resolved path. Both paths are absolute
+// to the host's root. Note that the paths may resolved outside the
+// container's mount point (e.g., to a volume or bind mount).
+func (c *Container) statOnHost(ctx context.Context, mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) {
+ // Now resolve the container's path. It may hit a volume, it may hit a
+ // bind mount, it may be relative.
+ resolvedRoot, resolvedPath, err := c.resolvePath(mountPoint, containerPath)
+ if err != nil {
+ return nil, "", "", err
+ }
+
+ statInfo, err := secureStat(resolvedRoot, resolvedPath)
+ return statInfo, resolvedRoot, resolvedPath, err
+}
+
+func (c *Container) stat(ctx context.Context, containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) {
+ var (
+ resolvedRoot string
+ resolvedPath string
+ absContainerPath string
+ statInfo *copier.StatForItem
+ statErr error
+ )
+
+ // Make sure that "/" copies the *contents* of the mount point and not
+ // the directory.
+ if containerPath == "/" {
+ containerPath = "/."
+ }
+
+ if c.state.State == define.ContainerStateRunning {
+ // If the container is running, we need to join it's mount namespace
+ // and stat there.
+ statInfo, resolvedRoot, resolvedPath, statErr = c.statInsideMount(ctx, containerPath)
+ } else {
+ // If the container is NOT running, we need to resolve the path
+ // on the host.
+ statInfo, resolvedRoot, resolvedPath, statErr = c.statOnHost(ctx, containerMountPoint, containerPath)
+ }
+
+ if statErr != nil {
+ if statInfo == nil {
+ return nil, "", "", statErr
+ }
+ // 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 crucial for the remote client.
+ if os.IsNotExist(statErr) || strings.Contains(statErr.Error(), "o such file or directory") {
+ statErr = copy.ErrENOENT
+ }
+ }
+
+ if statInfo.IsSymlink {
+ // Evaluated symlinks are always relative to the container's mount point.
+ absContainerPath = statInfo.ImmediateTarget
+ } else if strings.HasPrefix(resolvedPath, containerMountPoint) {
+ // If the path is on the container's mount point, strip it off.
+ absContainerPath = strings.TrimPrefix(resolvedPath, 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
+ }
+
+ // Preserve 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)
+ resolvedPath = copy.PreserveBasePath(containerPath, resolvedPath)
+
+ info := &define.FileInfo{
+ IsDir: statInfo.IsDir,
+ Name: filepath.Base(absContainerPath),
+ Size: statInfo.Size,
+ Mode: statInfo.Mode,
+ ModTime: statInfo.ModTime,
+ LinkTarget: absContainerPath,
+ }
+
+ return info, resolvedRoot, resolvedPath, statErr
+}
+
+// secureStat extracts file info for path in a chroot'ed environment in root.
+func secureStat(root string, path string) (*copier.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 := copier.Stat(root, "", copier.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.ErrENOENT
+ }
+
+ var statErr error
+ if stat.Error != "" {
+ statErr = errors.New(stat.Error)
+ }
+ return stat, statErr
+}
diff --git a/libpod/container_stat_unsupported.go b/libpod/container_stat_unsupported.go
new file mode 100644
index 000000000..c002e4d32
--- /dev/null
+++ b/libpod/container_stat_unsupported.go
@@ -0,0 +1,13 @@
+// +build !linux
+
+package libpod
+
+import (
+ "context"
+
+ "github.com/containers/podman/v3/libpod/define"
+)
+
+func (c *Container) stat(ctx context.Context, containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) {
+ return nil, "", "", nil
+}
diff --git a/libpod/define/fileinfo.go b/libpod/define/fileinfo.go
new file mode 100644
index 000000000..2c7b6fe99
--- /dev/null
+++ b/libpod/define/fileinfo.go
@@ -0,0 +1,16 @@
+package define
+
+import (
+ "os"
+ "time"
+)
+
+// FileInfo describes the attributes of a file or diretory.
+type FileInfo struct {
+ Name string `json:"name"`
+ Size int64 `json:"size"`
+ Mode os.FileMode `json:"mode"`
+ ModTime time.Time `json:"mtime"`
+ IsDir bool `json:"isDir"`
+ LinkTarget string `json:"linkTarget"`
+}
diff --git a/libpod/define/mount.go b/libpod/define/mount.go
new file mode 100644
index 000000000..1b0d019c8
--- /dev/null
+++ b/libpod/define/mount.go
@@ -0,0 +1,12 @@
+package define
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "bind"
+ // TypeVolume is the type for named volumes
+ TypeVolume = "volume"
+ // TypeTmpfs is the type for mounting tmpfs
+ TypeTmpfs = "tmpfs"
+ // TypeDevpts is the type for creating a devpts
+ TypeDevpts = "devpts"
+)
diff --git a/pkg/api/handlers/compat/containers_stop.go b/pkg/api/handlers/compat/containers_stop.go
index 0526865b9..3ae223693 100644
--- a/pkg/api/handlers/compat/containers_stop.go
+++ b/pkg/api/handlers/compat/containers_stop.go
@@ -39,11 +39,11 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
Ignore: query.Ignore,
}
if utils.IsLibpodRequest(r) {
- if query.LibpodTimeout > 0 {
+ if _, found := r.URL.Query()["timeout"]; found {
options.Timeout = &query.LibpodTimeout
}
} else {
- if query.DockerTimeout > 0 {
+ if _, found := r.URL.Query()["t"]; found {
options.Timeout = &query.DockerTimeout
}
}
diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go
index b95bcd90c..fb711311c 100644
--- a/pkg/copy/fileinfo.go
+++ b/pkg/copy/fileinfo.go
@@ -7,8 +7,8 @@ import (
"os"
"path/filepath"
"strings"
- "time"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/pkg/errors"
)
@@ -22,14 +22,7 @@ var ErrENOENT = errors.New("No such file or directory")
// FileInfo describes a file or directory and is returned by
// (*CopyItem).Stat().
-type FileInfo struct {
- Name string `json:"name"`
- Size int64 `json:"size"`
- Mode os.FileMode `json:"mode"`
- ModTime time.Time `json:"mtime"`
- IsDir bool `json:"isDir"`
- LinkTarget string `json:"linkTarget"`
-}
+type FileInfo = define.FileInfo
// EncodeFileInfo serializes the specified FileInfo as a base64 encoded JSON
// payload. Intended for Docker compat.
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index ac965834a..7d074f89d 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -8,7 +8,6 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/copy"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/cri-o/ocicni/pkg/ocicni"
)
@@ -145,7 +144,7 @@ type ContainerInspectReport struct {
}
type ContainerStatReport struct {
- copy.FileInfo
+ define.FileInfo
}
type CommitOptions struct {
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go
index 528771ee7..2ea63aa5e 100644
--- a/pkg/domain/infra/abi/archive.go
+++ b/pkg/domain/infra/abi/archive.go
@@ -3,72 +3,16 @@ package abi
import (
"context"
"io"
- "path/filepath"
- "strings"
- buildahCopiah "github.com/containers/buildah/copier"
- "github.com/containers/buildah/pkg/chrootuser"
- "github.com/containers/buildah/util"
- "github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/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
}
-
- containerMountPoint, err := container.Mount()
- 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, containerMountPoint, 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
+ return container.CopyFromArchive(ctx, containerPath, reader)
}
func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) {
@@ -76,108 +20,5 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID
if err != nil {
return nil, err
}
-
- containerMountPoint, err := container.Mount()
- 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 = "/."
- }
-
- statInfo, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, 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 points to ".", we want to copy the base directory.
- KeepDirectoryNames: statInfo.IsDir && filepath.Base(containerPath) != ".",
- 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
+ return container.CopyToArchive(ctx, containerPath, writer)
}
diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go
index 1baeb9178..98a23c70b 100644
--- a/pkg/domain/infra/abi/containers_stat.go
+++ b/pkg/domain/infra/abi/containers_stat.go
@@ -2,139 +2,20 @@ package abi
import (
"context"
- "os"
- "path/filepath"
- "strings"
- buildahCopiah "github.com/containers/buildah/copier"
- "github.com/containers/podman/v3/libpod"
- "github.com/containers/podman/v3/pkg/copy"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
-func (ic *ContainerEngine) containerStat(container *libpod.Container, containerMountPoint string, containerPath string) (*entities.ContainerStatReport, string, string, error) {
- // 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 := container.ResolvePath(context.Background(), 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 crucial for the remote client.
- if os.IsNotExist(err) || strings.Contains(statInfoErr.Error(), "o such file or directory") {
- statInfoErr = copy.ErrENOENT
- }
- // 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 preserve 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
}
- containerMountPoint, err := container.Mount()
- 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, containerMountPoint, containerPath)
- return statReport, err
-}
-
-// 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.ErrENOENT
- }
+ info, err := container.Stat(ctx, containerPath)
- var statErr error
- if stat.Error != "" {
- statErr = errors.New(stat.Error)
+ if info != nil {
+ return &entities.ContainerStatReport{FileInfo: *info}, err
}
- return stat, statErr
+ return nil, err
}
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index 50a74032c..edde8ece6 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -96,7 +96,15 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
}
// We need to iterate containers looking to see if they belong to the given network
for _, c := range containers {
- if util.StringInSlice(name, c.Config().Networks) {
+ networks, _, err := c.Networks()
+ // if container vanished or network does not exist, go to next container
+ if errors.Is(err, define.ErrNoSuchNetwork) || errors.Is(err, define.ErrNoSuchCtr) {
+ continue
+ }
+ if err != nil {
+ return reports, err
+ }
+ if util.StringInSlice(name, networks) {
// if user passes force, we nuke containers and pods
if !options.Force {
// Without the force option, we return an error
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index 2792d0cb7..5c945cff3 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -37,7 +38,7 @@ func addPrivilegedDevices(g *generate.Generator) error {
for _, d := range hostDevices {
devMnt := spec.Mount{
Destination: d.Path,
- Type: TypeBind,
+ Type: define.TypeBind,
Source: d.Path,
Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"},
}
@@ -259,7 +260,7 @@ func addDevice(g *generate.Generator, device string) error {
}
devMnt := spec.Mount{
Destination: dst,
- Type: TypeBind,
+ Type: define.TypeBind,
Source: src,
Options: []string{"slave", "nosuid", "noexec", perm, "rbind"},
}
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index eb4dbc944..4eae09a5e 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -277,7 +277,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
g.RemoveMount("/proc")
procMount := spec.Mount{
Destination: "/proc",
- Type: TypeBind,
+ Type: define.TypeBind,
Source: "/proc",
Options: []string{"rbind", "nosuid", "noexec", "nodev"},
}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index 0bb1421f6..e135f4728 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
@@ -18,16 +19,6 @@ import (
"github.com/sirupsen/logrus"
)
-// TODO unify this in one place - maybe libpod/define
-const (
- // TypeBind is the type for mounting host dir
- TypeBind = "bind"
- // TypeVolume is the type for named volumes
- TypeVolume = "volume"
- // TypeTmpfs is the type for mounting tmpfs
- TypeTmpfs = "tmpfs"
-)
-
var (
errDuplicateDest = errors.Errorf("duplicate mount destination")
)
@@ -156,7 +147,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
// Final step: maps to arrays
finalMounts := make([]spec.Mount, 0, len(baseMounts))
for _, mount := range baseMounts {
- if mount.Type == TypeBind {
+ if mount.Type == define.TypeBind {
absSrc, err := filepath.Abs(mount.Source)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
@@ -208,8 +199,8 @@ func getImageVolumes(ctx context.Context, img *image.Image, s *specgen.SpecGener
case "tmpfs":
mount := spec.Mount{
Destination: cleanDest,
- Source: TypeTmpfs,
- Type: TypeTmpfs,
+ Source: define.TypeTmpfs,
+ Type: define.TypeTmpfs,
Options: []string{"rprivate", "rw", "nodev", "exec"},
}
mounts[cleanDest] = mount
@@ -277,7 +268,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
}
for _, mnt := range spec.Mounts {
- if mnt.Type != TypeBind {
+ if mnt.Type != define.TypeBind {
continue
}
if _, exists := userVolumes[mnt.Destination]; exists {
@@ -338,9 +329,9 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, error) {
mount := spec.Mount{
Destination: "/dev/init",
- Type: TypeBind,
+ Type: define.TypeBind,
Source: path,
- Options: []string{TypeBind, "ro"},
+ Options: []string{define.TypeBind, "ro"},
}
if path == "" {
@@ -393,13 +384,13 @@ func SupersedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M
func InitFSMounts(mounts []spec.Mount) error {
for i, m := range mounts {
switch {
- case m.Type == TypeBind:
+ case m.Type == define.TypeBind:
opts, err := util.ProcessOptions(m.Options, false, m.Source)
if err != nil {
return err
}
mounts[i].Options = opts
- case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
+ case m.Type == define.TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
opts, err := util.ProcessOptions(m.Options, true, "")
if err != nil {
return err
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index c0fb3f887..c0fb61544 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -212,6 +212,7 @@ var _ = Describe("Podman cp", func() {
// Copy the root dir "/" of a container to the host.
It("podman cp the root directory from the ctr to an existing directory on the host ", func() {
+ SkipIfRootless("cannot copy tty devices in rootless mode")
container := "copyroottohost"
session := podmanTest.RunTopContainer(container)
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index 53521cdc4..ff2e1eb66 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -352,6 +352,29 @@ var _ = Describe("Podman network", func() {
Expect(rmAll.ExitCode()).To(BeZero())
})
+ It("podman network remove after disconnect when container initially created with the network", func() {
+ SkipIfRootless("disconnect works only in non rootless container")
+
+ container := "test"
+ network := "foo"
+
+ session := podmanTest.Podman([]string{"network", "create", network})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "--name", container, "--network", network, "-d", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"network", "disconnect", network, container})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"network", "rm", network})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman network remove bogus", func() {
session := podmanTest.Podman([]string{"network", "rm", "bogus"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go
index dd264eb0d..d6d58c94c 100644
--- a/test/e2e/stop_test.go
+++ b/test/e2e/stop_test.go
@@ -150,7 +150,7 @@ var _ = Describe("Podman stop", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"stop", "--time", "1", "test4"})
+ session = podmanTest.Podman([]string{"stop", "--time", "0", "test4"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
output := session.OutputToString()
@@ -166,7 +166,7 @@ var _ = Describe("Podman stop", func() {
session := podmanTest.Podman([]string{"run", "-d", "--name", "test5", ALPINE, "sleep", "100"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"stop", "--timeout", "1", "test5"})
+ session = podmanTest.Podman([]string{"stop", "--timeout", "0", "test5"})
// Without timeout container stops in 10 seconds
// If not stopped in 5 seconds, then --timeout did not work
session.Wait(5)
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index 312106b36..88ed983d8 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -15,6 +15,7 @@ load helpers
random-1-$(random_string 15)
random-2-$(random_string 20)
)
+
echo "${randomcontent[0]}" > $srcdir/hostfile0
echo "${randomcontent[1]}" > $srcdir/hostfile1
echo "${randomcontent[2]}" > $srcdir/hostfile2
@@ -24,6 +25,10 @@ load helpers
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer mkdir /srv/subdir
+ # Commit the image for testing non-running containers
+ run_podman commit -q cpcontainer
+ cpimage="$output"
+
# format is: <id> | <destination arg to cp> | <full dest path> | <test name>
# where:
# id is 0-2, one of the random strings/files
@@ -44,8 +49,7 @@ load helpers
0 | subdir | /srv/subdir/hostfile0 | copy to workdir/subdir
"
- # Copy one of the files into container, exec+cat, confirm the file
- # is there and matches what we expect
+ # RUNNING container
while read id dest dest_fullname description; do
run_podman cp $srcdir/hostfile$id cpcontainer:$dest
run_podman exec cpcontainer cat $dest_fullname
@@ -67,6 +71,44 @@ load helpers
is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: No such file or directory' \
"copy into nonexistent path in container"
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+
+ # CREATED container
+ while read id dest dest_fullname description; do
+ run_podman create --name cpcontainer --workdir=/srv $cpimage sleep infinity
+ run_podman cp $srcdir/hostfile$id cpcontainer:$dest
+ run_podman start cpcontainer
+ run_podman exec cpcontainer cat $dest_fullname
+ is "$output" "${randomcontent[$id]}" "$description (cp -> ctr:$dest)"
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+ done < <(parse_table "$tests")
+
+ run_podman rmi -f $cpimage
+}
+
+@test "podman cp file from host to container tmpfs mount" {
+ srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr
+ mkdir -p $srcdir
+ content=tmpfile-content$(random_string 20)
+ echo $content > $srcdir/file
+
+ # RUNNING container
+ run_podman run -d --mount type=tmpfs,dst=/tmp --name cpcontainer $IMAGE sleep infinity
+ run_podman cp $srcdir/file cpcontainer:/tmp
+ run_podman exec cpcontainer cat /tmp/file
+ is "$output" "${content}" "cp to running container's tmpfs"
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+
+ # CREATED container (with copy up)
+ run_podman create --mount type=tmpfs,dst=/tmp --name cpcontainer $IMAGE sleep infinity
+ run_podman cp $srcdir/file cpcontainer:/tmp
+ run_podman start cpcontainer
+ run_podman exec cpcontainer cat /tmp/file
+ is "$output" "${content}" "cp to created container's tmpfs"
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
}
@@ -87,6 +129,10 @@ load helpers
run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1"
run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2"
+ # Commit the image for testing non-running containers
+ run_podman commit -q cpcontainer
+ cpimage="$output"
+
# format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name>
tests="
0 | /tmp/containerfile | | /containerfile | copy to srcdir/
@@ -98,20 +144,33 @@ load helpers
2 | subdir/containerfile2 | / | /containerfile2 | copy from workdir/subdir (rel path) to srcdir
"
- # Copy one of the files to the host, cat, confirm the file
- # is there and matches what we expect
+ # RUNNING container
while read id src dest dest_fullname description; do
# dest may be "''" for empty table cells
if [[ $dest == "''" ]];then
unset dest
fi
run_podman cp cpcontainer:$src "$srcdir$dest"
- run cat $srcdir$dest_fullname
- is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to \$srcdir$dest)"
- rm $srcdir/$dest_fullname
+ is "$(< $srcdir$dest_fullname)" "${randomcontent[$id]}" "$description (cp ctr:$src to \$srcdir$dest)"
+ rm $srcdir$dest_fullname
done < <(parse_table "$tests")
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+ # Created container
+ run_podman create --name cpcontainer --workdir=/srv $cpimage
+ while read id src dest dest_fullname description; do
+ # dest may be "''" for empty table cells
+ if [[ $dest == "''" ]];then
+ unset dest
+ fi
+ run_podman cp cpcontainer:$src "$srcdir$dest"
+ is "$(< $srcdir$dest_fullname)" "${randomcontent[$id]}" "$description (cp ctr:$src to \$srcdir$dest)"
+ rm $srcdir$dest_fullname
+ done < <(parse_table "$tests")
run_podman rm -f cpcontainer
+
+ run_podman rmi -f $cpimage
}
@@ -134,6 +193,10 @@ load helpers
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer mkdir /srv/subdir
+ # Commit the image for testing non-running containers
+ run_podman commit -q cpcontainer
+ cpimage="$output"
+
# format is: <source arg to cp (appended to srcdir)> | <destination arg to cp> | <full dest path> | <test name>
tests="
| / | /dir-test | copy to root
@@ -141,9 +204,10 @@ load helpers
/ | /tmp | /tmp/dir-test | copy to tmp
/. | /usr/ | /usr/ | copy contents of dir to usr/
| . | /srv/dir-test | copy to workdir (rel path)
- | subdir/. | /srv/subdir/dir-test | copy to workdir subdir (rel path)
+ | subdir/. | /srv/subdir/dir-test | copy to workdir subdir (rel path)
"
+ # RUNNING container
while read src dest dest_fullname description; do
# src may be "''" for empty table cells
if [[ $src == "''" ]];then
@@ -156,52 +220,97 @@ load helpers
run_podman exec cpcontainer cat $dest_fullname/hostfile1
is "$output" "${randomcontent[1]}" "$description (cp -> ctr:$dest)"
done < <(parse_table "$tests")
-
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
+
+ # CREATED container
+ while read src dest dest_fullname description; do
+ # src may be "''" for empty table cells
+ if [[ $src == "''" ]];then
+ unset src
+ fi
+ run_podman create --name cpcontainer --workdir=/srv $cpimage sleep infinity
+ run_podman cp $srcdir$src cpcontainer:$dest
+ run_podman start cpcontainer
+ run_podman exec cpcontainer cat $dest_fullname/hostfile0 $dest_fullname/hostfile1
+ is "${lines[0]}" "${randomcontent[0]}" "$description (cp -> ctr:$dest)"
+ is "${lines[1]}" "${randomcontent[1]}" "$description (cp -> ctr:$dest)"
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+ done < <(parse_table "$tests")
+
+ run_podman rmi -f $cpimage
}
@test "podman cp dir from container to host" {
- srcdir=$PODMAN_TMPDIR/dir-test
- mkdir -p $srcdir
+ destdir=$PODMAN_TMPDIR/cp-test-dir-ctr-to-host
+ mkdir -p $destdir
+ # Create 2 files with random content in the container.
+ local -a randomcontent=(
+ random-0-$(random_string 10)
+ random-1-$(random_string 15)
+ )
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
- run_podman exec cpcontainer sh -c 'mkdir /srv/subdir; echo "This first file is on the container" > /srv/subdir/containerfile1'
- run_podman exec cpcontainer sh -c 'echo "This second file is on the container as well" > /srv/subdir/containerfile2'
+ run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[0]} > /srv/subdir/containerfile0"
+ run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/subdir/containerfile1"
# "." and "dir/." will copy the contents, so make sure that a dir ending
# with dot is treated correctly.
run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./'
- run_podman cp cpcontainer:/srv $srcdir
- run cat $srcdir/srv/subdir/containerfile1
- is "$output" "This first file is on the container"
- run cat $srcdir/srv/subdir/containerfile2
- is "$output" "This second file is on the container as well"
- rm -rf $srcdir/srv/subdir
-
- run_podman cp cpcontainer:/srv/. $srcdir
- run ls $srcdir/subdir
- run cat $srcdir/subdir/containerfile1
- is "$output" "This first file is on the container"
- run cat $srcdir/subdir/containerfile2
- is "$output" "This second file is on the container as well"
- rm -rf $srcdir/subdir
-
- run_podman cp cpcontainer:/srv/subdir/. $srcdir
- run cat $srcdir/containerfile1
- is "$output" "This first file is on the container"
- run cat $srcdir/containerfile2
- is "$output" "This second file is on the container as well"
- rm -rf $srcdir/subdir
-
- run_podman cp cpcontainer:/tmp/subdir. $srcdir
- run cat $srcdir/subdir./containerfile1
- is "$output" "This first file is on the container"
- run cat $srcdir/subdir./containerfile2
- is "$output" "This second file is on the container as well"
- rm -rf $srcdir/subdir.
+ # Commit the image for testing non-running containers
+ run_podman commit -q cpcontainer
+ cpimage="$output"
+
+ # format is: <source arg to cp (appended to /srv)> | <full dest path> | <test name>
+ tests="
+ /srv | /srv/subdir | copy /srv
+ /srv/ | /srv/subdir | copy /srv/
+ /srv/. | /subdir | copy /srv/.
+ /srv/subdir/. | | copy /srv/subdir/.
+ /tmp/subdir. | /subdir. | copy /tmp/subdir.
+"
+
+ # RUNNING container
+ while read src dest_fullname description; do
+ if [[ $src == "''" ]];then
+ unset src
+ fi
+ if [[ $dest == "''" ]];then
+ unset dest
+ fi
+ if [[ $dest_fullname == "''" ]];then
+ unset dest_fullname
+ fi
+ run_podman cp cpcontainer:$src $destdir
+ is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description"
+ is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description"
+ rm -rf $destdir/*
+ done < <(parse_table "$tests")
+ run_podman kill cpcontainer
+ run_podman rm -f cpcontainer
+ # CREATED container
+ run_podman create --name cpcontainer --workdir=/srv $cpimage
+ while read src dest_fullname description; do
+ if [[ $src == "''" ]];then
+ unset src
+ fi
+ if [[ $dest == "''" ]];then
+ unset dest
+ fi
+ if [[ $dest_fullname == "''" ]];then
+ unset dest_fullname
+ fi
+ run_podman cp cpcontainer:$src $destdir
+ is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description"
+ is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description"
+ rm -rf $destdir/*
+ done < <(parse_table "$tests")
run_podman rm -f cpcontainer
+
+ run_podman rmi -f $cpimage
}
@@ -228,9 +337,7 @@ load helpers
run_podman create --name cpcontainer -v $volume1:/tmp/volume -v $volume2:/tmp/volume/sub-volume $IMAGE
run_podman cp $srcdir/hostfile cpcontainer:/tmp/volume/sub-volume
-
- run cat $volume2_mount/hostfile
- is "$output" "This file should be in volume2"
+ is "$(< $volume2_mount/hostfile)" "This file should be in volume2"
# Volume 1 must be empty.
run ls $volume1_mount
@@ -254,9 +361,7 @@ load helpers
run_podman create --name cpcontainer -v $volume:/tmp/volume -v $mountdir:/tmp/volume/mount $IMAGE
run_podman cp $srcdir/hostfile cpcontainer:/tmp/volume/mount
-
- run cat $mountdir/hostfile
- is "$output" "This file should be in the mount"
+ is "$(< $mountdir/hostfile)" "This file should be in the mount"
run_podman rm -f cpcontainer
run_podman volume rm $volume
@@ -284,7 +389,7 @@ load helpers
# cp no longer supports wildcarding
run_podman 125 cp 'cpcontainer:/tmp/*' $dstdir
- run_podman rm cpcontainer
+ run_podman rm -f cpcontainer
}
@@ -308,7 +413,7 @@ load helpers
# make sure there are no files in dstdir
is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host"
- run_podman rm cpcontainer
+ run_podman rm -f cpcontainer
}
@@ -332,7 +437,7 @@ load helpers
# make sure there are no files in dstdir
is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host"
- run_podman rm cpcontainer
+ run_podman rm -f cpcontainer
}
@@ -352,7 +457,7 @@ load helpers
# dstdir must be empty
is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host"
- run_podman rm cpcontainer
+ run_podman rm -f cpcontainer
}
@@ -409,6 +514,7 @@ load helpers
run_podman exec cpcontainer cat /tmp/d3/x
is "$output" "$rand_content3" "cp creates file named x"
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
}
@@ -446,6 +552,7 @@ load helpers
run_podman exec cpcontainer cat $graphroot/$rand_filename
is "$output" "$rand_content" "Contents of file copied into container"
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
}
@@ -494,6 +601,7 @@ load helpers
run_podman 125 cp - cpcontainer:/tmp/IdoNotExist < $tar_file
is "$output" 'Error: destination must be a directory when copying from stdin'
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
}
@@ -527,8 +635,7 @@ load helpers
fi
tar xvf $srcdir/stdout.tar -C $srcdir
- run cat $srcdir/file.txt
- is "$output" "$rand_content"
+ is "$(< $srcdir/file.txt)" "$rand_content"
run 1 ls $srcdir/empty.txt
rm -f $srcdir/*
@@ -539,11 +646,10 @@ load helpers
fi
tar xvf $srcdir/stdout.tar -C $srcdir
- run cat $srcdir/tmp/file.txt
- is "$output" "$rand_content"
- run cat $srcdir/tmp/empty.txt
- is "$output" ""
+ is "$(< $srcdir/tmp/file.txt)" "$rand_content"
+ is "$(< $srcdir/tmp/empty.txt)" ""
+ run_podman kill cpcontainer
run_podman rm -f cpcontainer
}
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 215b2832e..49743ff33 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -77,13 +77,14 @@ function check_label() {
@test "podman selinux: inspect kvm labels" {
skip_if_no_selinux
skip_if_remote "runtime flag is not passed over remote"
- if [ ! -e /usr/bin/kata-runtime ]; then
- skip "kata-runtime not available"
- fi
- run_podman create --runtime=kata --name myc $IMAGE
+ tmpdir=$PODMAN_TMPDIR/kata-test
+ mkdir -p $tmpdir
+ KATA=${tmpdir}/kata-runtime
+ ln -s /bin/true ${KATA}
+ run_podman create --runtime=${KATA} --name myc $IMAGE
run_podman inspect --format='{{ .ProcessLabel }}' myc
- is "$output" ".*container_kvm_t.*"
+ is "$output" ".*container_kvm_t"
}
# pr #6752
diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go
index 0903fc7db..cd466ccb3 100644
--- a/vendor/github.com/containers/buildah/add.go
+++ b/vendor/github.com/containers/buildah/add.go
@@ -324,13 +324,33 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
return errors.Wrapf(err, "error processing excludes list %v", options.Excludes)
}
- // Copy each source in turn.
+ // Make sure that, if it's a symlink, we'll chroot to the target of the link;
+ // knowing that target requires that we resolve it within the chroot.
+ evalOptions := copier.EvalOptions{}
+ evaluated, err := copier.Eval(mountPoint, extractDirectory, evalOptions)
+ if err != nil {
+ return errors.Wrapf(err, "error checking on destination %v", extractDirectory)
+ }
+ extractDirectory = evaluated
+
+ // Set up ID maps.
var srcUIDMap, srcGIDMap []idtools.IDMap
if options.IDMappingOptions != nil {
srcUIDMap, srcGIDMap = convertRuntimeIDMaps(options.IDMappingOptions.UIDMap, options.IDMappingOptions.GIDMap)
}
destUIDMap, destGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
+ // Create the target directory if it doesn't exist yet.
+ mkdirOptions := copier.MkdirOptions{
+ UIDMap: destUIDMap,
+ GIDMap: destGIDMap,
+ ChownNew: chownDirs,
+ }
+ if err := copier.Mkdir(mountPoint, extractDirectory, mkdirOptions); err != nil {
+ return errors.Wrapf(err, "error ensuring target directory exists")
+ }
+
+ // Copy each source in turn.
for _, src := range sources {
var multiErr *multierror.Error
var getErr, closeErr, renameErr, putErr error
@@ -363,7 +383,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
ChmodFiles: nil,
IgnoreDevices: rsystem.RunningInUserNS(),
}
- putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
+ putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
}
hashCloser.Close()
pipeReader.Close()
@@ -498,7 +518,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
ChmodFiles: nil,
IgnoreDevices: rsystem.RunningInUserNS(),
}
- putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
+ putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
}
hashCloser.Close()
pipeReader.Close()
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
index dd43ea99a..77d313c58 100644
--- a/vendor/github.com/containers/buildah/buildah.go
+++ b/vendor/github.com/containers/buildah/buildah.go
@@ -28,7 +28,7 @@ const (
Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec
// too.
- Version = "1.19.6"
+ Version = "1.19.7"
// The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to
diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go
index 63cdb1974..b5e107d4b 100644
--- a/vendor/github.com/containers/buildah/copier/copier.go
+++ b/vendor/github.com/containers/buildah/copier/copier.go
@@ -70,6 +70,7 @@ func isArchivePath(path string) bool {
type requestType string
const (
+ requestEval requestType = "EVAL"
requestStat requestType = "STAT"
requestGet requestType = "GET"
requestPut requestType = "PUT"
@@ -95,6 +96,8 @@ type request struct {
func (req *request) Excludes() []string {
switch req.Request {
+ case requestEval:
+ return nil
case requestStat:
return req.StatOptions.Excludes
case requestGet:
@@ -112,6 +115,8 @@ func (req *request) Excludes() []string {
func (req *request) UIDMap() []idtools.IDMap {
switch req.Request {
+ case requestEval:
+ return nil
case requestStat:
return nil
case requestGet:
@@ -129,6 +134,8 @@ func (req *request) UIDMap() []idtools.IDMap {
func (req *request) GIDMap() []idtools.IDMap {
switch req.Request {
+ case requestEval:
+ return nil
case requestStat:
return nil
case requestGet:
@@ -148,6 +155,7 @@ func (req *request) GIDMap() []idtools.IDMap {
type response struct {
Error string `json:",omitempty"`
Stat statResponse
+ Eval evalResponse
Get getResponse
Put putResponse
Mkdir mkdirResponse
@@ -158,6 +166,11 @@ type statResponse struct {
Globs []*StatsForGlob
}
+// evalResponse encodes a response for a single Eval request.
+type evalResponse struct {
+ Evaluated string
+}
+
// StatsForGlob encode results for a single glob pattern passed to Stat().
type StatsForGlob struct {
Error string `json:",omitempty"` // error if the Glob pattern was malformed
@@ -192,6 +205,33 @@ type putResponse struct {
type mkdirResponse struct {
}
+// EvalOptions controls parts of Eval()'s behavior.
+type EvalOptions struct {
+}
+
+// Eval evaluates the directory's path, including any intermediate symbolic
+// links.
+// If root is specified and the current OS supports it, and the calling process
+// has the necessary privileges, evaluation 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.
+func Eval(root string, directory string, options EvalOptions) (string, error) {
+ req := request{
+ Request: requestEval,
+ Root: root,
+ Directory: directory,
+ }
+ resp, err := copier(nil, nil, req)
+ if err != nil {
+ return "", err
+ }
+ if resp.Error != "" {
+ return "", errors.New(resp.Error)
+ }
+ return resp.Eval.Evaluated, nil
+}
+
// StatOptions controls parts of Stat()'s behavior.
type StatOptions struct {
CheckForArchives bool // check for and populate the IsArchive bit in returned values
@@ -243,6 +283,7 @@ type GetOptions struct {
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
+ NoDerefSymlinks bool // don't follow symlinks when globs match them
}
// Get produces an archive containing items that match the specified glob
@@ -557,6 +598,9 @@ func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req reques
return killAndReturn(err, "error encoding request for copier subprocess")
}
if err = decoder.Decode(&resp); err != nil {
+ if errors.Is(err, io.EOF) && errorBuffer.Len() > 0 {
+ return killAndReturn(errors.New(errorBuffer.String()), "error in copier subprocess")
+ }
return killAndReturn(err, "error decoding response from copier subprocess")
}
if err = encoder.Encode(&request{Request: requestQuit}); err != nil {
@@ -667,7 +711,7 @@ func copierMain() {
var err error
chrooted, err = chroot(req.Root)
if err != nil {
- fmt.Fprintf(os.Stderr, "error changing to intended-new-root directory %q: %v", req.Root, err)
+ fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
}
@@ -762,6 +806,9 @@ func copierHandler(bulkReader io.Reader, bulkWriter io.Writer, req request) (*re
switch req.Request {
default:
return nil, nil, errors.Errorf("not an implemented request type: %q", req.Request)
+ case requestEval:
+ resp := copierHandlerEval(req)
+ return resp, nil, nil
case requestStat:
resp := copierHandlerStat(req, pm)
return resp, nil, nil
@@ -870,6 +917,17 @@ func resolvePath(root, path string, pm *fileutils.PatternMatcher) (string, error
return workingPath, nil
}
+func copierHandlerEval(req request) *response {
+ errorResponse := func(fmtspec string, args ...interface{}) *response {
+ return &response{Error: fmt.Sprintf(fmtspec, args...), Eval: evalResponse{}}
+ }
+ resolvedTarget, err := resolvePath(req.Root, req.Directory, nil)
+ if err != nil {
+ return errorResponse("copier: eval: error resolving %q: %v", req.Directory, err)
+ }
+ return &response{Eval: evalResponse{Evaluated: filepath.Join(req.rootPrefix, resolvedTarget)}}
+}
+
func copierHandlerStat(req request, pm *fileutils.PatternMatcher) *response {
errorResponse := func(fmtspec string, args ...interface{}) *response {
return &response{Error: fmt.Sprintf(fmtspec, args...), Stat: statResponse{}}
@@ -1024,7 +1082,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
// chase links. if we hit a dead end, we should just fail
followedLinks := 0
const maxFollowedLinks = 16
- for info.Mode()&os.ModeType == os.ModeSymlink && followedLinks < maxFollowedLinks {
+ for !req.GetOptions.NoDerefSymlinks && info.Mode()&os.ModeType == os.ModeSymlink && followedLinks < maxFollowedLinks {
path, err := os.Readlink(item)
if err != nil {
continue
@@ -1139,7 +1197,8 @@ func handleRename(rename map[string]string, name string) string {
return path.Join(mappedPrefix, remainder)
}
if prefix[len(prefix)-1] == '/' {
- if mappedPrefix, ok := rename[prefix[:len(prefix)-1]]; ok {
+ prefix = prefix[:len(prefix)-1]
+ if mappedPrefix, ok := rename[prefix]; ok {
return path.Join(mappedPrefix, remainder)
}
}
diff --git a/vendor/github.com/containers/buildah/copier/syscall_unix.go b/vendor/github.com/containers/buildah/copier/syscall_unix.go
index 2c2806d0a..aa40f327c 100644
--- a/vendor/github.com/containers/buildah/copier/syscall_unix.go
+++ b/vendor/github.com/containers/buildah/copier/syscall_unix.go
@@ -3,10 +3,10 @@
package copier
import (
- "fmt"
"os"
"time"
+ "github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -15,13 +15,13 @@ var canChroot = os.Getuid() == 0
func chroot(root string) (bool, error) {
if canChroot {
if err := os.Chdir(root); err != nil {
- return false, fmt.Errorf("error changing to intended-new-root directory %q: %v", root, err)
+ return false, errors.Wrapf(err, "error changing to intended-new-root directory %q", root)
}
if err := unix.Chroot(root); err != nil {
- return false, fmt.Errorf("error chrooting to directory %q: %v", root, err)
+ return false, errors.Wrapf(err, "error chrooting to directory %q", root)
}
if err := os.Chdir(string(os.PathSeparator)); err != nil {
- return false, fmt.Errorf("error changing to just-became-root directory %q: %v", root, err)
+ return false, errors.Wrapf(err, "error changing to just-became-root directory %q", root)
}
return true, nil
}
diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
index a3e5866ee..462561983 100644
--- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
@@ -77,13 +77,11 @@ func mountHelper(contentDir, source, dest string, _, _ int, graphOptions []strin
// Read-write overlay mounts want a lower, upper and a work layer.
workDir := filepath.Join(contentDir, "work")
upperDir := filepath.Join(contentDir, "upper")
- st, err := os.Stat(dest)
- if err == nil {
- if err := os.Chmod(upperDir, st.Mode()); err != nil {
- return mount, err
- }
+ st, err := os.Stat(source)
+ if err != nil {
+ return mount, err
}
- if !os.IsNotExist(err) {
+ if err := os.Chmod(upperDir, st.Mode()); err != nil {
return mount, err
}
overlayOptions = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s,private", source, upperDir, workDir)
diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go
index 1074275b0..8d6f68906 100644
--- a/vendor/github.com/coreos/go-iptables/iptables/iptables.go
+++ b/vendor/github.com/coreos/go-iptables/iptables/iptables.go
@@ -31,7 +31,6 @@ type Error struct {
exec.ExitError
cmd exec.Cmd
msg string
- proto Protocol
exitStatus *int //for overriding
}
@@ -51,9 +50,8 @@ func (e *Error) IsNotExist() bool {
if e.ExitStatus() != 1 {
return false
}
- cmdIptables := getIptablesCommand(e.proto)
- msgNoRuleExist := fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", cmdIptables)
- msgNoChainExist := fmt.Sprintf("%s: No chain/target/match by that name.\n", cmdIptables)
+ msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n"
+ msgNoChainExist := "No chain/target/match by that name.\n"
return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist)
}
@@ -75,6 +73,7 @@ type IPTables struct {
v2 int
v3 int
mode string // the underlying iptables operating mode, e.g. nf_tables
+ timeout int // time to wait for the iptables lock, default waits forever
}
// Stat represents a structured statistic entry.
@@ -91,19 +90,42 @@ type Stat struct {
Options string `json:"options"`
}
-// New creates a new IPTables.
-// For backwards compatibility, this always uses IPv4, i.e. "iptables".
-func New() (*IPTables, error) {
- return NewWithProtocol(ProtocolIPv4)
+type option func(*IPTables)
+
+func IPFamily(proto Protocol) option {
+ return func(ipt *IPTables) {
+ ipt.proto = proto
+ }
}
-// New creates a new IPTables for the given proto.
-// The proto will determine which command is used, either "iptables" or "ip6tables".
-func NewWithProtocol(proto Protocol) (*IPTables, error) {
- path, err := exec.LookPath(getIptablesCommand(proto))
+func Timeout(timeout int) option {
+ return func(ipt *IPTables) {
+ ipt.timeout = timeout
+ }
+}
+
+// New creates a new IPTables configured with the options passed as parameter.
+// For backwards compatibility, by default always uses IPv4 and timeout 0.
+// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing
+// the IPFamily and Timeout options as follow:
+// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5))
+func New(opts ...option) (*IPTables, error) {
+
+ ipt := &IPTables{
+ proto: ProtocolIPv4,
+ timeout: 0,
+ }
+
+ for _, opt := range opts {
+ opt(ipt)
+ }
+
+ path, err := exec.LookPath(getIptablesCommand(ipt.proto))
if err != nil {
return nil, err
}
+ ipt.path = path
+
vstring, err := getIptablesVersionString(path)
if err != nil {
return nil, fmt.Errorf("could not get iptables version: %v", err)
@@ -112,21 +134,23 @@ func NewWithProtocol(proto Protocol) (*IPTables, error) {
if err != nil {
return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err)
}
+ ipt.v1 = v1
+ ipt.v2 = v2
+ ipt.v3 = v3
+ ipt.mode = mode
checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
+ ipt.hasCheck = checkPresent
+ ipt.hasWait = waitPresent
+ ipt.hasRandomFully = randomFullyPresent
- ipt := IPTables{
- path: path,
- proto: proto,
- hasCheck: checkPresent,
- hasWait: waitPresent,
- hasRandomFully: randomFullyPresent,
- v1: v1,
- v2: v2,
- v3: v3,
- mode: mode,
- }
- return &ipt, nil
+ return ipt, nil
+}
+
+// New creates a new IPTables for the given proto.
+// The proto will determine which command is used, either "iptables" or "ip6tables".
+func NewWithProtocol(proto Protocol) (*IPTables, error) {
+ return New(IPFamily(proto), Timeout(0))
}
// Proto returns the protocol used by this IPTables.
@@ -185,6 +209,14 @@ func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
return ipt.run(cmd...)
}
+func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error {
+ exists, err := ipt.Exists(table, chain, rulespec...)
+ if err == nil && exists {
+ err = ipt.Delete(table, chain, rulespec...)
+ }
+ return err
+}
+
// List rules in specified table/chain
func (ipt *IPTables) List(table, chain string) ([]string, error) {
args := []string{"-t", table, "-S", chain}
@@ -222,6 +254,21 @@ func (ipt *IPTables) ListChains(table string) ([]string, error) {
return chains, nil
}
+// '-S' is fine with non existing rule index as long as the chain exists
+// therefore pass index 1 to reduce overhead for large chains
+func (ipt *IPTables) ChainExists(table, chain string) (bool, error) {
+ err := ipt.run("-t", table, "-S", chain, "1")
+ eerr, eok := err.(*Error)
+ switch {
+ case err == nil:
+ return true, nil
+ case eok && eerr.ExitStatus() == 1:
+ return false, nil
+ default:
+ return false, err
+ }
+}
+
// Stats lists rules including the byte and packet counts
func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
@@ -401,6 +448,18 @@ func (ipt *IPTables) DeleteChain(table, chain string) error {
return ipt.run("-t", table, "-X", chain)
}
+func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error {
+ exists, err := ipt.ChainExists(table, chain)
+ if err != nil || !exists {
+ return err
+ }
+ err = ipt.run("-t", table, "-F", chain)
+ if err == nil {
+ err = ipt.run("-t", table, "-X", chain)
+ }
+ return err
+}
+
// ChangePolicy changes policy on chain to target
func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
return ipt.run("-t", table, "-P", chain, target)
@@ -428,6 +487,9 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
args = append([]string{ipt.path}, args...)
if ipt.hasWait {
args = append(args, "--wait")
+ if ipt.timeout != 0 {
+ args = append(args, strconv.Itoa(ipt.timeout))
+ }
} else {
fmu, err := newXtablesFileLock()
if err != nil {
@@ -452,7 +514,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
if err := cmd.Run(); err != nil {
switch e := err.(type) {
case *exec.ExitError:
- return &Error{*e, cmd, stderr.String(), ipt.proto, nil}
+ return &Error{*e, cmd, stderr.String(), nil}
default:
return err
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 1d192693d..875bc3f89 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -65,14 +65,14 @@ github.com/containernetworking/cni/pkg/types/020
github.com/containernetworking/cni/pkg/types/current
github.com/containernetworking/cni/pkg/utils
github.com/containernetworking/cni/pkg/version
-# github.com/containernetworking/plugins v0.9.0
+# github.com/containernetworking/plugins v0.9.1
github.com/containernetworking/plugins/pkg/ip
github.com/containernetworking/plugins/pkg/ns
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.19.6
+# github.com/containers/buildah v1.19.7
github.com/containers/buildah
github.com/containers/buildah/bind
github.com/containers/buildah/chroot
@@ -226,7 +226,7 @@ github.com/containers/storage/pkg/system
github.com/containers/storage/pkg/tarlog
github.com/containers/storage/pkg/truncindex
github.com/containers/storage/pkg/unshare
-# github.com/coreos/go-iptables v0.4.5
+# github.com/coreos/go-iptables v0.5.0
github.com/coreos/go-iptables/iptables
# github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
github.com/coreos/go-systemd/activation