diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 6 | ||||
-rw-r--r-- | libpod/container_api.go | 73 | ||||
-rw-r--r-- | libpod/container_copy_linux.go | 266 | ||||
-rw-r--r-- | libpod/container_copy_unsupported.go | 16 | ||||
-rw-r--r-- | libpod/container_internal.go | 49 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 5 | ||||
-rw-r--r-- | libpod/container_path_resolution.go | 23 | ||||
-rw-r--r-- | libpod/container_stat_linux.go | 157 | ||||
-rw-r--r-- | libpod/container_stat_unsupported.go | 13 | ||||
-rw-r--r-- | libpod/define/fileinfo.go | 16 | ||||
-rw-r--r-- | libpod/define/mount.go | 12 | ||||
-rw-r--r-- | libpod/image/image.go | 27 | ||||
-rw-r--r-- | libpod/image/pull.go | 13 | ||||
-rw-r--r-- | libpod/kube.go | 12 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 9 | ||||
-rw-r--r-- | libpod/storage.go | 5 |
16 files changed, 580 insertions, 122 deletions
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..4ccb240e7 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" @@ -11,7 +12,6 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/signal" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -27,10 +27,6 @@ import ( // containers). The `recursive` parameter will, if set to true, start these // dependency containers before initializing this container. func (c *Container) Init(ctx context.Context, recursive bool) error { - span, _ := opentracing.StartSpanFromContext(ctx, "containerInit") - span.SetTag("struct", "container") - defer span.Finish() - if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -83,10 +79,6 @@ func (c *Container) Init(ctx context.Context, recursive bool) error { // running before being run. The recursive parameter, if set, will start all // dependencies before starting this container. func (c *Container) Start(ctx context.Context, recursive bool) error { - span, _ := opentracing.StartSpanFromContext(ctx, "containerStart") - span.SetTag("struct", "container") - defer span.Finish() - if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -349,10 +341,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 +355,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 +834,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..1614211fb 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -33,7 +33,6 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -399,10 +398,6 @@ func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) { // Create container root filesystem for use func (c *Container) setupStorage(ctx context.Context) error { - span, _ := opentracing.StartSpanFromContext(ctx, "setupStorage") - span.SetTag("type", "container") - defer span.Finish() - if !c.valid { return errors.Wrapf(define.ErrCtrRemoved, "container %s is not valid", c.ID()) } @@ -1035,10 +1030,6 @@ func (c *Container) cniHosts() string { // Initialize a container, creating it in the runtime func (c *Container) init(ctx context.Context, retainRetries bool) error { - span, _ := opentracing.StartSpanFromContext(ctx, "init") - span.SetTag("struct", "container") - defer span.Finish() - // Unconditionally remove conmon temporary files. // We've been running into far too many issues where they block startup. if err := c.removeConmonFiles(); err != nil { @@ -1111,10 +1102,6 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { // Deletes the container in the runtime, and resets its state to Exited. // The container can be restarted cleanly after this. func (c *Container) cleanupRuntime(ctx context.Context) error { - span, _ := opentracing.StartSpanFromContext(ctx, "cleanupRuntime") - span.SetTag("struct", "container") - defer span.Finish() - // If the container is not ContainerStateStopped or // ContainerStateCreated, do nothing. if !c.ensureState(define.ContainerStateStopped, define.ContainerStateCreated) { @@ -1156,10 +1143,6 @@ func (c *Container) cleanupRuntime(ctx context.Context) error { // Not necessary for ContainerStateExited - the container has already been // removed from the runtime, so init() can proceed freely. func (c *Container) reinit(ctx context.Context, retainRetries bool) error { - span, _ := opentracing.StartSpanFromContext(ctx, "reinit") - span.SetTag("struct", "container") - defer span.Finish() - logrus.Debugf("Recreating container %s in OCI runtime", c.ID()) if err := c.cleanupRuntime(ctx); err != nil { @@ -1307,9 +1290,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 +1299,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 @@ -1812,10 +1803,6 @@ func (c *Container) cleanupStorage() error { func (c *Container) cleanup(ctx context.Context) error { var lastError error - span, _ := opentracing.StartSpanFromContext(ctx, "cleanup") - span.SetTag("struct", "container") - defer span.Finish() - logrus.Debugf("Cleaning up container %s", c.ID()) // Remove healthcheck unit/timer file if it execs @@ -1876,10 +1863,6 @@ func (c *Container) cleanup(ctx context.Context) error { // delete deletes the container and runs any configured poststop // hooks. func (c *Container) delete(ctx context.Context) error { - span, _ := opentracing.StartSpanFromContext(ctx, "delete") - span.SetTag("struct", "container") - defer span.Finish() - if err := c.ociRuntime.DeleteContainer(c); err != nil { return errors.Wrapf(err, "error removing container %s from runtime", c.ID()) } @@ -1895,10 +1878,6 @@ func (c *Container) delete(ctx context.Context) error { // the OCI Runtime Specification (which requires them to run // post-delete, despite the stage name). func (c *Container) postDeleteHooks(ctx context.Context) error { - span, _ := opentracing.StartSpanFromContext(ctx, "postDeleteHooks") - span.SetTag("struct", "container") - defer span.Finish() - if c.state.ExtensionStageHooks != nil { extensionHooks, ok := c.state.ExtensionStageHooks["poststop"] if ok { @@ -2086,6 +2065,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_internal_linux.go b/libpod/container_internal_linux.go index 2684c2845..24319f4b5 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -49,7 +49,6 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -322,10 +321,6 @@ func (c *Container) getUserOverrides() *lookup.Overrides { // Generate spec for a container // Accepts a map of the container's dependencies func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "generateSpec") - span.SetTag("type", "container") - defer span.Finish() - overrides := c.getUserOverrides() execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides) if err != nil { 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/libpod/image/image.go b/libpod/image/image.go index 265178ad5..12dc22360 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -38,7 +38,6 @@ import ( "github.com/containers/storage" digest "github.com/opencontainers/go-digest" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -144,10 +143,6 @@ func (ir *Runtime) NewFromLocal(name string) (*Image, error) { // New creates a new image object where the image could be local // or remote func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, label *string, pullType util.PullType, progress chan types.ProgressProperties) (*Image, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "newImage") - span.SetTag("type", "runtime") - defer span.Finish() - // We don't know if the image is local or not ... check local first if pullType != util.PullImageAlways { newImage, err := ir.NewFromLocal(name) @@ -816,7 +811,7 @@ func (i *Image) UntagImage(tag string) error { // PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed. // Use PushImageToReference if the destination is known precisely. -func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged, progress chan types.ProgressProperties) error { if destination == "" { return errors.Wrapf(syscall.EINVAL, "destination image name must be specified") } @@ -834,11 +829,11 @@ func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination return err } } - return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) + return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags, progress) } // PushImageToReference pushes the given image to a location described by the given path -func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged, progress chan types.ProgressProperties) error { sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress) sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache") @@ -859,6 +854,10 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere } copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags) copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place. + if progress != nil { + copyOptions.Progress = progress + copyOptions.ProgressInterval = time.Second + } // Copy the image to the remote destination manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions) if err != nil { @@ -1293,21 +1292,11 @@ func (i *Image) inspect(ctx context.Context, calculateSize bool) (*inspect.Image // Inspect returns an image's inspect data func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "imageInspect") - - span.SetTag("type", "image") - defer span.Finish() - return i.inspect(ctx, true) } // InspectNoSize returns an image's inspect data without calculating the size for the image func (i *Image) InspectNoSize(ctx context.Context) (*inspect.ImageData, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "imageInspectNoSize") - - span.SetTag("type", "image") - defer span.Finish() - return i.inspect(ctx, false) } @@ -1648,7 +1637,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag return err } } - if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{RemoveSignatures: removeSignatures}, &DockerRegistryOptions{}, additionaltags); err != nil { + if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{RemoveSignatures: removeSignatures}, &DockerRegistryOptions{}, additionaltags, nil); err != nil { return errors.Wrapf(err, "unable to save %q", source) } i.newImageEvent(events.Save) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index c5fafc25d..58160b52f 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -23,7 +23,6 @@ import ( "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/registries" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -172,9 +171,6 @@ func (ir *Runtime) getPullRefPairsFromDockerArchiveReference(ctx context.Context // pullGoalFromImageReference returns a pull goal for a single ImageReference, depending on the used transport. // Note that callers are responsible for invoking (*pullGoal).cleanUp() to clean up possibly open resources. func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types.ImageReference, imgName string, sc *types.SystemContext) (*pullGoal, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "pullGoalFromImageReference") - defer span.Finish() - // supports pulling from docker-archive, oci, and registries switch srcRef.Transport().Name() { case DockerArchive: @@ -243,9 +239,6 @@ func toLocalImageName(imageName string) string { // pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries. // Use pullImageFromReference if the source is known precisely. func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions, label *string, progress chan types.ProgressProperties) ([]string, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "pullImageFromHeuristicSource") - defer span.Finish() - var goal *pullGoal sc := GetSystemContext(signaturePolicyPath, authfile, false) if dockerOptions != nil { @@ -281,9 +274,6 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s // pullImageFromReference pulls an image from a types.imageReference. func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.ImageReference, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions) ([]string, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "pullImageFromReference") - defer span.Finish() - sc := GetSystemContext(signaturePolicyPath, authfile, false) if dockerOptions != nil { sc.OSChoice = dockerOptions.OSChoice @@ -306,9 +296,6 @@ func cleanErrorMessage(err error) string { // doPullImage is an internal helper interpreting pullGoal. Almost everyone should call one of the callers of doPullImage instead. func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions, label *string, progress chan types.ProgressProperties) ([]string, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "doPullImage") - defer span.Finish() - policyContext, err := getPolicyContext(sc) if err != nil { return nil, err diff --git a/libpod/kube.go b/libpod/kube.go index 0c4f9f0a0..6feb69fea 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -676,8 +676,18 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { return nil, errors.Wrapf(err, "unable to sync container during YAML generation") } + mountpoint := c.state.Mountpoint + if mountpoint == "" { + var err error + mountpoint, err = c.mount() + if err != nil { + return nil, errors.Wrapf(err, "failed to mount %s mountpoint", c.ID()) + } + defer c.unmount(false) + } logrus.Debugf("Looking in container for user: %s", c.User()) - execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.User(), nil) + + execUser, err := lookup.GetUserGroupInfo(mountpoint, c.User(), nil) if err != nil { return nil, err } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 301c4627d..661ca7ff8 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -22,7 +22,6 @@ import ( "github.com/docker/go-units" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -179,10 +178,6 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf } func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) { - span, _ := opentracing.StartSpanFromContext(ctx, "newContainer") - span.SetTag("type", "runtime") - defer span.Finish() - ctr, err := r.initContainerVariables(rSpec, nil) if err != nil { return nil, errors.Wrapf(err, "error initializing container variables") @@ -472,10 +467,6 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, // infra container protections, and *not* remove from the database (as pod // remove will handle that). func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool) error { - span, _ := opentracing.StartSpanFromContext(ctx, "removeContainer") - span.SetTag("type", "runtime") - defer span.Finish() - if !c.valid { if ok, _ := r.state.HasContainer(c.ID()); !ok { // Container probably already removed diff --git a/libpod/storage.go b/libpod/storage.go index 418eb3151..4aa42dc8e 100644 --- a/libpod/storage.go +++ b/libpod/storage.go @@ -10,7 +10,6 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" v1 "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -67,10 +66,6 @@ func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) { // CreateContainerStorage creates the storage end of things. We already have the container spec created // TO-DO We should be passing in an Image object in the future. func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (_ ContainerInfo, retErr error) { - span, _ := opentracing.StartSpanFromContext(ctx, "createContainerStorage") - span.SetTag("type", "storageService") - defer span.Finish() - var imageConfig *v1.Image if imageName != "" { var ref types.ImageReference |