瀏覽代碼

Bump containerd to d97a907f

Signed-off-by: John Howard <jhoward@microsoft.com>
John Howard 6 年之前
父節點
當前提交
e57b2a8066
共有 70 個文件被更改,包括 3723 次插入1361 次删除
  1. 3 3
      vendor.conf
  2. 2 1
      vendor/github.com/containerd/containerd/README.md
  3. 2 2
      vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go
  4. 1 1
      vendor/github.com/containerd/containerd/api/services/events/v1/events.proto
  5. 9 0
      vendor/github.com/containerd/containerd/cio/io.go
  6. 0 58
      vendor/github.com/containerd/containerd/container_opts_unix.go
  7. 3 1
      vendor/github.com/containerd/containerd/content/local/store.go
  8. 4 4
      vendor/github.com/containerd/containerd/content/local/writer.go
  9. 1 1
      vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go
  10. 1 1
      vendor/github.com/containerd/containerd/events/exchange/exchange.go
  11. 57 0
      vendor/github.com/containerd/containerd/export.go
  12. 0 33
      vendor/github.com/containerd/containerd/import.go
  13. 26 15
      vendor/github.com/containerd/containerd/install.go
  14. 9 0
      vendor/github.com/containerd/containerd/install_opts.go
  15. 1 1
      vendor/github.com/containerd/containerd/metadata/bolt.go
  16. 1 1
      vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go
  17. 4 4
      vendor/github.com/containerd/containerd/metadata/buckets.go
  18. 1 1
      vendor/github.com/containerd/containerd/metadata/containers.go
  19. 6 11
      vendor/github.com/containerd/containerd/metadata/content.go
  20. 2 2
      vendor/github.com/containerd/containerd/metadata/db.go
  21. 1 1
      vendor/github.com/containerd/containerd/metadata/gc.go
  22. 1 1
      vendor/github.com/containerd/containerd/metadata/images.go
  23. 1 1
      vendor/github.com/containerd/containerd/metadata/leases.go
  24. 13 1
      vendor/github.com/containerd/containerd/metadata/migrations.go
  25. 1 1
      vendor/github.com/containerd/containerd/metadata/namespaces.go
  26. 1 1
      vendor/github.com/containerd/containerd/metadata/snapshot.go
  27. 4 0
      vendor/github.com/containerd/containerd/mount/mount_windows.go
  28. 212 6
      vendor/github.com/containerd/containerd/oci/spec.go
  29. 873 33
      vendor/github.com/containerd/containerd/oci/spec_opts.go
  30. 0 733
      vendor/github.com/containerd/containerd/oci/spec_opts_unix.go
  31. 0 89
      vendor/github.com/containerd/containerd/oci/spec_opts_windows.go
  32. 0 188
      vendor/github.com/containerd/containerd/oci/spec_unix.go
  33. 0 5
      vendor/github.com/containerd/containerd/platforms/defaults.go
  34. 6 26
      vendor/github.com/containerd/containerd/platforms/defaults_unix.go
  35. 10 10
      vendor/github.com/containerd/containerd/platforms/defaults_windows.go
  36. 1 1
      vendor/github.com/containerd/containerd/remotes/docker/fetcher.go
  37. 1 1
      vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go
  38. 53 2
      vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go
  39. 1 1
      vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go
  40. 4 4
      vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go
  41. 10 3
      vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go
  42. 0 1
      vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go
  43. 4 12
      vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go
  44. 2 3
      vendor/github.com/containerd/containerd/runtime/v1/linux/task.go
  45. 40 7
      vendor/github.com/containerd/containerd/runtime/v1/shim/service.go
  46. 1 1
      vendor/github.com/containerd/containerd/services/server/server.go
  47. 1 1
      vendor/github.com/containerd/containerd/sys/socket_unix.go
  48. 4 1
      vendor/github.com/containerd/containerd/task.go
  49. 62 0
      vendor/github.com/containerd/containerd/task_opts.go
  50. 19 10
      vendor/github.com/containerd/containerd/task_opts_unix.go
  51. 21 20
      vendor/github.com/containerd/containerd/vendor.conf
  52. 657 0
      vendor/github.com/containerd/continuity/context.go
  53. 88 0
      vendor/github.com/containerd/continuity/digests.go
  54. 113 0
      vendor/github.com/containerd/continuity/groups_unix.go
  55. 57 0
      vendor/github.com/containerd/continuity/hardlinks.go
  56. 36 0
      vendor/github.com/containerd/continuity/hardlinks_unix.go
  57. 12 0
      vendor/github.com/containerd/continuity/hardlinks_windows.go
  58. 47 0
      vendor/github.com/containerd/continuity/ioutils.go
  59. 144 0
      vendor/github.com/containerd/continuity/manifest.go
  60. 3 0
      vendor/github.com/containerd/continuity/proto/gen.go
  61. 181 0
      vendor/github.com/containerd/continuity/proto/manifest.pb.go
  62. 97 0
      vendor/github.com/containerd/continuity/proto/manifest.proto
  63. 574 0
      vendor/github.com/containerd/continuity/resource.go
  64. 37 0
      vendor/github.com/containerd/continuity/resource_unix.go
  65. 12 0
      vendor/github.com/containerd/continuity/resource_windows.go
  66. 2 0
      vendor/github.com/containerd/go-runc/console.go
  67. 44 55
      vendor/github.com/containerd/go-runc/io.go
  68. 76 0
      vendor/github.com/containerd/go-runc/io_unix.go
  69. 62 0
      vendor/github.com/containerd/go-runc/io_windows.go
  70. 1 2
      vendor/github.com/containerd/go-runc/runc.go

+ 3 - 3
vendor.conf

@@ -115,12 +115,12 @@ github.com/googleapis/gax-go v2.0.0
 google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9
 
 # containerd
-github.com/containerd/containerd v1.2.0-beta.2
+github.com/containerd/containerd d97a907f7f781c0ab8340877d8e6b53cc7f1c2f6
 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
-github.com/containerd/continuity c7c5070e6f6e090ab93b0a61eb921f2196fc3383
+github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537
 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
-github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031
+github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
 github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d
 github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef

+ 2 - 1
vendor/github.com/containerd/containerd/README.md

@@ -2,6 +2,7 @@
 
 [![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd)
 [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd)
+[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master)
 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield)
 [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd)
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271)
@@ -223,7 +224,7 @@ This will be the best place to discuss design and implementation.
 
 For sync communication we have a community slack with a #containerd channel that everyone is welcome to join and chat about development.
 
-**Slack:** https://dockr.ly/community
+**Slack:** https://join.slack.com/t/dockercommunity/shared_invite/enQtNDM4NjAwNDMyOTUwLWZlMDZmYWRjZjk4Zjc5ZGQ5NWZkOWI1Yjk2NGE3ZWVlYjYxM2VhYjczOWIyZDFhZTE3NTUwZWQzMjhmNGYyZTg
 
 ### Reporting security issues
 

+ 2 - 2
vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go

@@ -141,7 +141,7 @@ type EventsClient interface {
 	// Forward sends an event that has already been packaged into an envelope
 	// with a timestamp and namespace.
 	//
-	// This is useful if earlier timestamping is required or when fowarding on
+	// This is useful if earlier timestamping is required or when forwarding on
 	// behalf of another component, namespace or publisher.
 	Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error)
 	// Subscribe to a stream of events, possibly returning only that match any
@@ -223,7 +223,7 @@ type EventsServer interface {
 	// Forward sends an event that has already been packaged into an envelope
 	// with a timestamp and namespace.
 	//
-	// This is useful if earlier timestamping is required or when fowarding on
+	// This is useful if earlier timestamping is required or when forwarding on
 	// behalf of another component, namespace or publisher.
 	Forward(context.Context, *ForwardRequest) (*google_protobuf2.Empty, error)
 	// Subscribe to a stream of events, possibly returning only that match any

+ 1 - 1
vendor/github.com/containerd/containerd/api/services/events/v1/events.proto

@@ -20,7 +20,7 @@ service Events {
 	// Forward sends an event that has already been packaged into an envelope
 	// with a timestamp and namespace.
 	//
-	// This is useful if earlier timestamping is required or when fowarding on
+	// This is useful if earlier timestamping is required or when forwarding on
 	// behalf of another component, namespace or publisher.
 	rpc Forward(ForwardRequest) returns (google.protobuf.Empty);
 

+ 9 - 0
vendor/github.com/containerd/containerd/cio/io.go

@@ -141,6 +141,15 @@ func NewCreator(opts ...Opt) Creator {
 		if err != nil {
 			return nil, err
 		}
+		if streams.Stdin == nil {
+			fifos.Stdin = ""
+		}
+		if streams.Stdout == nil {
+			fifos.Stdout = ""
+		}
+		if streams.Stderr == nil {
+			fifos.Stderr = ""
+		}
 		return copyIO(fifos, streams)
 	}
 }

+ 0 - 58
vendor/github.com/containerd/containerd/container_opts_unix.go

@@ -20,25 +20,21 @@ package containerd
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
 	"syscall"
 
-	"github.com/containerd/containerd/api/types"
 	"github.com/containerd/containerd/containers"
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/mount"
 	"github.com/containerd/containerd/platforms"
-	"github.com/containerd/containerd/runtime/linux/runctypes"
 	"github.com/gogo/protobuf/proto"
 	protobuf "github.com/gogo/protobuf/types"
 	"github.com/opencontainers/image-spec/identity"
 	"github.com/opencontainers/image-spec/specs-go/v1"
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 )
 
@@ -105,44 +101,6 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts {
 	}
 }
 
-// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a
-// previous checkpoint. Additional software such as CRIU may be required to
-// restore a task from a checkpoint
-func WithTaskCheckpoint(im Image) NewTaskOpts {
-	return func(ctx context.Context, c *Client, info *TaskInfo) error {
-		desc := im.Target()
-		id := desc.Digest
-		index, err := decodeIndex(ctx, c.ContentStore(), desc)
-		if err != nil {
-			return err
-		}
-		for _, m := range index.Manifests {
-			if m.MediaType == images.MediaTypeContainerd1Checkpoint {
-				info.Checkpoint = &types.Descriptor{
-					MediaType: m.MediaType,
-					Size_:     m.Size,
-					Digest:    m.Digest,
-				}
-				return nil
-			}
-		}
-		return fmt.Errorf("checkpoint not found in index %s", id)
-	}
-}
-
-func decodeIndex(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (*v1.Index, error) {
-	var index v1.Index
-	p, err := content.ReadBlob(ctx, store, desc)
-	if err != nil {
-		return nil, err
-	}
-	if err := json.Unmarshal(p, &index); err != nil {
-		return nil, err
-	}
-
-	return &index, nil
-}
-
 // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
 // filesystem to be used by a container with user namespaces
 func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
@@ -221,19 +179,3 @@ func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc {
 		return os.Lchown(path, u, g)
 	}
 }
-
-// WithNoPivotRoot instructs the runtime not to you pivot_root
-func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error {
-	if info.Options == nil {
-		info.Options = &runctypes.CreateOptions{
-			NoPivotRoot: true,
-		}
-		return nil
-	}
-	copts, ok := info.Options.(*runctypes.CreateOptions)
-	if !ok {
-		return errors.New("invalid options type, expected runctypes.CreateOptions")
-	}
-	copts.NoPivotRoot = true
-	return nil
-}

+ 3 - 1
vendor/github.com/containerd/containerd/content/local/store.go

@@ -33,6 +33,8 @@ import (
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/filters"
 	"github.com/containerd/containerd/log"
+
+	"github.com/containerd/continuity"
 	digest "github.com/opencontainers/go-digest"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
@@ -651,5 +653,5 @@ func writeTimestampFile(p string, t time.Time) error {
 		return err
 	}
 
-	return ioutil.WriteFile(p, b, 0666)
+	return continuity.AtomicWriteFile(p, b, 0666)
 }

+ 4 - 4
vendor/github.com/containerd/containerd/content/local/writer.go

@@ -132,11 +132,11 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest,
 	// clean up!!
 	defer os.RemoveAll(w.path)
 
+	if _, err := os.Stat(target); err == nil {
+		// collision with the target file!
+		return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst)
+	}
 	if err := os.Rename(ingest, target); err != nil {
-		if os.IsExist(err) {
-			// collision with the target file!
-			return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst)
-		}
 		return err
 	}
 	commitTime := time.Now()

+ 1 - 1
vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go

@@ -30,7 +30,7 @@ import (
 )
 
 // WithProfile receives the name of a file stored on disk comprising a json
-// formated seccomp profile, as specified by the opencontainers/runtime-spec.
+// formatted seccomp profile, as specified by the opencontainers/runtime-spec.
 // The profile is read from the file, unmarshaled, and set to the spec.
 func WithProfile(profile string) oci.SpecOpts {
 	return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {

+ 1 - 1
vendor/github.com/containerd/containerd/events/exchange/exchange.go

@@ -52,7 +52,7 @@ var _ events.Subscriber = &Exchange{}
 
 // Forward accepts an envelope to be direcly distributed on the exchange.
 //
-// This is useful when an event is forwaded on behalf of another namespace or
+// This is useful when an event is forwarded on behalf of another namespace or
 // when the event is propagated on behalf of another publisher.
 func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err error) {
 	if err := validateEnvelope(envelope); err != nil {

+ 57 - 0
vendor/github.com/containerd/containerd/export.go

@@ -0,0 +1,57 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package containerd
+
+import (
+	"context"
+	"io"
+
+	"github.com/containerd/containerd/images"
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+type exportOpts struct {
+}
+
+// ExportOpt allows the caller to specify export-specific options
+type ExportOpt func(c *exportOpts) error
+
+func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) {
+	var eopts exportOpts
+	for _, o := range opts {
+		if err := o(&eopts); err != nil {
+			return eopts, err
+		}
+	}
+	return eopts, nil
+}
+
+// Export exports an image to a Tar stream.
+// OCI format is used by default.
+// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc.
+// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream.
+func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) {
+	_, err := resolveExportOpt(opts...) // unused now
+	if err != nil {
+		return nil, err
+	}
+	pr, pw := io.Pipe()
+	go func() {
+		pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw))
+	}()
+	return pr, nil
+}

+ 0 - 33
vendor/github.com/containerd/containerd/import.go

@@ -22,7 +22,6 @@ import (
 
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/images"
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
 type importOpts struct {
@@ -84,35 +83,3 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io
 	}
 	return images, nil
 }
-
-type exportOpts struct {
-}
-
-// ExportOpt allows the caller to specify export-specific options
-type ExportOpt func(c *exportOpts) error
-
-func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) {
-	var eopts exportOpts
-	for _, o := range opts {
-		if err := o(&eopts); err != nil {
-			return eopts, err
-		}
-	}
-	return eopts, nil
-}
-
-// Export exports an image to a Tar stream.
-// OCI format is used by default.
-// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc.
-// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream.
-func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) {
-	_, err := resolveExportOpt(opts...) // unused now
-	if err != nil {
-		return nil, err
-	}
-	pr, pw := io.Pipe()
-	go func() {
-		pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw))
-	}()
-	return pr, nil
-}

+ 26 - 15
vendor/github.com/containerd/containerd/install.go

@@ -33,25 +33,14 @@ import (
 
 // Install a binary image into the opt service
 func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error {
-	resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{
-		Filters: []string{
-			"id==opt",
-		},
-	})
-	if err != nil {
-		return err
-	}
-	if len(resp.Plugins) != 1 {
-		return errors.New("opt service not enabled")
-	}
-	path := resp.Plugins[0].Exports["path"]
-	if path == "" {
-		return errors.New("opt path not exported")
-	}
 	var config InstallConfig
 	for _, o := range opts {
 		o(&config)
 	}
+	path, err := c.getInstallPath(ctx, config)
+	if err != nil {
+		return err
+	}
 	var (
 		cs       = image.ContentStore()
 		platform = platforms.Default()
@@ -89,3 +78,25 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts)
 	}
 	return nil
 }
+
+func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (string, error) {
+	if config.Path != "" {
+		return config.Path, nil
+	}
+	resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{
+		Filters: []string{
+			"id==opt",
+		},
+	})
+	if err != nil {
+		return "", err
+	}
+	if len(resp.Plugins) != 1 {
+		return "", errors.New("opt service not enabled")
+	}
+	path := resp.Plugins[0].Exports["path"]
+	if path == "" {
+		return "", errors.New("opt path not exported")
+	}
+	return path, nil
+}

+ 9 - 0
vendor/github.com/containerd/containerd/install_opts.go

@@ -25,6 +25,8 @@ type InstallConfig struct {
 	Libs bool
 	// Replace will overwrite existing binaries or libs in the opt directory
 	Replace bool
+	// Path to install libs and binaries to
+	Path string
 }
 
 // WithInstallLibs installs libs from the image
@@ -36,3 +38,10 @@ func WithInstallLibs(c *InstallConfig) {
 func WithInstallReplace(c *InstallConfig) {
 	c.Replace = true
 }
+
+// WithInstallPath sets the optional install path
+func WithInstallPath(path string) InstallOpts {
+	return func(c *InstallConfig) {
+		c.Path = path
+	}
+}

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/bolt.go

@@ -19,8 +19,8 @@ package metadata
 import (
 	"context"
 
-	"github.com/boltdb/bolt"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type transactionKey struct{}

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go

@@ -19,8 +19,8 @@ package boltutil
 import (
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 var (

+ 4 - 4
vendor/github.com/containerd/containerd/metadata/buckets.go

@@ -17,11 +17,11 @@
 package metadata
 
 import (
-	"github.com/boltdb/bolt"
 	digest "github.com/opencontainers/go-digest"
+	bolt "go.etcd.io/bbolt"
 )
 
-// The layout where a "/" delineates a bucket is desribed in the following
+// The layout where a "/" delineates a bucket is described in the following
 // section. Please try to follow this as closely as possible when adding
 // functionality. We can bolster this with helpers and more structure if that
 // becomes an issue.
@@ -164,11 +164,11 @@ func getSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) *bolt.Buck
 }
 
 func createBlobBucket(tx *bolt.Tx, namespace string, dgst digest.Digest) (*bolt.Bucket, error) {
-	bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContent, bucketKeyObjectBlob, []byte(dgst.String()))
+	bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContent, bucketKeyObjectBlob)
 	if err != nil {
 		return nil, err
 	}
-	return bkt, nil
+	return bkt.CreateBucket([]byte(dgst.String()))
 }
 
 func getBlobsBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/containers.go

@@ -21,7 +21,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/containers"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/filters"
@@ -32,6 +31,7 @@ import (
 	"github.com/gogo/protobuf/proto"
 	"github.com/gogo/protobuf/types"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type containerStore struct {

+ 6 - 11
vendor/github.com/containerd/containerd/metadata/content.go

@@ -23,7 +23,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/filters"
@@ -34,6 +33,7 @@ import (
 	digest "github.com/opencontainers/go-digest"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type contentStore struct {
@@ -592,9 +592,6 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
 		}
 		size = nw.desc.Size
 		actual = nw.desc.Digest
-		if getBlobBucket(tx, nw.namespace, actual) != nil {
-			return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
-		}
 	} else {
 		status, err := nw.w.Status()
 		if err != nil {
@@ -606,18 +603,16 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
 		size = status.Offset
 		actual = nw.w.Digest()
 
-		if err := nw.w.Commit(ctx, size, expected); err != nil {
-			if !errdefs.IsAlreadyExists(err) {
-				return "", err
-			}
-			if getBlobBucket(tx, nw.namespace, actual) != nil {
-				return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
-			}
+		if err := nw.w.Commit(ctx, size, expected); err != nil && !errdefs.IsAlreadyExists(err) {
+			return "", err
 		}
 	}
 
 	bkt, err := createBlobBucket(tx, nw.namespace, actual)
 	if err != nil {
+		if err == bolt.ErrBucketExists {
+			return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
+		}
 		return "", err
 	}
 

+ 2 - 2
vendor/github.com/containerd/containerd/metadata/db.go

@@ -23,12 +23,12 @@ import (
 	"sync"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/gc"
 	"github.com/containerd/containerd/log"
 	"github.com/containerd/containerd/snapshots"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 const (
@@ -43,7 +43,7 @@ const (
 	// dbVersion represents updates to the schema
 	// version which are additions and compatible with
 	// prior version of the same schema.
-	dbVersion = 2
+	dbVersion = 3
 )
 
 // DB represents a metadata database backed by a bolt

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/gc.go

@@ -23,10 +23,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/gc"
 	"github.com/containerd/containerd/log"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 const (

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/images.go

@@ -23,7 +23,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/filters"
 	"github.com/containerd/containerd/images"
@@ -33,6 +32,7 @@ import (
 	digest "github.com/opencontainers/go-digest"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type imageStore struct {

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/leases.go

@@ -20,7 +20,6 @@ import (
 	"context"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/filters"
 	"github.com/containerd/containerd/leases"
@@ -28,6 +27,7 @@ import (
 	"github.com/containerd/containerd/namespaces"
 	digest "github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 // LeaseManager manages the create/delete lifecyle of leases

+ 13 - 1
vendor/github.com/containerd/containerd/metadata/migrations.go

@@ -16,7 +16,7 @@
 
 package metadata
 
-import "github.com/boltdb/bolt"
+import bolt "go.etcd.io/bbolt"
 
 type migration struct {
 	schema  string
@@ -45,6 +45,11 @@ var migrations = []migration{
 		version: 2,
 		migrate: migrateIngests,
 	},
+	{
+		schema:  "v1",
+		version: 3,
+		migrate: noOpMigration,
+	},
 }
 
 // addChildLinks Adds children key to the snapshotters to enforce snapshot
@@ -154,3 +159,10 @@ func migrateIngests(tx *bolt.Tx) error {
 
 	return nil
 }
+
+// noOpMigration was for a database change from boltdb/bolt which is no
+// longer being supported, to go.etcd.io/bbolt which is the currently
+// maintained repo for boltdb.
+func noOpMigration(tx *bolt.Tx) error {
+	return nil
+}

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/namespaces.go

@@ -19,11 +19,11 @@ package metadata
 import (
 	"context"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/errdefs"
 	l "github.com/containerd/containerd/labels"
 	"github.com/containerd/containerd/namespaces"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type namespaceStore struct {

+ 1 - 1
vendor/github.com/containerd/containerd/metadata/snapshot.go

@@ -23,7 +23,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/boltdb/bolt"
 	"github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/labels"
 	"github.com/containerd/containerd/log"
@@ -32,6 +31,7 @@ import (
 	"github.com/containerd/containerd/namespaces"
 	"github.com/containerd/containerd/snapshots"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 )
 
 type snapshotter struct {

+ 4 - 0
vendor/github.com/containerd/containerd/mount/mount_windows.go

@@ -32,6 +32,10 @@ var (
 
 // Mount to the provided target
 func (m *Mount) Mount(target string) error {
+	if m.Type != "windows-layer" {
+		return errors.Errorf("invalid windows mount type: '%s'", m.Type)
+	}
+
 	home, layerID := filepath.Split(m.Source)
 
 	parentLayerPaths, err := m.GetParentPaths()

+ 212 - 6
vendor/github.com/containerd/containerd/oci/spec.go

@@ -18,11 +18,27 @@ package oci
 
 import (
 	"context"
+	"path/filepath"
+	"runtime"
+
+	"github.com/containerd/containerd/namespaces"
+	"github.com/containerd/containerd/platforms"
 
 	"github.com/containerd/containerd/containers"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 )
 
+const (
+	rwm               = "rwm"
+	defaultRootfsPath = "rootfs"
+)
+
+var (
+	defaultUnixEnv = []string{
+		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+	}
+)
+
 // Spec is a type alias to the OCI runtime spec to allow third part SpecOpts
 // to be created without the "issues" with go vendoring and package imports
 type Spec = specs.Spec
@@ -30,12 +46,36 @@ type Spec = specs.Spec
 // GenerateSpec will generate a default spec from the provided image
 // for use as a containerd container
 func GenerateSpec(ctx context.Context, client Client, c *containers.Container, opts ...SpecOpts) (*Spec, error) {
-	s, err := createDefaultSpec(ctx, c.ID)
-	if err != nil {
+	return GenerateSpecWithPlatform(ctx, client, platforms.DefaultString(), c, opts...)
+}
+
+// GenerateSpecWithPlatform will generate a default spec from the provided image
+// for use as a containerd container in the platform requested.
+func GenerateSpecWithPlatform(ctx context.Context, client Client, platform string, c *containers.Container, opts ...SpecOpts) (*Spec, error) {
+	var s Spec
+	if err := generateDefaultSpecWithPlatform(ctx, platform, c.ID, &s); err != nil {
 		return nil, err
 	}
 
-	return s, ApplyOpts(ctx, client, c, s, opts...)
+	return &s, ApplyOpts(ctx, client, c, &s, opts...)
+}
+
+func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s *Spec) error {
+	plat, err := platforms.Parse(platform)
+	if err != nil {
+		return err
+	}
+
+	if plat.OS == "windows" {
+		err = populateDefaultWindowsSpec(ctx, s, id)
+	} else {
+		err = populateDefaultUnixSpec(ctx, s, id)
+		if err == nil && runtime.GOOS == "windows" {
+			// To run LCOW we have a Linux and Windows section. Add an empty one now.
+			s.Windows = &specs.Windows{}
+		}
+	}
+	return err
 }
 
 // ApplyOpts applys the options to the given spec, injecting data from the
@@ -50,7 +90,173 @@ func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *S
 	return nil
 }
 
-func createDefaultSpec(ctx context.Context, id string) (*Spec, error) {
-	var s Spec
-	return &s, populateDefaultSpec(ctx, &s, id)
+func defaultUnixCaps() []string {
+	return []string{
+		"CAP_CHOWN",
+		"CAP_DAC_OVERRIDE",
+		"CAP_FSETID",
+		"CAP_FOWNER",
+		"CAP_MKNOD",
+		"CAP_NET_RAW",
+		"CAP_SETGID",
+		"CAP_SETUID",
+		"CAP_SETFCAP",
+		"CAP_SETPCAP",
+		"CAP_NET_BIND_SERVICE",
+		"CAP_SYS_CHROOT",
+		"CAP_KILL",
+		"CAP_AUDIT_WRITE",
+	}
+}
+
+func defaultUnixNamespaces() []specs.LinuxNamespace {
+	return []specs.LinuxNamespace{
+		{
+			Type: specs.PIDNamespace,
+		},
+		{
+			Type: specs.IPCNamespace,
+		},
+		{
+			Type: specs.UTSNamespace,
+		},
+		{
+			Type: specs.MountNamespace,
+		},
+		{
+			Type: specs.NetworkNamespace,
+		},
+	}
+}
+
+func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error {
+	ns, err := namespaces.NamespaceRequired(ctx)
+	if err != nil {
+		return err
+	}
+
+	*s = Spec{
+		Version: specs.Version,
+		Root: &specs.Root{
+			Path: defaultRootfsPath,
+		},
+		Process: &specs.Process{
+			Env:             defaultUnixEnv,
+			Cwd:             "/",
+			NoNewPrivileges: true,
+			User: specs.User{
+				UID: 0,
+				GID: 0,
+			},
+			Capabilities: &specs.LinuxCapabilities{
+				Bounding:    defaultUnixCaps(),
+				Permitted:   defaultUnixCaps(),
+				Inheritable: defaultUnixCaps(),
+				Effective:   defaultUnixCaps(),
+			},
+			Rlimits: []specs.POSIXRlimit{
+				{
+					Type: "RLIMIT_NOFILE",
+					Hard: uint64(1024),
+					Soft: uint64(1024),
+				},
+			},
+		},
+		Mounts: []specs.Mount{
+			{
+				Destination: "/proc",
+				Type:        "proc",
+				Source:      "proc",
+			},
+			{
+				Destination: "/dev",
+				Type:        "tmpfs",
+				Source:      "tmpfs",
+				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
+			},
+			{
+				Destination: "/dev/pts",
+				Type:        "devpts",
+				Source:      "devpts",
+				Options:     []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
+			},
+			{
+				Destination: "/dev/shm",
+				Type:        "tmpfs",
+				Source:      "shm",
+				Options:     []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
+			},
+			{
+				Destination: "/dev/mqueue",
+				Type:        "mqueue",
+				Source:      "mqueue",
+				Options:     []string{"nosuid", "noexec", "nodev"},
+			},
+			{
+				Destination: "/sys",
+				Type:        "sysfs",
+				Source:      "sysfs",
+				Options:     []string{"nosuid", "noexec", "nodev", "ro"},
+			},
+			{
+				Destination: "/run",
+				Type:        "tmpfs",
+				Source:      "tmpfs",
+				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
+			},
+		},
+		Linux: &specs.Linux{
+			MaskedPaths: []string{
+				"/proc/acpi",
+				"/proc/kcore",
+				"/proc/keys",
+				"/proc/latency_stats",
+				"/proc/timer_list",
+				"/proc/timer_stats",
+				"/proc/sched_debug",
+				"/sys/firmware",
+				"/proc/scsi",
+			},
+			ReadonlyPaths: []string{
+				"/proc/asound",
+				"/proc/bus",
+				"/proc/fs",
+				"/proc/irq",
+				"/proc/sys",
+				"/proc/sysrq-trigger",
+			},
+			CgroupsPath: filepath.Join("/", ns, id),
+			Resources: &specs.LinuxResources{
+				Devices: []specs.LinuxDeviceCgroup{
+					{
+						Allow:  false,
+						Access: rwm,
+					},
+				},
+			},
+			Namespaces: defaultUnixNamespaces(),
+		},
+	}
+	return nil
+}
+
+func populateDefaultWindowsSpec(ctx context.Context, s *Spec, id string) error {
+	*s = Spec{
+		Version: specs.Version,
+		Root:    &specs.Root{},
+		Process: &specs.Process{
+			Cwd: `C:\`,
+			ConsoleSize: &specs.Box{
+				Width:  80,
+				Height: 20,
+			},
+		},
+		Windows: &specs.Windows{
+			IgnoreFlushesDuringBoot: true,
+			Network: &specs.WindowsNetwork{
+				AllowUnqualifiedDNSQuery: true,
+			},
+		},
+	}
+	return nil
 }

+ 873 - 33
vendor/github.com/containerd/containerd/oci/spec_opts.go

@@ -19,12 +19,25 @@ package oci
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
 	"strings"
 
 	"github.com/containerd/containerd/containers"
+	"github.com/containerd/containerd/content"
+	"github.com/containerd/containerd/images"
+	"github.com/containerd/containerd/mount"
+	"github.com/containerd/containerd/namespaces"
+	"github.com/containerd/containerd/platforms"
+	"github.com/containerd/continuity/fs"
+	"github.com/opencontainers/image-spec/specs-go/v1"
+	"github.com/opencontainers/runc/libcontainer/user"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
+	"github.com/syndtr/gocapability/capability"
 )
 
 // SpecOpts sets spec specific information to a newly generated OCI spec
@@ -49,13 +62,45 @@ func setProcess(s *Spec) {
 	}
 }
 
+// setRoot sets Root to empty if unset
+func setRoot(s *Spec) {
+	if s.Root == nil {
+		s.Root = &specs.Root{}
+	}
+}
+
+// setLinux sets Linux to empty if unset
+func setLinux(s *Spec) {
+	if s.Linux == nil {
+		s.Linux = &specs.Linux{}
+	}
+}
+
+// setCapabilities sets Linux Capabilities to empty if unset
+func setCapabilities(s *Spec) {
+	setProcess(s)
+	if s.Process.Capabilities == nil {
+		s.Process.Capabilities = &specs.LinuxCapabilities{}
+	}
+}
+
 // WithDefaultSpec returns a SpecOpts that will populate the spec with default
 // values.
 //
 // Use as the first option to clear the spec, then apply options afterwards.
 func WithDefaultSpec() SpecOpts {
 	return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error {
-		return populateDefaultSpec(ctx, s, c.ID)
+		return generateDefaultSpecWithPlatform(ctx, platforms.DefaultString(), c.ID, s)
+	}
+}
+
+// WithDefaultSpecForPlatform returns a SpecOpts that will populate the spec
+// with default values for a given platform.
+//
+// Use as the first option to clear the spec, then apply options afterwards.
+func WithDefaultSpecForPlatform(platform string) SpecOpts {
+	return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error {
+		return generateDefaultSpecWithPlatform(ctx, platform, c.ID, s)
 	}
 }
 
@@ -81,6 +126,55 @@ func WithSpecFromFile(filename string) SpecOpts {
 	}
 }
 
+// WithEnv appends environment variables
+func WithEnv(environmentVariables []string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		if len(environmentVariables) > 0 {
+			setProcess(s)
+			s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, environmentVariables)
+		}
+		return nil
+	}
+}
+
+// replaceOrAppendEnvValues returns the defaults with the overrides either
+// replaced by env key or appended to the list
+func replaceOrAppendEnvValues(defaults, overrides []string) []string {
+	cache := make(map[string]int, len(defaults))
+	for i, e := range defaults {
+		parts := strings.SplitN(e, "=", 2)
+		cache[parts[0]] = i
+	}
+
+	for _, value := range overrides {
+		// Values w/o = means they want this env to be removed/unset.
+		if !strings.Contains(value, "=") {
+			if i, exists := cache[value]; exists {
+				defaults[i] = "" // Used to indicate it should be removed
+			}
+			continue
+		}
+
+		// Just do a normal set/update
+		parts := strings.SplitN(value, "=", 2)
+		if i, exists := cache[parts[0]]; exists {
+			defaults[i] = value
+		} else {
+			defaults = append(defaults, value)
+		}
+	}
+
+	// Now remove all entries that we want to "unset"
+	for i := 0; i < len(defaults); i++ {
+		if defaults[i] == "" {
+			defaults = append(defaults[:i], defaults[i+1:]...)
+			i--
+		}
+	}
+
+	return defaults
+}
+
 // WithProcessArgs replaces the args on the generated spec
 func WithProcessArgs(args ...string) SpecOpts {
 	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
@@ -99,6 +193,32 @@ func WithProcessCwd(cwd string) SpecOpts {
 	}
 }
 
+// WithTTY sets the information on the spec as well as the environment variables for
+// using a TTY
+func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	setProcess(s)
+	s.Process.Terminal = true
+	if s.Linux != nil {
+		s.Process.Env = append(s.Process.Env, "TERM=xterm")
+	}
+
+	return nil
+}
+
+// WithTTYSize sets the information on the spec as well as the environment variables for
+// using a TTY
+func WithTTYSize(width, height int) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setProcess(s)
+		if s.Process.ConsoleSize == nil {
+			s.Process.ConsoleSize = &specs.Box{}
+		}
+		s.Process.ConsoleSize.Width = uint(width)
+		s.Process.ConsoleSize.Height = uint(height)
+		return nil
+	}
+}
+
 // WithHostname sets the container's hostname
 func WithHostname(name string) SpecOpts {
 	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
@@ -107,59 +227,779 @@ func WithHostname(name string) SpecOpts {
 	}
 }
 
-// WithEnv appends environment variables
-func WithEnv(environmentVariables []string) SpecOpts {
+// WithMounts appends mounts
+func WithMounts(mounts []specs.Mount) SpecOpts {
 	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		if len(environmentVariables) > 0 {
-			setProcess(s)
-			s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, environmentVariables)
+		s.Mounts = append(s.Mounts, mounts...)
+		return nil
+	}
+}
+
+// WithHostNamespace allows a task to run inside the host's linux namespace
+func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setLinux(s)
+		for i, n := range s.Linux.Namespaces {
+			if n.Type == ns {
+				s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
+				return nil
+			}
 		}
 		return nil
 	}
 }
 
-// WithMounts appends mounts
-func WithMounts(mounts []specs.Mount) SpecOpts {
+// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
+// spec, the existing namespace is replaced by the one provided.
+func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
 	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		s.Mounts = append(s.Mounts, mounts...)
+		setLinux(s)
+		for i, n := range s.Linux.Namespaces {
+			if n.Type == ns.Type {
+				before := s.Linux.Namespaces[:i]
+				after := s.Linux.Namespaces[i+1:]
+				s.Linux.Namespaces = append(before, ns)
+				s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
+				return nil
+			}
+		}
+		s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
 		return nil
 	}
 }
 
-// replaceOrAppendEnvValues returns the defaults with the overrides either
-// replaced by env key or appended to the list
-func replaceOrAppendEnvValues(defaults, overrides []string) []string {
-	cache := make(map[string]int, len(defaults))
-	for i, e := range defaults {
-		parts := strings.SplitN(e, "=", 2)
-		cache[parts[0]] = i
+// WithImageConfig configures the spec to from the configuration of an Image
+func WithImageConfig(image Image) SpecOpts {
+	return WithImageConfigArgs(image, nil)
+}
+
+// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that
+// replaces the CMD of the image
+func WithImageConfigArgs(image Image, args []string) SpecOpts {
+	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
+		ic, err := image.Config(ctx)
+		if err != nil {
+			return err
+		}
+		var (
+			ociimage v1.Image
+			config   v1.ImageConfig
+		)
+		switch ic.MediaType {
+		case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
+			p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
+			if err != nil {
+				return err
+			}
+
+			if err := json.Unmarshal(p, &ociimage); err != nil {
+				return err
+			}
+			config = ociimage.Config
+		default:
+			return fmt.Errorf("unknown image config media type %s", ic.MediaType)
+		}
+
+		setProcess(s)
+		if s.Linux != nil {
+			s.Process.Env = append(s.Process.Env, config.Env...)
+			cmd := config.Cmd
+			if len(args) > 0 {
+				cmd = args
+			}
+			s.Process.Args = append(config.Entrypoint, cmd...)
+
+			cwd := config.WorkingDir
+			if cwd == "" {
+				cwd = "/"
+			}
+			s.Process.Cwd = cwd
+			if config.User != "" {
+				if err := WithUser(config.User)(ctx, client, c, s); err != nil {
+					return err
+				}
+				return WithAdditionalGIDs(fmt.Sprintf("%d", s.Process.User.UID))(ctx, client, c, s)
+			}
+			// we should query the image's /etc/group for additional GIDs
+			// even if there is no specified user in the image config
+			return WithAdditionalGIDs("root")(ctx, client, c, s)
+		} else if s.Windows != nil {
+			s.Process.Env = config.Env
+			s.Process.Args = append(config.Entrypoint, config.Cmd...)
+			s.Process.User = specs.User{
+				Username: config.User,
+			}
+		} else {
+			return errors.New("spec does not contain Linux or Windows section")
+		}
+		return nil
 	}
+}
 
-	for _, value := range overrides {
-		// Values w/o = means they want this env to be removed/unset.
-		if !strings.Contains(value, "=") {
-			if i, exists := cache[value]; exists {
-				defaults[i] = "" // Used to indicate it should be removed
+// WithRootFSPath specifies unmanaged rootfs path.
+func WithRootFSPath(path string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setRoot(s)
+		s.Root.Path = path
+		// Entrypoint is not set here (it's up to caller)
+		return nil
+	}
+}
+
+// WithRootFSReadonly sets specs.Root.Readonly to true
+func WithRootFSReadonly() SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setRoot(s)
+		s.Root.Readonly = true
+		return nil
+	}
+}
+
+// WithNoNewPrivileges sets no_new_privileges on the process for the container
+func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	setProcess(s)
+	s.Process.NoNewPrivileges = true
+	return nil
+}
+
+// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
+func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	s.Mounts = append(s.Mounts, specs.Mount{
+		Destination: "/etc/hosts",
+		Type:        "bind",
+		Source:      "/etc/hosts",
+		Options:     []string{"rbind", "ro"},
+	})
+	return nil
+}
+
+// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly
+func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	s.Mounts = append(s.Mounts, specs.Mount{
+		Destination: "/etc/resolv.conf",
+		Type:        "bind",
+		Source:      "/etc/resolv.conf",
+		Options:     []string{"rbind", "ro"},
+	})
+	return nil
+}
+
+// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
+func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	s.Mounts = append(s.Mounts, specs.Mount{
+		Destination: "/etc/localtime",
+		Type:        "bind",
+		Source:      "/etc/localtime",
+		Options:     []string{"rbind", "ro"},
+	})
+	return nil
+}
+
+// WithUserNamespace sets the uid and gid mappings for the task
+// this can be called multiple times to add more mappings to the generated spec
+func WithUserNamespace(container, host, size uint32) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		var hasUserns bool
+		setLinux(s)
+		for _, ns := range s.Linux.Namespaces {
+			if ns.Type == specs.UserNamespace {
+				hasUserns = true
+				break
 			}
-			continue
 		}
+		if !hasUserns {
+			s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
+				Type: specs.UserNamespace,
+			})
+		}
+		mapping := specs.LinuxIDMapping{
+			ContainerID: container,
+			HostID:      host,
+			Size:        size,
+		}
+		s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
+		s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
+		return nil
+	}
+}
 
-		// Just do a normal set/update
-		parts := strings.SplitN(value, "=", 2)
-		if i, exists := cache[parts[0]]; exists {
-			defaults[i] = value
+// WithCgroup sets the container's cgroup path
+func WithCgroup(path string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setLinux(s)
+		s.Linux.CgroupsPath = path
+		return nil
+	}
+}
+
+// WithNamespacedCgroup uses the namespace set on the context to create a
+// root directory for containers in the cgroup with the id as the subcgroup
+func WithNamespacedCgroup() SpecOpts {
+	return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error {
+		namespace, err := namespaces.NamespaceRequired(ctx)
+		if err != nil {
+			return err
+		}
+		setLinux(s)
+		s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID)
+		return nil
+	}
+}
+
+// WithUser sets the user to be used within the container.
+// It accepts a valid user string in OCI Image Spec v1.0.0:
+//   user, uid, user:group, uid:gid, uid:group, user:gid
+func WithUser(userstr string) SpecOpts {
+	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
+		setProcess(s)
+		parts := strings.Split(userstr, ":")
+		switch len(parts) {
+		case 1:
+			v, err := strconv.Atoi(parts[0])
+			if err != nil {
+				// if we cannot parse as a uint they try to see if it is a username
+				return WithUsername(userstr)(ctx, client, c, s)
+			}
+			return WithUserID(uint32(v))(ctx, client, c, s)
+		case 2:
+			var (
+				username  string
+				groupname string
+			)
+			var uid, gid uint32
+			v, err := strconv.Atoi(parts[0])
+			if err != nil {
+				username = parts[0]
+			} else {
+				uid = uint32(v)
+			}
+			if v, err = strconv.Atoi(parts[1]); err != nil {
+				groupname = parts[1]
+			} else {
+				gid = uint32(v)
+			}
+			if username == "" && groupname == "" {
+				s.Process.User.UID, s.Process.User.GID = uid, gid
+				return nil
+			}
+			f := func(root string) error {
+				if username != "" {
+					user, err := getUserFromPath(root, func(u user.User) bool {
+						return u.Name == username
+					})
+					if err != nil {
+						return err
+					}
+					uid = uint32(user.Uid)
+				}
+				if groupname != "" {
+					gid, err = getGIDFromPath(root, func(g user.Group) bool {
+						return g.Name == groupname
+					})
+					if err != nil {
+						return err
+					}
+				}
+				s.Process.User.UID, s.Process.User.GID = uid, gid
+				return nil
+			}
+			if c.Snapshotter == "" && c.SnapshotKey == "" {
+				if !isRootfsAbs(s.Root.Path) {
+					return errors.New("rootfs absolute path is required")
+				}
+				return f(s.Root.Path)
+			}
+			if c.Snapshotter == "" {
+				return errors.New("no snapshotter set for container")
+			}
+			if c.SnapshotKey == "" {
+				return errors.New("rootfs snapshot not created for container")
+			}
+			snapshotter := client.SnapshotService(c.Snapshotter)
+			mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
+			if err != nil {
+				return err
+			}
+			return mount.WithTempMount(ctx, mounts, f)
+		default:
+			return fmt.Errorf("invalid USER value %s", userstr)
+		}
+	}
+}
+
+// WithUIDGID allows the UID and GID for the Process to be set
+func WithUIDGID(uid, gid uint32) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setProcess(s)
+		s.Process.User.UID = uid
+		s.Process.User.GID = gid
+		return nil
+	}
+}
+
+// WithUserID sets the correct UID and GID for the container based
+// on the image's /etc/passwd contents. If /etc/passwd does not exist,
+// or uid is not found in /etc/passwd, it sets the requested uid,
+// additionally sets the gid to 0, and does not return an error.
+func WithUserID(uid uint32) SpecOpts {
+	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
+		setProcess(s)
+		if c.Snapshotter == "" && c.SnapshotKey == "" {
+			if !isRootfsAbs(s.Root.Path) {
+				return errors.Errorf("rootfs absolute path is required")
+			}
+			user, err := getUserFromPath(s.Root.Path, func(u user.User) bool {
+				return u.Uid == int(uid)
+			})
+			if err != nil {
+				if os.IsNotExist(err) || err == errNoUsersFound {
+					s.Process.User.UID, s.Process.User.GID = uid, 0
+					return nil
+				}
+				return err
+			}
+			s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid)
+			return nil
+
+		}
+		if c.Snapshotter == "" {
+			return errors.Errorf("no snapshotter set for container")
+		}
+		if c.SnapshotKey == "" {
+			return errors.Errorf("rootfs snapshot not created for container")
+		}
+		snapshotter := client.SnapshotService(c.Snapshotter)
+		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
+		if err != nil {
+			return err
+		}
+		return mount.WithTempMount(ctx, mounts, func(root string) error {
+			user, err := getUserFromPath(root, func(u user.User) bool {
+				return u.Uid == int(uid)
+			})
+			if err != nil {
+				if os.IsNotExist(err) || err == errNoUsersFound {
+					s.Process.User.UID, s.Process.User.GID = uid, 0
+					return nil
+				}
+				return err
+			}
+			s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid)
+			return nil
+		})
+	}
+}
+
+// WithUsername sets the correct UID and GID for the container
+// based on the the image's /etc/passwd contents. If /etc/passwd
+// does not exist, or the username is not found in /etc/passwd,
+// it returns error.
+func WithUsername(username string) SpecOpts {
+	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
+		setProcess(s)
+		if s.Linux != nil {
+			if c.Snapshotter == "" && c.SnapshotKey == "" {
+				if !isRootfsAbs(s.Root.Path) {
+					return errors.Errorf("rootfs absolute path is required")
+				}
+				user, err := getUserFromPath(s.Root.Path, func(u user.User) bool {
+					return u.Name == username
+				})
+				if err != nil {
+					return err
+				}
+				s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid)
+				return nil
+			}
+			if c.Snapshotter == "" {
+				return errors.Errorf("no snapshotter set for container")
+			}
+			if c.SnapshotKey == "" {
+				return errors.Errorf("rootfs snapshot not created for container")
+			}
+			snapshotter := client.SnapshotService(c.Snapshotter)
+			mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
+			if err != nil {
+				return err
+			}
+			return mount.WithTempMount(ctx, mounts, func(root string) error {
+				user, err := getUserFromPath(root, func(u user.User) bool {
+					return u.Name == username
+				})
+				if err != nil {
+					return err
+				}
+				s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid)
+				return nil
+			})
+		} else if s.Windows != nil {
+			s.Process.User.Username = username
 		} else {
-			defaults = append(defaults, value)
+			return errors.New("spec does not contain Linux or Windows section")
 		}
+		return nil
 	}
+}
 
-	// Now remove all entries that we want to "unset"
-	for i := 0; i < len(defaults); i++ {
-		if defaults[i] == "" {
-			defaults = append(defaults[:i], defaults[i+1:]...)
-			i--
+// WithAdditionalGIDs sets the OCI spec's additionalGids array to any additional groups listed
+// for a particular user in the /etc/groups file of the image's root filesystem
+// The passed in user can be either a uid or a username.
+func WithAdditionalGIDs(userstr string) SpecOpts {
+	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
+		setProcess(s)
+		setAdditionalGids := func(root string) error {
+			var username string
+			uid, err := strconv.Atoi(userstr)
+			if err == nil {
+				user, err := getUserFromPath(root, func(u user.User) bool {
+					return u.Uid == uid
+				})
+				if err != nil {
+					if os.IsNotExist(err) || err == errNoUsersFound {
+						return nil
+					}
+					return err
+				}
+				username = user.Name
+			} else {
+				username = userstr
+			}
+			gids, err := getSupplementalGroupsFromPath(root, func(g user.Group) bool {
+				// we only want supplemental groups
+				if g.Name == username {
+					return false
+				}
+				for _, entry := range g.List {
+					if entry == username {
+						return true
+					}
+				}
+				return false
+			})
+			if err != nil {
+				if os.IsNotExist(err) {
+					return nil
+				}
+				return err
+			}
+			s.Process.User.AdditionalGids = gids
+			return nil
+		}
+		if c.Snapshotter == "" && c.SnapshotKey == "" {
+			if !isRootfsAbs(s.Root.Path) {
+				return errors.Errorf("rootfs absolute path is required")
+			}
+			return setAdditionalGids(s.Root.Path)
 		}
+		if c.Snapshotter == "" {
+			return errors.Errorf("no snapshotter set for container")
+		}
+		if c.SnapshotKey == "" {
+			return errors.Errorf("rootfs snapshot not created for container")
+		}
+		snapshotter := client.SnapshotService(c.Snapshotter)
+		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
+		if err != nil {
+			return err
+		}
+		return mount.WithTempMount(ctx, mounts, setAdditionalGids)
 	}
+}
 
-	return defaults
+// WithCapabilities sets Linux capabilities on the process
+func WithCapabilities(caps []string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setCapabilities(s)
+
+		s.Process.Capabilities.Bounding = caps
+		s.Process.Capabilities.Effective = caps
+		s.Process.Capabilities.Permitted = caps
+		s.Process.Capabilities.Inheritable = caps
+
+		return nil
+	}
+}
+
+// WithAllCapabilities sets all linux capabilities for the process
+var WithAllCapabilities = WithCapabilities(getAllCapabilities())
+
+func getAllCapabilities() []string {
+	last := capability.CAP_LAST_CAP
+	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
+	if last == capability.Cap(63) {
+		last = capability.CAP_BLOCK_SUSPEND
+	}
+	var caps []string
+	for _, cap := range capability.List() {
+		if cap > last {
+			continue
+		}
+		caps = append(caps, "CAP_"+strings.ToUpper(cap.String()))
+	}
+	return caps
+}
+
+// WithAmbientCapabilities set the Linux ambient capabilities for the process
+// Ambient capabilities should only be set for non-root users or the caller should
+// understand how these capabilities are used and set
+func WithAmbientCapabilities(caps []string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setCapabilities(s)
+
+		s.Process.Capabilities.Ambient = caps
+		return nil
+	}
+}
+
+var errNoUsersFound = errors.New("no users found")
+
+func getUserFromPath(root string, filter func(user.User) bool) (user.User, error) {
+	ppath, err := fs.RootPath(root, "/etc/passwd")
+	if err != nil {
+		return user.User{}, err
+	}
+	users, err := user.ParsePasswdFileFilter(ppath, filter)
+	if err != nil {
+		return user.User{}, err
+	}
+	if len(users) == 0 {
+		return user.User{}, errNoUsersFound
+	}
+	return users[0], nil
+}
+
+var errNoGroupsFound = errors.New("no groups found")
+
+func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
+	gpath, err := fs.RootPath(root, "/etc/group")
+	if err != nil {
+		return 0, err
+	}
+	groups, err := user.ParseGroupFileFilter(gpath, filter)
+	if err != nil {
+		return 0, err
+	}
+	if len(groups) == 0 {
+		return 0, errNoGroupsFound
+	}
+	g := groups[0]
+	return uint32(g.Gid), nil
+}
+
+func getSupplementalGroupsFromPath(root string, filter func(user.Group) bool) ([]uint32, error) {
+	gpath, err := fs.RootPath(root, "/etc/group")
+	if err != nil {
+		return []uint32{}, err
+	}
+	groups, err := user.ParseGroupFileFilter(gpath, filter)
+	if err != nil {
+		return []uint32{}, err
+	}
+	if len(groups) == 0 {
+		// if there are no additional groups; just return an empty set
+		return []uint32{}, nil
+	}
+	addlGids := []uint32{}
+	for _, grp := range groups {
+		addlGids = append(addlGids, uint32(grp.Gid))
+	}
+	return addlGids, nil
+}
+
+func isRootfsAbs(root string) bool {
+	return filepath.IsAbs(root)
+}
+
+// WithMaskedPaths sets the masked paths option
+func WithMaskedPaths(paths []string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setLinux(s)
+		s.Linux.MaskedPaths = paths
+		return nil
+	}
+}
+
+// WithReadonlyPaths sets the read only paths option
+func WithReadonlyPaths(paths []string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setLinux(s)
+		s.Linux.ReadonlyPaths = paths
+		return nil
+	}
+}
+
+// WithWriteableSysfs makes any sysfs mounts writeable
+func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	for i, m := range s.Mounts {
+		if m.Type == "sysfs" {
+			var options []string
+			for _, o := range m.Options {
+				if o == "ro" {
+					o = "rw"
+				}
+				options = append(options, o)
+			}
+			s.Mounts[i].Options = options
+		}
+	}
+	return nil
+}
+
+// WithWriteableCgroupfs makes any cgroup mounts writeable
+func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	for i, m := range s.Mounts {
+		if m.Type == "cgroup" {
+			var options []string
+			for _, o := range m.Options {
+				if o == "ro" {
+					o = "rw"
+				}
+				options = append(options, o)
+			}
+			s.Mounts[i].Options = options
+		}
+	}
+	return nil
 }
+
+// WithSelinuxLabel sets the process SELinux label
+func WithSelinuxLabel(label string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setProcess(s)
+		s.Process.SelinuxLabel = label
+		return nil
+	}
+}
+
+// WithApparmorProfile sets the Apparmor profile for the process
+func WithApparmorProfile(profile string) SpecOpts {
+	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+		setProcess(s)
+		s.Process.ApparmorProfile = profile
+		return nil
+	}
+}
+
+// WithSeccompUnconfined clears the seccomp profile
+func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	setLinux(s)
+	s.Linux.Seccomp = nil
+	return nil
+}
+
+// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's
+// allowed and denied devices
+func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	setLinux(s)
+	if s.Linux.Resources == nil {
+		s.Linux.Resources = &specs.LinuxResources{}
+	}
+	s.Linux.Resources.Devices = nil
+	return nil
+}
+
+// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to
+// the container's resource cgroup spec
+func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
+	setLinux(s)
+	if s.Linux.Resources == nil {
+		s.Linux.Resources = &specs.LinuxResources{}
+	}
+	intptr := func(i int64) *int64 {
+		return &i
+	}
+	s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{
+		{
+			// "/dev/null",
+			Type:   "c",
+			Major:  intptr(1),
+			Minor:  intptr(3),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/random",
+			Type:   "c",
+			Major:  intptr(1),
+			Minor:  intptr(8),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/full",
+			Type:   "c",
+			Major:  intptr(1),
+			Minor:  intptr(7),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/tty",
+			Type:   "c",
+			Major:  intptr(5),
+			Minor:  intptr(0),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/zero",
+			Type:   "c",
+			Major:  intptr(1),
+			Minor:  intptr(5),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/urandom",
+			Type:   "c",
+			Major:  intptr(1),
+			Minor:  intptr(9),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// "/dev/console",
+			Type:   "c",
+			Major:  intptr(5),
+			Minor:  intptr(1),
+			Access: rwm,
+			Allow:  true,
+		},
+		// /dev/pts/ - pts namespaces are "coming soon"
+		{
+			Type:   "c",
+			Major:  intptr(136),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			Type:   "c",
+			Major:  intptr(5),
+			Minor:  intptr(2),
+			Access: rwm,
+			Allow:  true,
+		},
+		{
+			// tuntap
+			Type:   "c",
+			Major:  intptr(10),
+			Minor:  intptr(200),
+			Access: rwm,
+			Allow:  true,
+		},
+	}...)
+	return nil
+}
+
+// WithPrivileged sets up options for a privileged container
+// TODO(justincormack) device handling
+var WithPrivileged = Compose(
+	WithAllCapabilities,
+	WithMaskedPaths(nil),
+	WithReadonlyPaths(nil),
+	WithWriteableSysfs,
+	WithWriteableCgroupfs,
+	WithSelinuxLabel(""),
+	WithApparmorProfile(""),
+	WithSeccompUnconfined,
+)

+ 0 - 733
vendor/github.com/containerd/containerd/oci/spec_opts_unix.go

@@ -1,733 +0,0 @@
-// +build !windows
-
-/*
-   Copyright The containerd Authors.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package oci
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-
-	"github.com/containerd/containerd/containers"
-	"github.com/containerd/containerd/content"
-	"github.com/containerd/containerd/images"
-	"github.com/containerd/containerd/mount"
-	"github.com/containerd/containerd/namespaces"
-	"github.com/containerd/continuity/fs"
-	"github.com/opencontainers/image-spec/specs-go/v1"
-	"github.com/opencontainers/runc/libcontainer/user"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/pkg/errors"
-	"github.com/syndtr/gocapability/capability"
-)
-
-// WithTTY sets the information on the spec as well as the environment variables for
-// using a TTY
-func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	setProcess(s)
-	s.Process.Terminal = true
-	s.Process.Env = append(s.Process.Env, "TERM=xterm")
-	return nil
-}
-
-// setRoot sets Root to empty if unset
-func setRoot(s *Spec) {
-	if s.Root == nil {
-		s.Root = &specs.Root{}
-	}
-}
-
-// setLinux sets Linux to empty if unset
-func setLinux(s *Spec) {
-	if s.Linux == nil {
-		s.Linux = &specs.Linux{}
-	}
-}
-
-// setCapabilities sets Linux Capabilities to empty if unset
-func setCapabilities(s *Spec) {
-	setProcess(s)
-	if s.Process.Capabilities == nil {
-		s.Process.Capabilities = &specs.LinuxCapabilities{}
-	}
-}
-
-// WithHostNamespace allows a task to run inside the host's linux namespace
-func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setLinux(s)
-		for i, n := range s.Linux.Namespaces {
-			if n.Type == ns {
-				s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
-				return nil
-			}
-		}
-		return nil
-	}
-}
-
-// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
-// spec, the existing namespace is replaced by the one provided.
-func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setLinux(s)
-		for i, n := range s.Linux.Namespaces {
-			if n.Type == ns.Type {
-				before := s.Linux.Namespaces[:i]
-				after := s.Linux.Namespaces[i+1:]
-				s.Linux.Namespaces = append(before, ns)
-				s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
-				return nil
-			}
-		}
-		s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
-		return nil
-	}
-}
-
-// WithImageConfig configures the spec to from the configuration of an Image
-func WithImageConfig(image Image) SpecOpts {
-	return WithImageConfigArgs(image, nil)
-}
-
-// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that
-// replaces the CMD of the image
-func WithImageConfigArgs(image Image, args []string) SpecOpts {
-	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
-		ic, err := image.Config(ctx)
-		if err != nil {
-			return err
-		}
-		var (
-			ociimage v1.Image
-			config   v1.ImageConfig
-		)
-		switch ic.MediaType {
-		case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
-			p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
-			if err != nil {
-				return err
-			}
-
-			if err := json.Unmarshal(p, &ociimage); err != nil {
-				return err
-			}
-			config = ociimage.Config
-		default:
-			return fmt.Errorf("unknown image config media type %s", ic.MediaType)
-		}
-
-		setProcess(s)
-		s.Process.Env = append(s.Process.Env, config.Env...)
-		cmd := config.Cmd
-		if len(args) > 0 {
-			cmd = args
-		}
-		s.Process.Args = append(config.Entrypoint, cmd...)
-
-		cwd := config.WorkingDir
-		if cwd == "" {
-			cwd = "/"
-		}
-		s.Process.Cwd = cwd
-		if config.User != "" {
-			return WithUser(config.User)(ctx, client, c, s)
-		}
-		return nil
-	}
-}
-
-// WithRootFSPath specifies unmanaged rootfs path.
-func WithRootFSPath(path string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setRoot(s)
-		s.Root.Path = path
-		// Entrypoint is not set here (it's up to caller)
-		return nil
-	}
-}
-
-// WithRootFSReadonly sets specs.Root.Readonly to true
-func WithRootFSReadonly() SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setRoot(s)
-		s.Root.Readonly = true
-		return nil
-	}
-}
-
-// WithNoNewPrivileges sets no_new_privileges on the process for the container
-func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	setProcess(s)
-	s.Process.NoNewPrivileges = true
-	return nil
-}
-
-// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
-func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	s.Mounts = append(s.Mounts, specs.Mount{
-		Destination: "/etc/hosts",
-		Type:        "bind",
-		Source:      "/etc/hosts",
-		Options:     []string{"rbind", "ro"},
-	})
-	return nil
-}
-
-// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly
-func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	s.Mounts = append(s.Mounts, specs.Mount{
-		Destination: "/etc/resolv.conf",
-		Type:        "bind",
-		Source:      "/etc/resolv.conf",
-		Options:     []string{"rbind", "ro"},
-	})
-	return nil
-}
-
-// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
-func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	s.Mounts = append(s.Mounts, specs.Mount{
-		Destination: "/etc/localtime",
-		Type:        "bind",
-		Source:      "/etc/localtime",
-		Options:     []string{"rbind", "ro"},
-	})
-	return nil
-}
-
-// WithUserNamespace sets the uid and gid mappings for the task
-// this can be called multiple times to add more mappings to the generated spec
-func WithUserNamespace(container, host, size uint32) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		var hasUserns bool
-		setLinux(s)
-		for _, ns := range s.Linux.Namespaces {
-			if ns.Type == specs.UserNamespace {
-				hasUserns = true
-				break
-			}
-		}
-		if !hasUserns {
-			s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
-				Type: specs.UserNamespace,
-			})
-		}
-		mapping := specs.LinuxIDMapping{
-			ContainerID: container,
-			HostID:      host,
-			Size:        size,
-		}
-		s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
-		s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
-		return nil
-	}
-}
-
-// WithCgroup sets the container's cgroup path
-func WithCgroup(path string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setLinux(s)
-		s.Linux.CgroupsPath = path
-		return nil
-	}
-}
-
-// WithNamespacedCgroup uses the namespace set on the context to create a
-// root directory for containers in the cgroup with the id as the subcgroup
-func WithNamespacedCgroup() SpecOpts {
-	return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error {
-		namespace, err := namespaces.NamespaceRequired(ctx)
-		if err != nil {
-			return err
-		}
-		setLinux(s)
-		s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID)
-		return nil
-	}
-}
-
-// WithUser sets the user to be used within the container.
-// It accepts a valid user string in OCI Image Spec v1.0.0:
-//   user, uid, user:group, uid:gid, uid:group, user:gid
-func WithUser(userstr string) SpecOpts {
-	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
-		setProcess(s)
-		parts := strings.Split(userstr, ":")
-		switch len(parts) {
-		case 1:
-			v, err := strconv.Atoi(parts[0])
-			if err != nil {
-				// if we cannot parse as a uint they try to see if it is a username
-				return WithUsername(userstr)(ctx, client, c, s)
-			}
-			return WithUserID(uint32(v))(ctx, client, c, s)
-		case 2:
-			var (
-				username  string
-				groupname string
-			)
-			var uid, gid uint32
-			v, err := strconv.Atoi(parts[0])
-			if err != nil {
-				username = parts[0]
-			} else {
-				uid = uint32(v)
-			}
-			if v, err = strconv.Atoi(parts[1]); err != nil {
-				groupname = parts[1]
-			} else {
-				gid = uint32(v)
-			}
-			if username == "" && groupname == "" {
-				s.Process.User.UID, s.Process.User.GID = uid, gid
-				return nil
-			}
-			f := func(root string) error {
-				if username != "" {
-					uid, _, err = getUIDGIDFromPath(root, func(u user.User) bool {
-						return u.Name == username
-					})
-					if err != nil {
-						return err
-					}
-				}
-				if groupname != "" {
-					gid, err = getGIDFromPath(root, func(g user.Group) bool {
-						return g.Name == groupname
-					})
-					if err != nil {
-						return err
-					}
-				}
-				s.Process.User.UID, s.Process.User.GID = uid, gid
-				return nil
-			}
-			if c.Snapshotter == "" && c.SnapshotKey == "" {
-				if !isRootfsAbs(s.Root.Path) {
-					return errors.New("rootfs absolute path is required")
-				}
-				return f(s.Root.Path)
-			}
-			if c.Snapshotter == "" {
-				return errors.New("no snapshotter set for container")
-			}
-			if c.SnapshotKey == "" {
-				return errors.New("rootfs snapshot not created for container")
-			}
-			snapshotter := client.SnapshotService(c.Snapshotter)
-			mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
-			if err != nil {
-				return err
-			}
-			return mount.WithTempMount(ctx, mounts, f)
-		default:
-			return fmt.Errorf("invalid USER value %s", userstr)
-		}
-	}
-}
-
-// WithUIDGID allows the UID and GID for the Process to be set
-func WithUIDGID(uid, gid uint32) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setProcess(s)
-		s.Process.User.UID = uid
-		s.Process.User.GID = gid
-		return nil
-	}
-}
-
-// WithUserID sets the correct UID and GID for the container based
-// on the image's /etc/passwd contents. If /etc/passwd does not exist,
-// or uid is not found in /etc/passwd, it sets the requested uid,
-// additionally sets the gid to 0, and does not return an error.
-func WithUserID(uid uint32) SpecOpts {
-	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
-		setProcess(s)
-		if c.Snapshotter == "" && c.SnapshotKey == "" {
-			if !isRootfsAbs(s.Root.Path) {
-				return errors.Errorf("rootfs absolute path is required")
-			}
-			uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool {
-				return u.Uid == int(uid)
-			})
-			if err != nil {
-				if os.IsNotExist(err) || err == errNoUsersFound {
-					s.Process.User.UID, s.Process.User.GID = uid, 0
-					return nil
-				}
-				return err
-			}
-			s.Process.User.UID, s.Process.User.GID = uuid, ugid
-			return nil
-
-		}
-		if c.Snapshotter == "" {
-			return errors.Errorf("no snapshotter set for container")
-		}
-		if c.SnapshotKey == "" {
-			return errors.Errorf("rootfs snapshot not created for container")
-		}
-		snapshotter := client.SnapshotService(c.Snapshotter)
-		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
-		if err != nil {
-			return err
-		}
-		return mount.WithTempMount(ctx, mounts, func(root string) error {
-			uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool {
-				return u.Uid == int(uid)
-			})
-			if err != nil {
-				if os.IsNotExist(err) || err == errNoUsersFound {
-					s.Process.User.UID, s.Process.User.GID = uid, 0
-					return nil
-				}
-				return err
-			}
-			s.Process.User.UID, s.Process.User.GID = uuid, ugid
-			return nil
-		})
-	}
-}
-
-// WithUsername sets the correct UID and GID for the container
-// based on the the image's /etc/passwd contents. If /etc/passwd
-// does not exist, or the username is not found in /etc/passwd,
-// it returns error.
-func WithUsername(username string) SpecOpts {
-	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
-		setProcess(s)
-		if c.Snapshotter == "" && c.SnapshotKey == "" {
-			if !isRootfsAbs(s.Root.Path) {
-				return errors.Errorf("rootfs absolute path is required")
-			}
-			uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool {
-				return u.Name == username
-			})
-			if err != nil {
-				return err
-			}
-			s.Process.User.UID, s.Process.User.GID = uid, gid
-			return nil
-		}
-		if c.Snapshotter == "" {
-			return errors.Errorf("no snapshotter set for container")
-		}
-		if c.SnapshotKey == "" {
-			return errors.Errorf("rootfs snapshot not created for container")
-		}
-		snapshotter := client.SnapshotService(c.Snapshotter)
-		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
-		if err != nil {
-			return err
-		}
-		return mount.WithTempMount(ctx, mounts, func(root string) error {
-			uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool {
-				return u.Name == username
-			})
-			if err != nil {
-				return err
-			}
-			s.Process.User.UID, s.Process.User.GID = uid, gid
-			return nil
-		})
-	}
-}
-
-// WithCapabilities sets Linux capabilities on the process
-func WithCapabilities(caps []string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setCapabilities(s)
-
-		s.Process.Capabilities.Bounding = caps
-		s.Process.Capabilities.Effective = caps
-		s.Process.Capabilities.Permitted = caps
-		s.Process.Capabilities.Inheritable = caps
-
-		return nil
-	}
-}
-
-// WithAllCapabilities sets all linux capabilities for the process
-var WithAllCapabilities = WithCapabilities(getAllCapabilities())
-
-func getAllCapabilities() []string {
-	last := capability.CAP_LAST_CAP
-	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
-	if last == capability.Cap(63) {
-		last = capability.CAP_BLOCK_SUSPEND
-	}
-	var caps []string
-	for _, cap := range capability.List() {
-		if cap > last {
-			continue
-		}
-		caps = append(caps, "CAP_"+strings.ToUpper(cap.String()))
-	}
-	return caps
-}
-
-// WithAmbientCapabilities set the Linux ambient capabilities for the process
-// Ambient capabilities should only be set for non-root users or the caller should
-// understand how these capabilities are used and set
-func WithAmbientCapabilities(caps []string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setCapabilities(s)
-
-		s.Process.Capabilities.Ambient = caps
-		return nil
-	}
-}
-
-var errNoUsersFound = errors.New("no users found")
-
-func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) {
-	ppath, err := fs.RootPath(root, "/etc/passwd")
-	if err != nil {
-		return 0, 0, err
-	}
-	users, err := user.ParsePasswdFileFilter(ppath, filter)
-	if err != nil {
-		return 0, 0, err
-	}
-	if len(users) == 0 {
-		return 0, 0, errNoUsersFound
-	}
-	u := users[0]
-	return uint32(u.Uid), uint32(u.Gid), nil
-}
-
-var errNoGroupsFound = errors.New("no groups found")
-
-func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
-	gpath, err := fs.RootPath(root, "/etc/group")
-	if err != nil {
-		return 0, err
-	}
-	groups, err := user.ParseGroupFileFilter(gpath, filter)
-	if err != nil {
-		return 0, err
-	}
-	if len(groups) == 0 {
-		return 0, errNoGroupsFound
-	}
-	g := groups[0]
-	return uint32(g.Gid), nil
-}
-
-func isRootfsAbs(root string) bool {
-	return filepath.IsAbs(root)
-}
-
-// WithMaskedPaths sets the masked paths option
-func WithMaskedPaths(paths []string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setLinux(s)
-		s.Linux.MaskedPaths = paths
-		return nil
-	}
-}
-
-// WithReadonlyPaths sets the read only paths option
-func WithReadonlyPaths(paths []string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setLinux(s)
-		s.Linux.ReadonlyPaths = paths
-		return nil
-	}
-}
-
-// WithWriteableSysfs makes any sysfs mounts writeable
-func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	for i, m := range s.Mounts {
-		if m.Type == "sysfs" {
-			var options []string
-			for _, o := range m.Options {
-				if o == "ro" {
-					o = "rw"
-				}
-				options = append(options, o)
-			}
-			s.Mounts[i].Options = options
-		}
-	}
-	return nil
-}
-
-// WithWriteableCgroupfs makes any cgroup mounts writeable
-func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	for i, m := range s.Mounts {
-		if m.Type == "cgroup" {
-			var options []string
-			for _, o := range m.Options {
-				if o == "ro" {
-					o = "rw"
-				}
-				options = append(options, o)
-			}
-			s.Mounts[i].Options = options
-		}
-	}
-	return nil
-}
-
-// WithSelinuxLabel sets the process SELinux label
-func WithSelinuxLabel(label string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setProcess(s)
-		s.Process.SelinuxLabel = label
-		return nil
-	}
-}
-
-// WithApparmorProfile sets the Apparmor profile for the process
-func WithApparmorProfile(profile string) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setProcess(s)
-		s.Process.ApparmorProfile = profile
-		return nil
-	}
-}
-
-// WithSeccompUnconfined clears the seccomp profile
-func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	setLinux(s)
-	s.Linux.Seccomp = nil
-	return nil
-}
-
-// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's
-// allowed and denied devices
-func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	setLinux(s)
-	if s.Linux.Resources == nil {
-		s.Linux.Resources = &specs.LinuxResources{}
-	}
-	s.Linux.Resources.Devices = nil
-	return nil
-}
-
-// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to
-// the container's resource cgroup spec
-func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-	setLinux(s)
-	if s.Linux.Resources == nil {
-		s.Linux.Resources = &specs.LinuxResources{}
-	}
-	intptr := func(i int64) *int64 {
-		return &i
-	}
-	s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{
-		{
-			// "/dev/null",
-			Type:   "c",
-			Major:  intptr(1),
-			Minor:  intptr(3),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/random",
-			Type:   "c",
-			Major:  intptr(1),
-			Minor:  intptr(8),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/full",
-			Type:   "c",
-			Major:  intptr(1),
-			Minor:  intptr(7),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/tty",
-			Type:   "c",
-			Major:  intptr(5),
-			Minor:  intptr(0),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/zero",
-			Type:   "c",
-			Major:  intptr(1),
-			Minor:  intptr(5),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/urandom",
-			Type:   "c",
-			Major:  intptr(1),
-			Minor:  intptr(9),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// "/dev/console",
-			Type:   "c",
-			Major:  intptr(5),
-			Minor:  intptr(1),
-			Access: rwm,
-			Allow:  true,
-		},
-		// /dev/pts/ - pts namespaces are "coming soon"
-		{
-			Type:   "c",
-			Major:  intptr(136),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			Type:   "c",
-			Major:  intptr(5),
-			Minor:  intptr(2),
-			Access: rwm,
-			Allow:  true,
-		},
-		{
-			// tuntap
-			Type:   "c",
-			Major:  intptr(10),
-			Minor:  intptr(200),
-			Access: rwm,
-			Allow:  true,
-		},
-	}...)
-	return nil
-}
-
-// WithPrivileged sets up options for a privileged container
-// TODO(justincormack) device handling
-var WithPrivileged = Compose(
-	WithAllCapabilities,
-	WithMaskedPaths(nil),
-	WithReadonlyPaths(nil),
-	WithWriteableSysfs,
-	WithWriteableCgroupfs,
-	WithSelinuxLabel(""),
-	WithApparmorProfile(""),
-	WithSeccompUnconfined,
-)

+ 0 - 89
vendor/github.com/containerd/containerd/oci/spec_opts_windows.go

@@ -1,89 +0,0 @@
-// +build windows
-
-/*
-   Copyright The containerd Authors.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package oci
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-
-	"github.com/containerd/containerd/containers"
-	"github.com/containerd/containerd/content"
-	"github.com/containerd/containerd/images"
-	"github.com/opencontainers/image-spec/specs-go/v1"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// WithImageConfig configures the spec to from the configuration of an Image
-func WithImageConfig(image Image) SpecOpts {
-	return func(ctx context.Context, client Client, _ *containers.Container, s *Spec) error {
-		setProcess(s)
-		ic, err := image.Config(ctx)
-		if err != nil {
-			return err
-		}
-		var (
-			ociimage v1.Image
-			config   v1.ImageConfig
-		)
-		switch ic.MediaType {
-		case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
-			p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
-			if err != nil {
-				return err
-			}
-			if err := json.Unmarshal(p, &ociimage); err != nil {
-				return err
-			}
-			config = ociimage.Config
-		default:
-			return fmt.Errorf("unknown image config media type %s", ic.MediaType)
-		}
-		s.Process.Env = config.Env
-		s.Process.Args = append(config.Entrypoint, config.Cmd...)
-		s.Process.User = specs.User{
-			Username: config.User,
-		}
-		return nil
-	}
-}
-
-// WithTTY sets the information on the spec as well as the environment variables for
-// using a TTY
-func WithTTY(width, height int) SpecOpts {
-	return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
-		setProcess(s)
-		s.Process.Terminal = true
-		if s.Process.ConsoleSize == nil {
-			s.Process.ConsoleSize = &specs.Box{}
-		}
-		s.Process.ConsoleSize.Width = uint(width)
-		s.Process.ConsoleSize.Height = uint(height)
-		return nil
-	}
-}
-
-// WithUsername sets the username on the process
-func WithUsername(username string) SpecOpts {
-	return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
-		setProcess(s)
-		s.Process.User.Username = username
-		return nil
-	}
-}

+ 0 - 188
vendor/github.com/containerd/containerd/oci/spec_unix.go

@@ -1,188 +0,0 @@
-// +build !windows
-
-/*
-   Copyright The containerd Authors.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package oci
-
-import (
-	"context"
-	"path/filepath"
-
-	"github.com/containerd/containerd/namespaces"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-const (
-	rwm               = "rwm"
-	defaultRootfsPath = "rootfs"
-)
-
-var (
-	defaultEnv = []string{
-		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-	}
-)
-
-func defaultCaps() []string {
-	return []string{
-		"CAP_CHOWN",
-		"CAP_DAC_OVERRIDE",
-		"CAP_FSETID",
-		"CAP_FOWNER",
-		"CAP_MKNOD",
-		"CAP_NET_RAW",
-		"CAP_SETGID",
-		"CAP_SETUID",
-		"CAP_SETFCAP",
-		"CAP_SETPCAP",
-		"CAP_NET_BIND_SERVICE",
-		"CAP_SYS_CHROOT",
-		"CAP_KILL",
-		"CAP_AUDIT_WRITE",
-	}
-}
-
-func defaultNamespaces() []specs.LinuxNamespace {
-	return []specs.LinuxNamespace{
-		{
-			Type: specs.PIDNamespace,
-		},
-		{
-			Type: specs.IPCNamespace,
-		},
-		{
-			Type: specs.UTSNamespace,
-		},
-		{
-			Type: specs.MountNamespace,
-		},
-		{
-			Type: specs.NetworkNamespace,
-		},
-	}
-}
-
-func populateDefaultSpec(ctx context.Context, s *Spec, id string) error {
-	ns, err := namespaces.NamespaceRequired(ctx)
-	if err != nil {
-		return err
-	}
-
-	*s = Spec{
-		Version: specs.Version,
-		Root: &specs.Root{
-			Path: defaultRootfsPath,
-		},
-		Process: &specs.Process{
-			Env:             defaultEnv,
-			Cwd:             "/",
-			NoNewPrivileges: true,
-			User: specs.User{
-				UID: 0,
-				GID: 0,
-			},
-			Capabilities: &specs.LinuxCapabilities{
-				Bounding:    defaultCaps(),
-				Permitted:   defaultCaps(),
-				Inheritable: defaultCaps(),
-				Effective:   defaultCaps(),
-			},
-			Rlimits: []specs.POSIXRlimit{
-				{
-					Type: "RLIMIT_NOFILE",
-					Hard: uint64(1024),
-					Soft: uint64(1024),
-				},
-			},
-		},
-		Mounts: []specs.Mount{
-			{
-				Destination: "/proc",
-				Type:        "proc",
-				Source:      "proc",
-			},
-			{
-				Destination: "/dev",
-				Type:        "tmpfs",
-				Source:      "tmpfs",
-				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
-			},
-			{
-				Destination: "/dev/pts",
-				Type:        "devpts",
-				Source:      "devpts",
-				Options:     []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
-			},
-			{
-				Destination: "/dev/shm",
-				Type:        "tmpfs",
-				Source:      "shm",
-				Options:     []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
-			},
-			{
-				Destination: "/dev/mqueue",
-				Type:        "mqueue",
-				Source:      "mqueue",
-				Options:     []string{"nosuid", "noexec", "nodev"},
-			},
-			{
-				Destination: "/sys",
-				Type:        "sysfs",
-				Source:      "sysfs",
-				Options:     []string{"nosuid", "noexec", "nodev", "ro"},
-			},
-			{
-				Destination: "/run",
-				Type:        "tmpfs",
-				Source:      "tmpfs",
-				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
-			},
-		},
-		Linux: &specs.Linux{
-			MaskedPaths: []string{
-				"/proc/acpi",
-				"/proc/kcore",
-				"/proc/keys",
-				"/proc/latency_stats",
-				"/proc/timer_list",
-				"/proc/timer_stats",
-				"/proc/sched_debug",
-				"/sys/firmware",
-				"/proc/scsi",
-			},
-			ReadonlyPaths: []string{
-				"/proc/asound",
-				"/proc/bus",
-				"/proc/fs",
-				"/proc/irq",
-				"/proc/sys",
-				"/proc/sysrq-trigger",
-			},
-			CgroupsPath: filepath.Join("/", ns, id),
-			Resources: &specs.LinuxResources{
-				Devices: []specs.LinuxDeviceCgroup{
-					{
-						Allow:  false,
-						Access: rwm,
-					},
-				},
-			},
-			Namespaces: defaultNamespaces(),
-		},
-	}
-	return nil
-}

+ 0 - 5
vendor/github.com/containerd/containerd/platforms/defaults.go

@@ -22,11 +22,6 @@ import (
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
-// Default returns the default matcher for the platform.
-func Default() MatchComparer {
-	return Only(DefaultSpec())
-}
-
 // DefaultString returns the default string specifier for the platform.
 func DefaultString() string {
 	return Format(DefaultSpec())

+ 6 - 26
vendor/github.com/containerd/containerd/oci/spec_windows.go → vendor/github.com/containerd/containerd/platforms/defaults_unix.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 /*
    Copyright The containerd Authors.
 
@@ -14,31 +16,9 @@
    limitations under the License.
 */
 
-package oci
-
-import (
-	"context"
-
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-)
+package platforms
 
-func populateDefaultSpec(ctx context.Context, s *Spec, id string) error {
-	*s = Spec{
-		Version: specs.Version,
-		Root:    &specs.Root{},
-		Process: &specs.Process{
-			Cwd: `C:\`,
-			ConsoleSize: &specs.Box{
-				Width:  80,
-				Height: 20,
-			},
-		},
-		Windows: &specs.Windows{
-			IgnoreFlushesDuringBoot: true,
-			Network: &specs.WindowsNetwork{
-				AllowUnqualifiedDNSQuery: true,
-			},
-		},
-	}
-	return nil
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+	return Only(DefaultSpec())
 }

+ 10 - 10
vendor/github.com/containerd/containerd/task_opts_windows.go → vendor/github.com/containerd/containerd/platforms/defaults_windows.go

@@ -1,3 +1,5 @@
+// +build windows
+
 /*
    Copyright The containerd Authors.
 
@@ -14,18 +16,16 @@
    limitations under the License.
 */
 
-package containerd
+package platforms
 
 import (
-	"context"
-
-	specs "github.com/opencontainers/runtime-spec/specs-go"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
-// WithResources sets the provided resources on the spec for task updates
-func WithResources(resources *specs.WindowsResources) UpdateTaskOpts {
-	return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
-		r.Resources = resources
-		return nil
-	}
+// Default returns the default matcher for the platform.
+func Default() MatchComparer {
+	return Ordered(DefaultSpec(), specs.Platform{
+		OS:           "linux",
+		Architecture: "amd64",
+	})
 }

+ 1 - 1
vendor/github.com/containerd/containerd/remotes/docker/fetcher.go

@@ -117,7 +117,7 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int
 			}
 		} else {
 			// TODO: Should any cases where use of content range
-			// without the proper header be considerd?
+			// without the proper header be considered?
 			// 206 responses?
 
 			// Discard up to offset

+ 1 - 1
vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go

@@ -134,7 +134,7 @@ func (hrs *httpReadSeeker) reader() (io.Reader, error) {
 		// There is an edge case here where offset == size of the content. If
 		// we seek, we will probably get an error for content that cannot be
 		// sought (?). In that case, we should err on committing the content,
-		// as the length is already satisified but we just return the empty
+		// as the length is already satisfied but we just return the empty
 		// reader instead.
 
 		hrs.rc = ioutil.NopCloser(bytes.NewReader([]byte{}))

+ 53 - 2
vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go

@@ -272,8 +272,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
 			return err
 		}
 
-		// TODO: Check if blob -> diff id mapping already exists
-		// TODO: Check if blob empty label exists
+		reuse, err := c.reuseLabelBlobState(ctx, desc)
+		if err != nil {
+			return err
+		}
+
+		if reuse {
+			return nil
+		}
 
 		ra, err := c.contentStore.ReaderAt(ctx, desc)
 		if err != nil {
@@ -343,6 +349,17 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
 
 	state := calc.State()
 
+	cinfo := content.Info{
+		Digest: desc.Digest,
+		Labels: map[string]string{
+			"containerd.io/uncompressed": state.diffID.String(),
+		},
+	}
+
+	if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil {
+		return errors.Wrap(err, "failed to update uncompressed label")
+	}
+
 	c.mu.Lock()
 	c.blobMap[desc.Digest] = state
 	c.layerBlobs[state.diffID] = desc
@@ -351,6 +368,40 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
 	return nil
 }
 
+func (c *Converter) reuseLabelBlobState(ctx context.Context, desc ocispec.Descriptor) (bool, error) {
+	cinfo, err := c.contentStore.Info(ctx, desc.Digest)
+	if err != nil {
+		return false, errors.Wrap(err, "failed to get blob info")
+	}
+	desc.Size = cinfo.Size
+
+	diffID, ok := cinfo.Labels["containerd.io/uncompressed"]
+	if !ok {
+		return false, nil
+	}
+
+	bState := blobState{empty: false}
+
+	if bState.diffID, err = digest.Parse(diffID); err != nil {
+		log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse digest from label containerd.io/uncompressed: %v", diffID)
+		return false, nil
+	}
+
+	// NOTE: there is no need to read header to get compression method
+	// because there are only two kinds of methods.
+	if bState.diffID == desc.Digest {
+		desc.MediaType = images.MediaTypeDockerSchema2Layer
+	} else {
+		desc.MediaType = images.MediaTypeDockerSchema2LayerGzip
+	}
+
+	c.mu.Lock()
+	c.blobMap[desc.Digest] = bState
+	c.layerBlobs[bState.diffID] = desc
+	c.mu.Unlock()
+	return true, nil
+}
+
 func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) {
 	if c.pulledManifest == nil {
 		return nil, nil, errors.New("missing schema 1 manifest for conversion")

+ 1 - 1
vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go

@@ -147,7 +147,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
 			return errors.Wrap(err, "creating new NULL IO")
 		}
 	} else {
-		if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID); err != nil {
+		if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil {
 			return errors.Wrap(err, "failed to create runc io pipes")
 		}
 	}

+ 4 - 4
vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go

@@ -60,11 +60,11 @@ func (s *execCreatedState) Start(ctx context.Context) error {
 }
 
 func (s *execCreatedState) Delete(ctx context.Context) error {
-	s.p.mu.Lock()
-	defer s.p.mu.Unlock()
 	if err := s.p.delete(ctx); err != nil {
 		return err
 	}
+	s.p.mu.Lock()
+	defer s.p.mu.Unlock()
 	return s.transition("deleted")
 }
 
@@ -168,11 +168,11 @@ func (s *execStoppedState) Start(ctx context.Context) error {
 }
 
 func (s *execStoppedState) Delete(ctx context.Context) error {
-	s.p.mu.Lock()
-	defer s.p.mu.Unlock()
 	if err := s.p.delete(ctx); err != nil {
 		return err
 	}
+	s.p.mu.Lock()
+	defer s.p.mu.Unlock()
 	return s.transition("deleted")
 }
 

+ 10 - 3
vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go

@@ -123,7 +123,7 @@ func (p *Init) Create(ctx context.Context, r *CreateConfig) error {
 			return errors.Wrap(err, "creating new NULL IO")
 		}
 	} else {
-		if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID); err != nil {
+		if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID, withConditionalIO(p.stdio)); err != nil {
 			return errors.Wrap(err, "failed to create OCI runtime io pipes")
 		}
 	}
@@ -228,7 +228,7 @@ func (p *Init) Status(ctx context.Context) (string, error) {
 	defer p.mu.Unlock()
 	c, err := p.runtime.State(ctx, p.id)
 	if err != nil {
-		if os.IsNotExist(err) {
+		if strings.Contains(err.Error(), "does not exist") {
 			return "stopped", nil
 		}
 		return "", p.runtimeError(err, "OCI runtime state failed")
@@ -249,7 +249,6 @@ func (p *Init) setExited(status int) {
 }
 
 func (p *Init) delete(context context.Context) error {
-	p.KillAll(context)
 	p.wg.Wait()
 	err := p.runtime.Delete(context, p.id, nil)
 	// ignore errors if a runtime has already deleted the process
@@ -400,3 +399,11 @@ func (p *Init) runtimeError(rErr error, msg string) error {
 		return errors.Errorf("%s: %s", msg, rMsg)
 	}
 }
+
+func withConditionalIO(c proc.Stdio) runc.IOOpt {
+	return func(o *runc.IOOption) {
+		o.OpenStdin = c.Stdin != ""
+		o.OpenStdout = c.Stdout != ""
+		o.OpenStderr = c.Stderr != ""
+	}
+}

+ 0 - 1
vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go

@@ -109,7 +109,6 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
 		i.dest(fw, fr)
 	}
 	if stdin == "" {
-		rio.Stdin().Close()
 		return nil
 	}
 	f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)

+ 4 - 12
vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go

@@ -26,7 +26,6 @@ import (
 	"path/filepath"
 	"time"
 
-	"github.com/boltdb/bolt"
 	eventstypes "github.com/containerd/containerd/api/events"
 	"github.com/containerd/containerd/api/types"
 	"github.com/containerd/containerd/containers"
@@ -49,6 +48,7 @@ import (
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
+	bolt "go.etcd.io/bbolt"
 	"golang.org/x/sys/unix"
 )
 
@@ -204,7 +204,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
 				log.G(ctx).WithError(err).WithFields(logrus.Fields{
 					"id":        id,
 					"namespace": namespace,
-				}).Warn("failed to clen up after killed shim")
+				}).Warn("failed to clean up after killed shim")
 			}
 		}
 		shimopt = ShimRemote(r.config, r.address, cgroup, exitHandler)
@@ -248,8 +248,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
 	if err != nil {
 		return nil, errdefs.FromGRPC(err)
 	}
-	t, err := newTask(id, namespace, int(cr.Pid), s, r.events,
-		proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
+	t, err := newTask(id, namespace, int(cr.Pid), s, r.events, r.tasks, bundle)
 	if err != nil {
 		return nil, err
 	}
@@ -341,15 +340,8 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
 			}
 			continue
 		}
-		ropts, err := r.getRuncOptions(ctx, id)
-		if err != nil {
-			log.G(ctx).WithError(err).WithField("id", id).
-				Error("get runtime options")
-			continue
-		}
 
-		t, err := newTask(id, ns, pid, s, r.events,
-			proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
+		t, err := newTask(id, ns, pid, s, r.events, r.tasks, bundle)
 		if err != nil {
 			log.G(ctx).WithError(err).Error("loading task type")
 			continue

+ 2 - 3
vendor/github.com/containerd/containerd/runtime/v1/linux/task.go

@@ -31,8 +31,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/containerd/containerd/runtime"
 	"github.com/containerd/containerd/runtime/v1/shim/client"
-	shim "github.com/containerd/containerd/runtime/v1/shim/v1"
-	runc "github.com/containerd/go-runc"
+	"github.com/containerd/containerd/runtime/v1/shim/v1"
 	"github.com/containerd/ttrpc"
 	"github.com/containerd/typeurl"
 	"github.com/gogo/protobuf/types"
@@ -52,7 +51,7 @@ type Task struct {
 	bundle    *bundle
 }
 
-func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, runtime *runc.Runc, list *runtime.TaskList, bundle *bundle) (*Task, error) {
+func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, list *runtime.TaskList, bundle *bundle) (*Task, error) {
 	var (
 		err error
 		cg  cgroups.Cgroup

+ 40 - 7
vendor/github.com/containerd/containerd/runtime/v1/shim/service.go

@@ -20,7 +20,9 @@ package shim
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"sync"
@@ -41,6 +43,7 @@ import (
 	runc "github.com/containerd/go-runc"
 	"github.com/containerd/typeurl"
 	ptypes "github.com/gogo/protobuf/types"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
@@ -221,19 +224,21 @@ func (s *Service) Delete(ctx context.Context, r *ptypes.Empty) (*shimapi.DeleteR
 
 // DeleteProcess deletes an exec'd process
 func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) {
-	s.mu.Lock()
-	defer s.mu.Unlock()
 	if r.ID == s.id {
 		return nil, status.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess")
 	}
+	s.mu.Lock()
 	p := s.processes[r.ID]
+	s.mu.Unlock()
 	if p == nil {
 		return nil, errors.Wrapf(errdefs.ErrNotFound, "process %s", r.ID)
 	}
 	if err := p.Delete(ctx); err != nil {
 		return nil, err
 	}
+	s.mu.Lock()
 	delete(s.processes, r.ID)
+	s.mu.Unlock()
 	return &shimapi.DeleteResponse{
 		ExitStatus: uint32(p.ExitStatus()),
 		ExitedAt:   p.ExitedAt(),
@@ -507,13 +512,22 @@ func (s *Service) processExits() {
 func (s *Service) checkProcesses(e runc.Exit) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
+
+	shouldKillAll, err := shouldKillAllOnExit(s.bundle)
+	if err != nil {
+		log.G(s.context).WithError(err).Error("failed to check shouldKillAll")
+	}
+
 	for _, p := range s.processes {
 		if p.Pid() == e.Pid {
-			if ip, ok := p.(*proc.Init); ok {
-				// Ensure all children are killed
-				if err := ip.KillAll(s.context); err != nil {
-					log.G(s.context).WithError(err).WithField("id", ip.ID()).
-						Error("failed to kill init's children")
+
+			if shouldKillAll {
+				if ip, ok := p.(*proc.Init); ok {
+					// Ensure all children are killed
+					if err := ip.KillAll(s.context); err != nil {
+						log.G(s.context).WithError(err).WithField("id", ip.ID()).
+							Error("failed to kill init's children")
+					}
 				}
 			}
 			p.SetExited(e.Status)
@@ -529,6 +543,25 @@ func (s *Service) checkProcesses(e runc.Exit) {
 	}
 }
 
+func shouldKillAllOnExit(bundlePath string) (bool, error) {
+	var bundleSpec specs.Spec
+	bundleConfigContents, err := ioutil.ReadFile(filepath.Join(bundlePath, "config.json"))
+	if err != nil {
+		return false, err
+	}
+	json.Unmarshal(bundleConfigContents, &bundleSpec)
+
+	if bundleSpec.Linux != nil {
+		for _, ns := range bundleSpec.Linux.Namespaces {
+			if ns.Type == specs.PIDNamespace {
+				return false, nil
+			}
+		}
+	}
+
+	return true, nil
+}
+
 func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
 	s.mu.Lock()
 	defer s.mu.Unlock()

+ 1 - 1
vendor/github.com/containerd/containerd/services/server/server.go

@@ -29,7 +29,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/boltdb/bolt"
 	csapi "github.com/containerd/containerd/api/services/content/v1"
 	ssapi "github.com/containerd/containerd/api/services/snapshots/v1"
 	"github.com/containerd/containerd/content"
@@ -46,6 +45,7 @@ import (
 	metrics "github.com/docker/go-metrics"
 	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
 	"github.com/pkg/errors"
+	bolt "go.etcd.io/bbolt"
 	"google.golang.org/grpc"
 )
 

+ 1 - 1
vendor/github.com/containerd/containerd/sys/socket_unix.go

@@ -42,7 +42,7 @@ func CreateUnixSocket(path string) (net.Listener, error) {
 	return net.Listen("unix", path)
 }
 
-// GetLocalListener returns a listerner out of a unix socket.
+// GetLocalListener returns a listener out of a unix socket.
 func GetLocalListener(path string, uid, gid int) (net.Listener, error) {
 	// Ensure parent directory is created
 	if err := mkdirAs(filepath.Dir(path), uid, gid); err != nil {

+ 4 - 1
vendor/github.com/containerd/containerd/task.go

@@ -607,8 +607,11 @@ func writeContent(ctx context.Context, store content.Ingester, mediaType, ref st
 	if err != nil {
 		return d, err
 	}
+
 	if err := writer.Commit(ctx, size, "", opts...); err != nil {
-		return d, err
+		if !errdefs.IsAlreadyExists(err) {
+			return d, err
+		}
 	}
 	return v1.Descriptor{
 		MediaType: mediaType,

+ 62 - 0
vendor/github.com/containerd/containerd/task_opts.go

@@ -18,10 +18,18 @@ package containerd
 
 import (
 	"context"
+	"encoding/json"
+	"fmt"
 	"syscall"
 
+	"github.com/containerd/containerd/api/types"
+	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/errdefs"
+	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/mount"
+	imagespec "github.com/opencontainers/image-spec/specs-go/v1"
+	"github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
 )
 
 // NewTaskOpts allows the caller to set options on a new task
@@ -35,6 +43,44 @@ func WithRootFS(mounts []mount.Mount) NewTaskOpts {
 	}
 }
 
+// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a
+// previous checkpoint. Additional software such as CRIU may be required to
+// restore a task from a checkpoint
+func WithTaskCheckpoint(im Image) NewTaskOpts {
+	return func(ctx context.Context, c *Client, info *TaskInfo) error {
+		desc := im.Target()
+		id := desc.Digest
+		index, err := decodeIndex(ctx, c.ContentStore(), desc)
+		if err != nil {
+			return err
+		}
+		for _, m := range index.Manifests {
+			if m.MediaType == images.MediaTypeContainerd1Checkpoint {
+				info.Checkpoint = &types.Descriptor{
+					MediaType: m.MediaType,
+					Size_:     m.Size,
+					Digest:    m.Digest,
+				}
+				return nil
+			}
+		}
+		return fmt.Errorf("checkpoint not found in index %s", id)
+	}
+}
+
+func decodeIndex(ctx context.Context, store content.Provider, desc imagespec.Descriptor) (*imagespec.Index, error) {
+	var index imagespec.Index
+	p, err := content.ReadBlob(ctx, store, desc)
+	if err != nil {
+		return nil, err
+	}
+	if err := json.Unmarshal(p, &index); err != nil {
+		return nil, err
+	}
+
+	return &index, nil
+}
+
 // WithCheckpointName sets the image name for the checkpoint
 func WithCheckpointName(name string) CheckpointTaskOpts {
 	return func(r *CheckpointTaskInfo) error {
@@ -92,3 +138,19 @@ func WithKillExecID(execID string) KillOpts {
 		return nil
 	}
 }
+
+// WithResources sets the provided resources for task updates. Resources must be
+// either a *specs.LinuxResources or a *specs.WindowsResources
+func WithResources(resources interface{}) UpdateTaskOpts {
+	return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
+		switch resources.(type) {
+		case *specs.LinuxResources:
+		case *specs.WindowsResources:
+		default:
+			return errors.New("WithResources requires a *specs.LinuxResources or *specs.WindowsResources")
+		}
+
+		r.Resources = resources
+		return nil
+	}
+}

+ 19 - 10
vendor/github.com/containerd/containerd/task_opts_linux.go → vendor/github.com/containerd/containerd/task_opts_unix.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 /*
    Copyright The containerd Authors.
 
@@ -18,20 +20,11 @@ package containerd
 
 import (
 	"context"
-	"errors"
 
 	"github.com/containerd/containerd/runtime/linux/runctypes"
-	"github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
 )
 
-// WithResources sets the provided resources for task updates
-func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
-	return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
-		r.Resources = resources
-		return nil
-	}
-}
-
 // WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage.
 // There is an upper limit on the number of keyrings in a linux system
 func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error {
@@ -46,3 +39,19 @@ func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error {
 	opts.NoNewKeyring = true
 	return nil
 }
+
+// WithNoPivotRoot instructs the runtime not to you pivot_root
+func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error {
+	if info.Options == nil {
+		info.Options = &runctypes.CreateOptions{
+			NoPivotRoot: true,
+		}
+		return nil
+	}
+	opts, ok := info.Options.(*runctypes.CreateOptions)
+	if !ok {
+		return errors.New("invalid options type, expected runctypes.CreateOptions")
+	}
+	opts.NoPivotRoot = true
+	return nil
+}

+ 21 - 20
vendor/github.com/containerd/containerd/vendor.conf

@@ -1,10 +1,10 @@
-github.com/containerd/go-runc acb7c88cac264acca9b5eae187a117f4d77a1292
+github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
 github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244
-github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
+github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537
 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
@@ -19,7 +19,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0
 github.com/gogo/protobuf v1.0.0
 github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef
 github.com/golang/protobuf v1.1.0
-github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce # v1.0.1-43-gd810dbc
+github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d
 github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd
 github.com/sirupsen/logrus v1.0.0
 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
@@ -34,17 +34,17 @@ github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
 github.com/Microsoft/go-winio v0.4.10
 github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55
-github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
 github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d
 github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
 gotest.tools v2.1.0
 github.com/google/go-cmp v0.1.0
+go.etcd.io/bbolt v1.3.1-etcd.8
 
 # cri dependencies
-github.com/containerd/cri v1.11.1
-github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
+github.com/containerd/cri 9f39e3289533fc228c5e5fcac0a6dbdd60c6047b # release/1.2 branch
+github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd
 github.com/blang/semver v3.1.0
 github.com/containernetworking/cni v0.6.0
 github.com/containernetworking/plugins v0.7.0
@@ -52,32 +52,33 @@ github.com/davecgh/go-spew v1.1.0
 github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
 github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
 github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
-github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
-github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
+github.com/emicklei/go-restful v2.2.1
+github.com/ghodss/yaml v1.0.0
 github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed
 github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
 github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
 github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
-github.com/json-iterator/go f2b4162afba35581b6d4a50d3b8f34e33c144682
-github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd
+github.com/json-iterator/go 1.1.5
+github.com/modern-go/reflect2 1.0.1
 github.com/modern-go/concurrent 1.0.3
 github.com/opencontainers/runtime-tools v0.6.0
-github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
+github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a
 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
-github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc
+github.com/tchap/go-patricia v2.2.6
 github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6
 github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b
 github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
+golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
-gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77
-k8s.io/api 9e5ffd1f1320950b238cfce291b926411f0af722
-k8s.io/apimachinery ed135c5b96450fd24e5e981c708114fbbd950697
-k8s.io/apiserver a90e3a95c2e91b944bfca8225c4e0d12e42a9eb5
-k8s.io/client-go 03bfb9bdcfe5482795b999f39ca3ed9ad42ce5bb
-k8s.io/kubernetes v1.11.0
-k8s.io/utils 733eca437aa39379e4bcc25e726439dfca40fcff
+gopkg.in/yaml.v2 v2.2.1
+k8s.io/api 012f271b5d41baad56190c5f1ae19bff16df0fd8
+k8s.io/apimachinery 6429050ef506887d121f3e7306e894f8900d8a63
+k8s.io/apiserver e9312c15296b6c2c923ebd5031ff5d1d5fd022d7
+k8s.io/client-go 37c3c02ec96533daec0dbda1f39a6b1d68505c79
+k8s.io/kubernetes v1.12.0-beta.1
+k8s.io/utils 982821ea41da7e7c15f3d3738921eb2e7e241ccd
 
 # zfs dependencies
 github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec
@@ -85,4 +86,4 @@ github.com/mistifyio/go-zfs 166add352731e515512690329794ee593f1aaff2
 github.com/pborman/uuid c65b2f87fee37d1c7854c9164a450713c28d50cd
 
 # aufs dependencies
-github.com/containerd/aufs a7fbd554da7a9eafbe5a460a421313a9fd18d988
+github.com/containerd/aufs ffa39970e26ad01d81f540b21e65f9c1841a5f92

+ 657 - 0
vendor/github.com/containerd/continuity/context.go

@@ -0,0 +1,657 @@
+package continuity
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/containerd/continuity/devices"
+	driverpkg "github.com/containerd/continuity/driver"
+	"github.com/containerd/continuity/pathdriver"
+
+	"github.com/opencontainers/go-digest"
+)
+
+var (
+	// ErrNotFound represents the resource not found
+	ErrNotFound = fmt.Errorf("not found")
+	// ErrNotSupported represents the resource not supported
+	ErrNotSupported = fmt.Errorf("not supported")
+)
+
+// Context represents a file system context for accessing resources. The
+// responsibility of the context is to convert system specific resources to
+// generic Resource objects. Most of this is safe path manipulation, as well
+// as extraction of resource details.
+type Context interface {
+	Apply(Resource) error
+	Verify(Resource) error
+	Resource(string, os.FileInfo) (Resource, error)
+	Walk(filepath.WalkFunc) error
+}
+
+// SymlinkPath is intended to give the symlink target value
+// in a root context. Target and linkname are absolute paths
+// not under the given root.
+type SymlinkPath func(root, linkname, target string) (string, error)
+
+// ContextOptions represents options to create a new context.
+type ContextOptions struct {
+	Digester   Digester
+	Driver     driverpkg.Driver
+	PathDriver pathdriver.PathDriver
+	Provider   ContentProvider
+}
+
+// context represents a file system context for accessing resources.
+// Generally, all path qualified access and system considerations should land
+// here.
+type context struct {
+	driver     driverpkg.Driver
+	pathDriver pathdriver.PathDriver
+	root       string
+	digester   Digester
+	provider   ContentProvider
+}
+
+// NewContext returns a Context associated with root. The default driver will
+// be used, as returned by NewDriver.
+func NewContext(root string) (Context, error) {
+	return NewContextWithOptions(root, ContextOptions{})
+}
+
+// NewContextWithOptions returns a Context associate with the root.
+func NewContextWithOptions(root string, options ContextOptions) (Context, error) {
+	// normalize to absolute path
+	pathDriver := options.PathDriver
+	if pathDriver == nil {
+		pathDriver = pathdriver.LocalPathDriver
+	}
+
+	root = pathDriver.FromSlash(root)
+	root, err := pathDriver.Abs(pathDriver.Clean(root))
+	if err != nil {
+		return nil, err
+	}
+
+	driver := options.Driver
+	if driver == nil {
+		driver, err = driverpkg.NewSystemDriver()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	digester := options.Digester
+	if digester == nil {
+		digester = simpleDigester{digest.Canonical}
+	}
+
+	// Check the root directory. Need to be a little careful here. We are
+	// allowing a link for now, but this may have odd behavior when
+	// canonicalizing paths. As long as all files are opened through the link
+	// path, this should be okay.
+	fi, err := driver.Stat(root)
+	if err != nil {
+		return nil, err
+	}
+
+	if !fi.IsDir() {
+		return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid}
+	}
+
+	return &context{
+		root:       root,
+		driver:     driver,
+		pathDriver: pathDriver,
+		digester:   digester,
+		provider:   options.Provider,
+	}, nil
+}
+
+// Resource returns the resource as path p, populating the entry with info
+// from fi. The path p should be the path of the resource in the context,
+// typically obtained through Walk or from the value of Resource.Path(). If fi
+// is nil, it will be resolved.
+func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) {
+	fp, err := c.fullpath(p)
+	if err != nil {
+		return nil, err
+	}
+
+	if fi == nil {
+		fi, err = c.driver.Lstat(fp)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	base, err := newBaseResource(p, fi)
+	if err != nil {
+		return nil, err
+	}
+
+	base.xattrs, err = c.resolveXAttrs(fp, fi, base)
+	if err == ErrNotSupported {
+		log.Printf("resolving xattrs on %s not supported", fp)
+	} else if err != nil {
+		return nil, err
+	}
+
+	// TODO(stevvooe): Handle windows alternate data streams.
+
+	if fi.Mode().IsRegular() {
+		dgst, err := c.digest(p)
+		if err != nil {
+			return nil, err
+		}
+
+		return newRegularFile(*base, base.paths, fi.Size(), dgst)
+	}
+
+	if fi.Mode().IsDir() {
+		return newDirectory(*base)
+	}
+
+	if fi.Mode()&os.ModeSymlink != 0 {
+		// We handle relative links vs absolute links by including a
+		// beginning slash for absolute links. Effectively, the bundle's
+		// root is treated as the absolute link anchor.
+		target, err := c.driver.Readlink(fp)
+		if err != nil {
+			return nil, err
+		}
+
+		return newSymLink(*base, target)
+	}
+
+	if fi.Mode()&os.ModeNamedPipe != 0 {
+		return newNamedPipe(*base, base.paths)
+	}
+
+	if fi.Mode()&os.ModeDevice != 0 {
+		deviceDriver, ok := c.driver.(driverpkg.DeviceInfoDriver)
+		if !ok {
+			log.Printf("device extraction not supported %s", fp)
+			return nil, ErrNotSupported
+		}
+
+		// character and block devices merely need to recover the
+		// major/minor device number.
+		major, minor, err := deviceDriver.DeviceInfo(fi)
+		if err != nil {
+			return nil, err
+		}
+
+		return newDevice(*base, base.paths, major, minor)
+	}
+
+	log.Printf("%q (%v) is not supported", fp, fi.Mode())
+	return nil, ErrNotFound
+}
+
+func (c *context) verifyMetadata(resource, target Resource) error {
+	if target.Mode() != resource.Mode() {
+		return fmt.Errorf("resource %q has incorrect mode: %v != %v", target.Path(), target.Mode(), resource.Mode())
+	}
+
+	if target.UID() != resource.UID() {
+		return fmt.Errorf("unexpected uid for %q: %v != %v", target.Path(), target.UID(), resource.GID())
+	}
+
+	if target.GID() != resource.GID() {
+		return fmt.Errorf("unexpected gid for %q: %v != %v", target.Path(), target.GID(), target.GID())
+	}
+
+	if xattrer, ok := resource.(XAttrer); ok {
+		txattrer, tok := target.(XAttrer)
+		if !tok {
+			return fmt.Errorf("resource %q has xattrs but target does not support them", resource.Path())
+		}
+
+		// For xattrs, only ensure that we have those defined in the resource
+		// and their values match. We can ignore other xattrs. In other words,
+		// we only verify that target has the subset defined by resource.
+		txattrs := txattrer.XAttrs()
+		for attr, value := range xattrer.XAttrs() {
+			tvalue, ok := txattrs[attr]
+			if !ok {
+				return fmt.Errorf("resource %q target missing xattr %q", resource.Path(), attr)
+			}
+
+			if !bytes.Equal(value, tvalue) {
+				return fmt.Errorf("xattr %q value differs for resource %q", attr, resource.Path())
+			}
+		}
+	}
+
+	switch r := resource.(type) {
+	case RegularFile:
+		// TODO(stevvooe): Another reason to use a record-based approach. We
+		// have to do another type switch to get this to work. This could be
+		// fixed with an Equal function, but let's study this a little more to
+		// be sure.
+		t, ok := target.(RegularFile)
+		if !ok {
+			return fmt.Errorf("resource %q target not a regular file", r.Path())
+		}
+
+		if t.Size() != r.Size() {
+			return fmt.Errorf("resource %q target has incorrect size: %v != %v", t.Path(), t.Size(), r.Size())
+		}
+	case Directory:
+		t, ok := target.(Directory)
+		if !ok {
+			return fmt.Errorf("resource %q target not a directory", t.Path())
+		}
+	case SymLink:
+		t, ok := target.(SymLink)
+		if !ok {
+			return fmt.Errorf("resource %q target not a symlink", t.Path())
+		}
+
+		if t.Target() != r.Target() {
+			return fmt.Errorf("resource %q target has mismatched target: %q != %q", t.Path(), t.Target(), r.Target())
+		}
+	case Device:
+		t, ok := target.(Device)
+		if !ok {
+			return fmt.Errorf("resource %q is not a device", t.Path())
+		}
+
+		if t.Major() != r.Major() || t.Minor() != r.Minor() {
+			return fmt.Errorf("resource %q has mismatched major/minor numbers: %d,%d != %d,%d", t.Path(), t.Major(), t.Minor(), r.Major(), r.Minor())
+		}
+	case NamedPipe:
+		t, ok := target.(NamedPipe)
+		if !ok {
+			return fmt.Errorf("resource %q is not a named pipe", t.Path())
+		}
+	default:
+		return fmt.Errorf("cannot verify resource: %v", resource)
+	}
+
+	return nil
+}
+
+// Verify the resource in the context. An error will be returned a discrepancy
+// is found.
+func (c *context) Verify(resource Resource) error {
+	fp, err := c.fullpath(resource.Path())
+	if err != nil {
+		return err
+	}
+
+	fi, err := c.driver.Lstat(fp)
+	if err != nil {
+		return err
+	}
+
+	target, err := c.Resource(resource.Path(), fi)
+	if err != nil {
+		return err
+	}
+
+	if target.Path() != resource.Path() {
+		return fmt.Errorf("resource paths do not match: %q != %q", target.Path(), resource.Path())
+	}
+
+	if err := c.verifyMetadata(resource, target); err != nil {
+		return err
+	}
+
+	if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable {
+		hardlinkKey, err := newHardlinkKey(fi)
+		if err == errNotAHardLink {
+			if len(h.Paths()) > 1 {
+				return fmt.Errorf("%q is not a hardlink to %q", h.Paths()[1], resource.Path())
+			}
+		} else if err != nil {
+			return err
+		}
+
+		for _, path := range h.Paths()[1:] {
+			fpLink, err := c.fullpath(path)
+			if err != nil {
+				return err
+			}
+
+			fiLink, err := c.driver.Lstat(fpLink)
+			if err != nil {
+				return err
+			}
+
+			targetLink, err := c.Resource(path, fiLink)
+			if err != nil {
+				return err
+			}
+
+			hardlinkKeyLink, err := newHardlinkKey(fiLink)
+			if err != nil {
+				return err
+			}
+
+			if hardlinkKeyLink != hardlinkKey {
+				return fmt.Errorf("%q is not a hardlink to %q", path, resource.Path())
+			}
+
+			if err := c.verifyMetadata(resource, targetLink); err != nil {
+				return err
+			}
+		}
+	}
+
+	switch r := resource.(type) {
+	case RegularFile:
+		t, ok := target.(RegularFile)
+		if !ok {
+			return fmt.Errorf("resource %q target not a regular file", r.Path())
+		}
+
+		// TODO(stevvooe): This may need to get a little more sophisticated
+		// for digest comparison. We may want to actually calculate the
+		// provided digests, rather than the implementations having an
+		// overlap.
+		if !digestsMatch(t.Digests(), r.Digests()) {
+			return fmt.Errorf("digests for resource %q do not match: %v != %v", t.Path(), t.Digests(), r.Digests())
+		}
+	}
+
+	return nil
+}
+
+func (c *context) checkoutFile(fp string, rf RegularFile) error {
+	if c.provider == nil {
+		return fmt.Errorf("no file provider")
+	}
+	var (
+		r   io.ReadCloser
+		err error
+	)
+	for _, dgst := range rf.Digests() {
+		r, err = c.provider.Reader(dgst)
+		if err == nil {
+			break
+		}
+	}
+	if err != nil {
+		return fmt.Errorf("file content could not be provided: %v", err)
+	}
+	defer r.Close()
+
+	return atomicWriteFile(fp, r, rf.Size(), rf.Mode())
+}
+
+// Apply the resource to the contexts. An error will be returned if the
+// operation fails. Depending on the resource type, the resource may be
+// created. For resource that cannot be resolved, an error will be returned.
+func (c *context) Apply(resource Resource) error {
+	fp, err := c.fullpath(resource.Path())
+	if err != nil {
+		return err
+	}
+
+	if !strings.HasPrefix(fp, c.root) {
+		return fmt.Errorf("resource %v escapes root", resource)
+	}
+
+	var chmod = true
+	fi, err := c.driver.Lstat(fp)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			return err
+		}
+	}
+
+	switch r := resource.(type) {
+	case RegularFile:
+		if fi == nil {
+			if err := c.checkoutFile(fp, r); err != nil {
+				return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
+			}
+			chmod = false
+		} else {
+			if !fi.Mode().IsRegular() {
+				return fmt.Errorf("file %q should be a regular file, but is not", resource.Path())
+			}
+			if fi.Size() != r.Size() {
+				if err := c.checkoutFile(fp, r); err != nil {
+					return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
+				}
+			} else {
+				for _, dgst := range r.Digests() {
+					f, err := os.Open(fp)
+					if err != nil {
+						return fmt.Errorf("failure opening file for read %q: %v", resource.Path(), err)
+					}
+					compared, err := dgst.Algorithm().FromReader(f)
+					if err == nil && dgst != compared {
+						if err := c.checkoutFile(fp, r); err != nil {
+							return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
+						}
+						break
+					}
+					if err1 := f.Close(); err == nil {
+						err = err1
+					}
+					if err != nil {
+						return fmt.Errorf("error checking digest for %q: %v", resource.Path(), err)
+					}
+				}
+			}
+		}
+	case Directory:
+		if fi == nil {
+			if err := c.driver.Mkdir(fp, resource.Mode()); err != nil {
+				return err
+			}
+		} else if !fi.Mode().IsDir() {
+			return fmt.Errorf("%q should be a directory, but is not", resource.Path())
+		}
+
+	case SymLink:
+		var target string // only possibly set if target resource is a symlink
+
+		if fi != nil {
+			if fi.Mode()&os.ModeSymlink != 0 {
+				target, err = c.driver.Readlink(fp)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		if target != r.Target() {
+			if fi != nil {
+				if err := c.driver.Remove(fp); err != nil { // RemoveAll in case of directory?
+					return err
+				}
+			}
+
+			if err := c.driver.Symlink(r.Target(), fp); err != nil {
+				return err
+			}
+		}
+
+	case Device:
+		if fi == nil {
+			if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil {
+				return err
+			}
+		} else if (fi.Mode() & os.ModeDevice) == 0 {
+			return fmt.Errorf("%q should be a device, but is not", resource.Path())
+		} else {
+			major, minor, err := devices.DeviceInfo(fi)
+			if err != nil {
+				return err
+			}
+			if major != r.Major() || minor != r.Minor() {
+				if err := c.driver.Remove(fp); err != nil {
+					return err
+				}
+
+				if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil {
+					return err
+				}
+			}
+		}
+
+	case NamedPipe:
+		if fi == nil {
+			if err := c.driver.Mkfifo(fp, resource.Mode()); err != nil {
+				return err
+			}
+		} else if (fi.Mode() & os.ModeNamedPipe) == 0 {
+			return fmt.Errorf("%q should be a named pipe, but is not", resource.Path())
+		}
+	}
+
+	if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable {
+		for _, path := range h.Paths() {
+			if path == resource.Path() {
+				continue
+			}
+
+			lp, err := c.fullpath(path)
+			if err != nil {
+				return err
+			}
+
+			if _, fi := c.driver.Lstat(lp); fi == nil {
+				c.driver.Remove(lp)
+			}
+			if err := c.driver.Link(fp, lp); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Update filemode if file was not created
+	if chmod {
+		if err := c.driver.Lchmod(fp, resource.Mode()); err != nil {
+			return err
+		}
+	}
+
+	if err := c.driver.Lchown(fp, resource.UID(), resource.GID()); err != nil {
+		return err
+	}
+
+	if xattrer, ok := resource.(XAttrer); ok {
+		// For xattrs, only ensure that we have those defined in the resource
+		// and their values are set. We can ignore other xattrs. In other words,
+		// we only set xattres defined by resource but never remove.
+
+		if _, ok := resource.(SymLink); ok {
+			lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver)
+			if !ok {
+				return fmt.Errorf("unsupported symlink xattr for resource %q", resource.Path())
+			}
+			if err := lxattrDriver.LSetxattr(fp, xattrer.XAttrs()); err != nil {
+				return err
+			}
+		} else {
+			xattrDriver, ok := c.driver.(driverpkg.XAttrDriver)
+			if !ok {
+				return fmt.Errorf("unsupported xattr for resource %q", resource.Path())
+			}
+			if err := xattrDriver.Setxattr(fp, xattrer.XAttrs()); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+// Walk provides a convenience function to call filepath.Walk correctly for
+// the context. Otherwise identical to filepath.Walk, the path argument is
+// corrected to be contained within the context.
+func (c *context) Walk(fn filepath.WalkFunc) error {
+	root := c.root
+	fi, err := c.driver.Lstat(c.root)
+	if err == nil && fi.Mode()&os.ModeSymlink != 0 {
+		root, err = c.driver.Readlink(c.root)
+		if err != nil {
+			return err
+		}
+	}
+	return c.pathDriver.Walk(root, func(p string, fi os.FileInfo, err error) error {
+		contained, err := c.containWithRoot(p, root)
+		return fn(contained, fi, err)
+	})
+}
+
+// fullpath returns the system path for the resource, joined with the context
+// root. The path p must be a part of the context.
+func (c *context) fullpath(p string) (string, error) {
+	p = c.pathDriver.Join(c.root, p)
+	if !strings.HasPrefix(p, c.root) {
+		return "", fmt.Errorf("invalid context path")
+	}
+
+	return p, nil
+}
+
+// contain cleans and santizes the filesystem path p to be an absolute path,
+// effectively relative to the context root.
+func (c *context) contain(p string) (string, error) {
+	return c.containWithRoot(p, c.root)
+}
+
+// containWithRoot cleans and santizes the filesystem path p to be an absolute path,
+// effectively relative to the passed root. Extra care should be used when calling this
+// instead of contain. This is needed for Walk, as if context root is a symlink,
+// it must be evaluated prior to the Walk
+func (c *context) containWithRoot(p string, root string) (string, error) {
+	sanitized, err := c.pathDriver.Rel(root, p)
+	if err != nil {
+		return "", err
+	}
+
+	// ZOMBIES(stevvooe): In certain cases, we may want to remap these to a
+	// "containment error", so the caller can decide what to do.
+	return c.pathDriver.Join("/", c.pathDriver.Clean(sanitized)), nil
+}
+
+// digest returns the digest of the file at path p, relative to the root.
+func (c *context) digest(p string) (digest.Digest, error) {
+	f, err := c.driver.Open(c.pathDriver.Join(c.root, p))
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	return c.digester.Digest(f)
+}
+
+// resolveXAttrs attempts to resolve the extended attributes for the resource
+// at the path fp, which is the full path to the resource. If the resource
+// cannot have xattrs, nil will be returned.
+func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) {
+	if fi.Mode().IsRegular() || fi.Mode().IsDir() {
+		xattrDriver, ok := c.driver.(driverpkg.XAttrDriver)
+		if !ok {
+			log.Println("xattr extraction not supported")
+			return nil, ErrNotSupported
+		}
+
+		return xattrDriver.Getxattr(fp)
+	}
+
+	if fi.Mode()&os.ModeSymlink != 0 {
+		lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver)
+		if !ok {
+			log.Println("xattr extraction for symlinks not supported")
+			return nil, ErrNotSupported
+		}
+
+		return lxattrDriver.LGetxattr(fp)
+	}
+
+	return nil, nil
+}

+ 88 - 0
vendor/github.com/containerd/continuity/digests.go

@@ -0,0 +1,88 @@
+package continuity
+
+import (
+	"fmt"
+	"io"
+	"sort"
+
+	"github.com/opencontainers/go-digest"
+)
+
+// Digester produces a digest for a given read stream
+type Digester interface {
+	Digest(io.Reader) (digest.Digest, error)
+}
+
+// ContentProvider produces a read stream for a given digest
+type ContentProvider interface {
+	Reader(digest.Digest) (io.ReadCloser, error)
+}
+
+type simpleDigester struct {
+	algorithm digest.Algorithm
+}
+
+func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
+	digester := sd.algorithm.Digester()
+
+	if _, err := io.Copy(digester.Hash(), r); err != nil {
+		return "", err
+	}
+
+	return digester.Digest(), nil
+}
+
+// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
+// digests are not repeated and no two digests with the same algorithm have
+// different values. Because a stable sort is used, this has the effect of
+// "zipping" digest collections from multiple resources.
+func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
+	sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
+	seen := map[digest.Digest]struct{}{}
+	algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
+
+	var out []digest.Digest
+	// uniqify the digests
+	for _, d := range digests {
+		if _, ok := seen[d]; ok {
+			continue
+		}
+
+		seen[d] = struct{}{}
+		algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
+
+		if len(algs[d.Algorithm()]) > 1 {
+			return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
+		}
+
+		out = append(out, d)
+	}
+
+	return out, nil
+}
+
+// digestsMatch compares the two sets of digests to see if they match.
+func digestsMatch(as, bs []digest.Digest) bool {
+	all := append(as, bs...)
+
+	uniqified, err := uniqifyDigests(all...)
+	if err != nil {
+		// the only error uniqifyDigests returns is when the digests disagree.
+		return false
+	}
+
+	disjoint := len(as) + len(bs)
+	if len(uniqified) == disjoint {
+		// if these two sets have the same cardinality, we know both sides
+		// didn't share any digests.
+		return false
+	}
+
+	return true
+}
+
+type digestSlice []digest.Digest
+
+func (p digestSlice) Len() int           { return len(p) }
+func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
+func (p digestSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

+ 113 - 0
vendor/github.com/containerd/continuity/groups_unix.go

@@ -0,0 +1,113 @@
+package continuity
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+)
+
+// TODO(stevvooe): This needs a lot of work before we can call it useful.
+
+type groupIndex struct {
+	byName map[string]*group
+	byGID  map[int]*group
+}
+
+func getGroupIndex() (*groupIndex, error) {
+	f, err := os.Open("/etc/group")
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	groups, err := parseGroups(f)
+	if err != nil {
+		return nil, err
+	}
+
+	return newGroupIndex(groups), nil
+}
+
+func newGroupIndex(groups []group) *groupIndex {
+	gi := &groupIndex{
+		byName: make(map[string]*group),
+		byGID:  make(map[int]*group),
+	}
+
+	for i, group := range groups {
+		gi.byGID[group.gid] = &groups[i]
+		gi.byName[group.name] = &groups[i]
+	}
+
+	return gi
+}
+
+type group struct {
+	name    string
+	gid     int
+	members []string
+}
+
+func getGroupName(gid int) (string, error) {
+	f, err := os.Open("/etc/group")
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	groups, err := parseGroups(f)
+	if err != nil {
+		return "", err
+	}
+
+	for _, group := range groups {
+		if group.gid == gid {
+			return group.name, nil
+		}
+	}
+
+	return "", fmt.Errorf("no group for gid")
+}
+
+// parseGroups parses an /etc/group file for group names, ids and membership.
+// This is unix specific.
+func parseGroups(rd io.Reader) ([]group, error) {
+	var groups []group
+	scanner := bufio.NewScanner(rd)
+
+	for scanner.Scan() {
+		if strings.HasPrefix(scanner.Text(), "#") {
+			continue // skip comment
+		}
+
+		parts := strings.SplitN(scanner.Text(), ":", 4)
+
+		if len(parts) != 4 {
+			return nil, fmt.Errorf("bad entry: %q", scanner.Text())
+		}
+
+		name, _, sgid, smembers := parts[0], parts[1], parts[2], parts[3]
+
+		gid, err := strconv.Atoi(sgid)
+		if err != nil {
+			return nil, fmt.Errorf("bad gid: %q", gid)
+		}
+
+		members := strings.Split(smembers, ",")
+
+		groups = append(groups, group{
+			name:    name,
+			gid:     gid,
+			members: members,
+		})
+	}
+
+	if scanner.Err() != nil {
+		return nil, scanner.Err()
+	}
+
+	return groups, nil
+}

+ 57 - 0
vendor/github.com/containerd/continuity/hardlinks.go

@@ -0,0 +1,57 @@
+package continuity
+
+import (
+	"fmt"
+	"os"
+)
+
+var (
+	errNotAHardLink = fmt.Errorf("invalid hardlink")
+)
+
+type hardlinkManager struct {
+	hardlinks map[hardlinkKey][]Resource
+}
+
+func newHardlinkManager() *hardlinkManager {
+	return &hardlinkManager{
+		hardlinks: map[hardlinkKey][]Resource{},
+	}
+}
+
+// Add attempts to add the resource to the hardlink manager. If the resource
+// cannot be considered as a hardlink candidate, errNotAHardLink is returned.
+func (hlm *hardlinkManager) Add(fi os.FileInfo, resource Resource) error {
+	if _, ok := resource.(Hardlinkable); !ok {
+		return errNotAHardLink
+	}
+
+	key, err := newHardlinkKey(fi)
+	if err != nil {
+		return err
+	}
+
+	hlm.hardlinks[key] = append(hlm.hardlinks[key], resource)
+
+	return nil
+}
+
+// Merge processes the current state of the hardlink manager and merges any
+// shared nodes into hardlinked resources.
+func (hlm *hardlinkManager) Merge() ([]Resource, error) {
+	var resources []Resource
+	for key, linked := range hlm.hardlinks {
+		if len(linked) < 1 {
+			return nil, fmt.Errorf("no hardlink entrys for dev, inode pair: %#v", key)
+		}
+
+		merged, err := Merge(linked...)
+		if err != nil {
+			return nil, fmt.Errorf("error merging hardlink: %v", err)
+		}
+
+		resources = append(resources, merged)
+	}
+
+	return resources, nil
+}

+ 36 - 0
vendor/github.com/containerd/continuity/hardlinks_unix.go

@@ -0,0 +1,36 @@
+// +build linux darwin freebsd solaris
+
+package continuity
+
+import (
+	"fmt"
+	"os"
+	"syscall"
+)
+
+// hardlinkKey provides a tuple-key for managing hardlinks. This is system-
+// specific.
+type hardlinkKey struct {
+	dev   uint64
+	inode uint64
+}
+
+// newHardlinkKey returns a hardlink key for the provided file info. If the
+// resource does not represent a possible hardlink, errNotAHardLink will be
+// returned.
+func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) {
+	sys, ok := fi.Sys().(*syscall.Stat_t)
+	if !ok {
+		return hardlinkKey{}, fmt.Errorf("cannot resolve (*syscall.Stat_t) from os.FileInfo")
+	}
+
+	if sys.Nlink < 2 {
+		// NOTE(stevvooe): This is not always true for all filesystems. We
+		// should somehow detect this and provided a slow "polyfill" that
+		// leverages os.SameFile if we detect a filesystem where link counts
+		// is not really supported.
+		return hardlinkKey{}, errNotAHardLink
+	}
+
+	return hardlinkKey{dev: uint64(sys.Dev), inode: uint64(sys.Ino)}, nil
+}

+ 12 - 0
vendor/github.com/containerd/continuity/hardlinks_windows.go

@@ -0,0 +1,12 @@
+package continuity
+
+import "os"
+
+type hardlinkKey struct{}
+
+func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) {
+	// NOTE(stevvooe): Obviously, this is not yet implemented. However, the
+	// makings of an implementation are available in src/os/types_windows.go. More
+	// investigation needs to be done to figure out exactly how to do this.
+	return hardlinkKey{}, errNotAHardLink
+}

+ 47 - 0
vendor/github.com/containerd/continuity/ioutils.go

@@ -0,0 +1,47 @@
+package continuity
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+// AtomicWriteFile atomically writes data to a file by first writing to a
+// temp file and calling rename.
+func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
+	buf := bytes.NewBuffer(data)
+	return atomicWriteFile(filename, buf, int64(len(data)), perm)
+}
+
+// atomicWriteFile writes data to a file by first writing to a temp
+// file and calling rename.
+func atomicWriteFile(filename string, r io.Reader, dataSize int64, perm os.FileMode) error {
+	f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
+	if err != nil {
+		return err
+	}
+	err = os.Chmod(f.Name(), perm)
+	if err != nil {
+		f.Close()
+		return err
+	}
+	n, err := io.Copy(f, r)
+	if err == nil && n < dataSize {
+		f.Close()
+		return io.ErrShortWrite
+	}
+	if err != nil {
+		f.Close()
+		return err
+	}
+	if err := f.Sync(); err != nil {
+		f.Close()
+		return err
+	}
+	if err := f.Close(); err != nil {
+		return err
+	}
+	return os.Rename(f.Name(), filename)
+}

+ 144 - 0
vendor/github.com/containerd/continuity/manifest.go

@@ -0,0 +1,144 @@
+package continuity
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"sort"
+
+	pb "github.com/containerd/continuity/proto"
+	"github.com/golang/protobuf/proto"
+)
+
+// Manifest provides the contents of a manifest. Users of this struct should
+// not typically modify any fields directly.
+type Manifest struct {
+	// Resources specifies all the resources for a manifest in order by path.
+	Resources []Resource
+}
+
+func Unmarshal(p []byte) (*Manifest, error) {
+	var bm pb.Manifest
+
+	if err := proto.Unmarshal(p, &bm); err != nil {
+		return nil, err
+	}
+
+	var m Manifest
+	for _, b := range bm.Resource {
+		r, err := fromProto(b)
+		if err != nil {
+			return nil, err
+		}
+
+		m.Resources = append(m.Resources, r)
+	}
+
+	return &m, nil
+}
+
+func Marshal(m *Manifest) ([]byte, error) {
+	var bm pb.Manifest
+	for _, resource := range m.Resources {
+		bm.Resource = append(bm.Resource, toProto(resource))
+	}
+
+	return proto.Marshal(&bm)
+}
+
+func MarshalText(w io.Writer, m *Manifest) error {
+	var bm pb.Manifest
+	for _, resource := range m.Resources {
+		bm.Resource = append(bm.Resource, toProto(resource))
+	}
+
+	return proto.MarshalText(w, &bm)
+}
+
+// BuildManifest creates the manifest for the given context
+func BuildManifest(ctx Context) (*Manifest, error) {
+	resourcesByPath := map[string]Resource{}
+	hardlinks := newHardlinkManager()
+
+	if err := ctx.Walk(func(p string, fi os.FileInfo, err error) error {
+		if err != nil {
+			return fmt.Errorf("error walking %s: %v", p, err)
+		}
+
+		if p == string(os.PathSeparator) {
+			// skip root
+			return nil
+		}
+
+		resource, err := ctx.Resource(p, fi)
+		if err != nil {
+			if err == ErrNotFound {
+				return nil
+			}
+			log.Printf("error getting resource %q: %v", p, err)
+			return err
+		}
+
+		// add to the hardlink manager
+		if err := hardlinks.Add(fi, resource); err == nil {
+			// Resource has been accepted by hardlink manager so we don't add
+			// it to the resourcesByPath until we merge at the end.
+			return nil
+		} else if err != errNotAHardLink {
+			// handle any other case where we have a proper error.
+			return fmt.Errorf("adding hardlink %s: %v", p, err)
+		}
+
+		resourcesByPath[p] = resource
+
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	// merge and post-process the hardlinks.
+	hardlinked, err := hardlinks.Merge()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, resource := range hardlinked {
+		resourcesByPath[resource.Path()] = resource
+	}
+
+	var resources []Resource
+	for _, resource := range resourcesByPath {
+		resources = append(resources, resource)
+	}
+
+	sort.Stable(ByPath(resources))
+
+	return &Manifest{
+		Resources: resources,
+	}, nil
+}
+
+// VerifyManifest verifies all the resources in a manifest
+// against files from the given context.
+func VerifyManifest(ctx Context, manifest *Manifest) error {
+	for _, resource := range manifest.Resources {
+		if err := ctx.Verify(resource); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ApplyManifest applies on the resources in a manifest to
+// the given context.
+func ApplyManifest(ctx Context, manifest *Manifest) error {
+	for _, resource := range manifest.Resources {
+		if err := ctx.Apply(resource); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 3 - 0
vendor/github.com/containerd/continuity/proto/gen.go

@@ -0,0 +1,3 @@
+package proto
+
+//go:generate protoc --go_out=. manifest.proto

+ 181 - 0
vendor/github.com/containerd/continuity/proto/manifest.pb.go

@@ -0,0 +1,181 @@
+// Code generated by protoc-gen-go.
+// source: manifest.proto
+// DO NOT EDIT!
+
+/*
+Package proto is a generated protocol buffer package.
+
+It is generated from these files:
+	manifest.proto
+
+It has these top-level messages:
+	Manifest
+	Resource
+	XAttr
+	ADSEntry
+*/
+package proto
+
+import proto1 "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto1.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// Manifest specifies the entries in a container bundle, keyed and sorted by
+// path.
+type Manifest struct {
+	Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"`
+}
+
+func (m *Manifest) Reset()                    { *m = Manifest{} }
+func (m *Manifest) String() string            { return proto1.CompactTextString(m) }
+func (*Manifest) ProtoMessage()               {}
+func (*Manifest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Manifest) GetResource() []*Resource {
+	if m != nil {
+		return m.Resource
+	}
+	return nil
+}
+
+type Resource struct {
+	// Path specifies the path from the bundle root. If more than one
+	// path is present, the entry may represent a hardlink, rather than using
+	// a link target. The path format is operating system specific.
+	Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"`
+	// Uid specifies the user id for the resource.
+	Uid int64 `protobuf:"varint,2,opt,name=uid" json:"uid,omitempty"`
+	// Gid specifies the group id for the resource.
+	Gid int64 `protobuf:"varint,3,opt,name=gid" json:"gid,omitempty"`
+	// user and group are not currently used but their field numbers have been
+	// reserved for future use. As such, they are marked as deprecated.
+	User  string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"`
+	Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
+	// Mode defines the file mode and permissions. We've used the same
+	// bit-packing from Go's os package,
+	// http://golang.org/pkg/os/#FileMode, since they've done the work of
+	// creating a cross-platform layout.
+	Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"`
+	// Size specifies the size in bytes of the resource. This is only valid
+	// for regular files.
+	Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"`
+	// Digest specifies the content digest of the target file. Only valid for
+	// regular files. The strings are formatted in OCI style, i.e. <alg>:<encoded>.
+	// For detailed information about the format, please refer to OCI Image Spec:
+	// https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification
+	// The digests are sorted in lexical order and implementations may choose
+	// which algorithms they prefer.
+	Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"`
+	// Target defines the target of a hard or soft link. Absolute links start
+	// with a slash and specify the resource relative to the bundle root.
+	// Relative links do not start with a slash and are relative to the
+	// resource path.
+	Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"`
+	// Major specifies the major device number for character and block devices.
+	Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"`
+	// Minor specifies the minor device number for character and block devices.
+	Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"`
+	// Xattr provides storage for extended attributes for the target resource.
+	Xattr []*XAttr `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty"`
+	// Ads stores one or more alternate data streams for the target resource.
+	Ads []*ADSEntry `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty"`
+}
+
+func (m *Resource) Reset()                    { *m = Resource{} }
+func (m *Resource) String() string            { return proto1.CompactTextString(m) }
+func (*Resource) ProtoMessage()               {}
+func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Resource) GetXattr() []*XAttr {
+	if m != nil {
+		return m.Xattr
+	}
+	return nil
+}
+
+func (m *Resource) GetAds() []*ADSEntry {
+	if m != nil {
+		return m.Ads
+	}
+	return nil
+}
+
+// XAttr encodes extended attributes for a resource.
+type XAttr struct {
+	// Name specifies the attribute name.
+	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Data specifies the associated data for the attribute.
+	Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+}
+
+func (m *XAttr) Reset()                    { *m = XAttr{} }
+func (m *XAttr) String() string            { return proto1.CompactTextString(m) }
+func (*XAttr) ProtoMessage()               {}
+func (*XAttr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+// ADSEntry encodes information for a Windows Alternate Data Stream.
+type ADSEntry struct {
+	// Name specifices the stream name.
+	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Data specifies the stream data.
+	// See also the description about the digest below.
+	Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+	// Digest is a CAS representation of the stream data.
+	//
+	// At least one of data or digest MUST be specified, and either one of them
+	// SHOULD be specified.
+	//
+	// How to access the actual data using the digest is implementation-specific,
+	// and implementations can choose not to implement digest.
+	// So, digest SHOULD be used only when the stream data is large.
+	Digest string `protobuf:"bytes,3,opt,name=digest" json:"digest,omitempty"`
+}
+
+func (m *ADSEntry) Reset()                    { *m = ADSEntry{} }
+func (m *ADSEntry) String() string            { return proto1.CompactTextString(m) }
+func (*ADSEntry) ProtoMessage()               {}
+func (*ADSEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func init() {
+	proto1.RegisterType((*Manifest)(nil), "proto.Manifest")
+	proto1.RegisterType((*Resource)(nil), "proto.Resource")
+	proto1.RegisterType((*XAttr)(nil), "proto.XAttr")
+	proto1.RegisterType((*ADSEntry)(nil), "proto.ADSEntry")
+}
+
+func init() { proto1.RegisterFile("manifest.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 317 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x4f, 0x4b, 0xf3, 0x40,
+	0x10, 0xc6, 0x49, 0x93, 0xf4, 0x4d, 0xa7, 0xed, 0xab, 0x2c, 0x52, 0xe6, 0x18, 0x73, 0x0a, 0x08,
+	0x15, 0xf4, 0xe0, 0xb9, 0xa2, 0x17, 0xc1, 0xcb, 0x7a, 0xf1, 0xba, 0xba, 0x6b, 0x5c, 0x21, 0xd9,
+	0xb0, 0xd9, 0x80, 0xfa, 0xe5, 0xfc, 0x6a, 0x32, 0xb3, 0x69, 0xd1, 0x9b, 0xa7, 0x3c, 0xcf, 0x6f,
+	0xfe, 0x64, 0xf6, 0x81, 0xff, 0xad, 0xea, 0xec, 0x8b, 0x19, 0xc2, 0xb6, 0xf7, 0x2e, 0x38, 0x91,
+	0xf3, 0xa7, 0xba, 0x82, 0xe2, 0x7e, 0x2a, 0x88, 0x33, 0x28, 0xbc, 0x19, 0xdc, 0xe8, 0x9f, 0x0d,
+	0x26, 0x65, 0x5a, 0x2f, 0x2f, 0x8e, 0x62, 0xf3, 0x56, 0x4e, 0x58, 0x1e, 0x1a, 0xaa, 0xaf, 0x19,
+	0x14, 0x7b, 0x2c, 0x04, 0x64, 0xbd, 0x0a, 0xaf, 0x3c, 0xb5, 0x90, 0xac, 0xc5, 0x31, 0xa4, 0xa3,
+	0xd5, 0x38, 0x2b, 0x93, 0x3a, 0x95, 0x24, 0x89, 0x34, 0x56, 0x63, 0x1a, 0x49, 0x63, 0xb5, 0xd8,
+	0x40, 0x36, 0x0e, 0xc6, 0x63, 0x56, 0x26, 0xf5, 0xe2, 0x7a, 0x86, 0x89, 0x64, 0x2f, 0x10, 0xf2,
+	0xc6, 0xbb, 0xb1, 0xc7, 0xfc, 0x50, 0x88, 0x80, 0xfe, 0xd4, 0x3a, 0x6d, 0x70, 0x5e, 0x26, 0xf5,
+	0x5a, 0xb2, 0x26, 0x36, 0xd8, 0x4f, 0x83, 0xff, 0xca, 0xa4, 0xce, 0x24, 0x6b, 0xb1, 0x81, 0xb9,
+	0xb6, 0x8d, 0x19, 0x02, 0x16, 0x7c, 0xd3, 0xe4, 0x88, 0x07, 0xe5, 0x1b, 0x13, 0x70, 0x41, 0xab,
+	0xe5, 0xe4, 0xc4, 0x09, 0xe4, 0xad, 0x7a, 0x73, 0x1e, 0x81, 0x97, 0x44, 0xc3, 0xd4, 0x76, 0xce,
+	0xe3, 0x72, 0xa2, 0x64, 0x44, 0x05, 0xf9, 0xbb, 0x0a, 0xc1, 0xe3, 0x8a, 0x43, 0x5a, 0x4d, 0x21,
+	0x3d, 0xee, 0x42, 0xf0, 0x32, 0x96, 0xc4, 0x29, 0xa4, 0x4a, 0x0f, 0xb8, 0xfe, 0x15, 0xe3, 0xee,
+	0xe6, 0xe1, 0xb6, 0x0b, 0xfe, 0x43, 0x52, 0xad, 0x3a, 0x87, 0x9c, 0x47, 0xe8, 0xfe, 0x4e, 0xb5,
+	0x94, 0x39, 0x5d, 0xc4, 0x9a, 0x98, 0x56, 0x41, 0x71, 0x7c, 0x2b, 0xc9, 0xba, 0xba, 0x83, 0x62,
+	0xbf, 0xe1, 0xaf, 0x33, 0x3f, 0x72, 0x48, 0xe3, 0x7b, 0xa3, 0x7b, 0x9a, 0xf3, 0x45, 0x97, 0xdf,
+	0x01, 0x00, 0x00, 0xff, 0xff, 0xef, 0x27, 0x99, 0xf7, 0x17, 0x02, 0x00, 0x00,
+}

+ 97 - 0
vendor/github.com/containerd/continuity/proto/manifest.proto

@@ -0,0 +1,97 @@
+syntax = "proto3";
+
+package proto;
+
+// Manifest specifies the entries in a container bundle, keyed and sorted by
+// path.
+message Manifest {
+    repeated Resource resource = 1;
+}
+
+message Resource {
+    // Path specifies the path from the bundle root. If more than one
+    // path is present, the entry may represent a hardlink, rather than using
+    // a link target. The path format is operating system specific.
+    repeated string path = 1;
+
+    // NOTE(stevvooe): Need to define clear precedence for user/group/uid/gid precedence.
+
+    // Uid specifies the user id for the resource.
+    int64 uid = 2;
+
+    // Gid specifies the group id for the resource.
+    int64 gid = 3;
+
+    // user and group are not currently used but their field numbers have been
+    // reserved for future use. As such, they are marked as deprecated.
+    string user = 4 [deprecated=true]; // "deprecated" stands for "reserved" here
+    string group = 5 [deprecated=true]; // "deprecated" stands for "reserved" here
+
+    // Mode defines the file mode and permissions. We've used the same
+    // bit-packing from Go's os package,
+    // http://golang.org/pkg/os/#FileMode, since they've done the work of
+    // creating a cross-platform layout.
+    uint32 mode = 6;
+
+    // NOTE(stevvooe): Beyond here, we start defining type specific fields.
+
+    // Size specifies the size in bytes of the resource. This is only valid
+    // for regular files.
+    uint64 size = 7;
+
+    // Digest specifies the content digest of the target file. Only valid for
+    // regular files. The strings are formatted in OCI style, i.e. <alg>:<encoded>.
+    // For detailed information about the format, please refer to OCI Image Spec:
+    // https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification
+    // The digests are sorted in lexical order and implementations may choose
+    // which algorithms they prefer.
+    repeated string digest = 8;
+
+    // Target defines the target of a hard or soft link. Absolute links start
+    // with a slash and specify the resource relative to the bundle root.
+    // Relative links do not start with a slash and are relative to the
+    // resource path.
+    string target = 9;
+
+    // Major specifies the major device number for character and block devices.
+    uint64 major = 10;
+
+    // Minor specifies the minor device number for character and block devices.
+    uint64 minor = 11;
+
+    // Xattr provides storage for extended attributes for the target resource.
+    repeated XAttr xattr = 12;
+
+    // Ads stores one or more alternate data streams for the target resource.
+    repeated ADSEntry ads = 13;
+
+}
+
+// XAttr encodes extended attributes for a resource.
+message XAttr {
+    // Name specifies the attribute name.
+    string name = 1;
+
+    // Data specifies the associated data for the attribute.
+    bytes data = 2;
+}
+
+// ADSEntry encodes information for a Windows Alternate Data Stream.
+message ADSEntry {
+    // Name specifices the stream name.
+    string name = 1;
+
+    // Data specifies the stream data.
+    // See also the description about the digest below.
+    bytes data = 2;
+
+    // Digest is a CAS representation of the stream data.
+    //
+    // At least one of data or digest MUST be specified, and either one of them
+    // SHOULD be specified.
+    //
+    // How to access the actual data using the digest is implementation-specific,
+    // and implementations can choose not to implement digest.
+    // So, digest SHOULD be used only when the stream data is large.
+    string digest = 3;
+}

+ 574 - 0
vendor/github.com/containerd/continuity/resource.go

@@ -0,0 +1,574 @@
+package continuity
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"reflect"
+	"sort"
+
+	pb "github.com/containerd/continuity/proto"
+	"github.com/opencontainers/go-digest"
+)
+
+// TODO(stevvooe): A record based model, somewhat sketched out at the bottom
+// of this file, will be more flexible. Another possibly is to tie the package
+// interface directly to the protobuf type. This will have efficiency
+// advantages at the cost coupling the nasty codegen types to the exported
+// interface.
+
+type Resource interface {
+	// Path provides the primary resource path relative to the bundle root. In
+	// cases where resources have more than one path, such as with hard links,
+	// this will return the primary path, which is often just the first entry.
+	Path() string
+
+	// Mode returns the
+	Mode() os.FileMode
+
+	UID() int64
+	GID() int64
+}
+
+// ByPath provides the canonical sort order for a set of resources. Use with
+// sort.Stable for deterministic sorting.
+type ByPath []Resource
+
+func (bp ByPath) Len() int           { return len(bp) }
+func (bp ByPath) Swap(i, j int)      { bp[i], bp[j] = bp[j], bp[i] }
+func (bp ByPath) Less(i, j int) bool { return bp[i].Path() < bp[j].Path() }
+
+type XAttrer interface {
+	XAttrs() map[string][]byte
+}
+
+// Hardlinkable is an interface that a resource type satisfies if it can be a
+// hardlink target.
+type Hardlinkable interface {
+	// Paths returns all paths of the resource, including the primary path
+	// returned by Resource.Path. If len(Paths()) > 1, the resource is a hard
+	// link.
+	Paths() []string
+}
+
+type RegularFile interface {
+	Resource
+	XAttrer
+	Hardlinkable
+
+	Size() int64
+	Digests() []digest.Digest
+}
+
+// Merge two or more Resources into new file. Typically, this should be
+// used to merge regular files as hardlinks. If the files are not identical,
+// other than Paths and Digests, the merge will fail and an error will be
+// returned.
+func Merge(fs ...Resource) (Resource, error) {
+	if len(fs) < 1 {
+		return nil, fmt.Errorf("please provide a resource to merge")
+	}
+
+	if len(fs) == 1 {
+		return fs[0], nil
+	}
+
+	var paths []string
+	var digests []digest.Digest
+	bypath := map[string][]Resource{}
+
+	// The attributes are all compared against the first to make sure they
+	// agree before adding to the above collections. If any of these don't
+	// correctly validate, the merge fails.
+	prototype := fs[0]
+	xattrs := make(map[string][]byte)
+
+	// initialize xattrs for use below. All files must have same xattrs.
+	if prototypeXAttrer, ok := prototype.(XAttrer); ok {
+		for attr, value := range prototypeXAttrer.XAttrs() {
+			xattrs[attr] = value
+		}
+	}
+
+	for _, f := range fs {
+		h, isHardlinkable := f.(Hardlinkable)
+		if !isHardlinkable {
+			return nil, errNotAHardLink
+		}
+
+		if f.Mode() != prototype.Mode() {
+			return nil, fmt.Errorf("modes do not match: %v != %v", f.Mode(), prototype.Mode())
+		}
+
+		if f.UID() != prototype.UID() {
+			return nil, fmt.Errorf("uid does not match: %v != %v", f.UID(), prototype.UID())
+		}
+
+		if f.GID() != prototype.GID() {
+			return nil, fmt.Errorf("gid does not match: %v != %v", f.GID(), prototype.GID())
+		}
+
+		if xattrer, ok := f.(XAttrer); ok {
+			fxattrs := xattrer.XAttrs()
+			if !reflect.DeepEqual(fxattrs, xattrs) {
+				return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", f, fxattrs, xattrs)
+			}
+		}
+
+		for _, p := range h.Paths() {
+			pfs, ok := bypath[p]
+			if !ok {
+				// ensure paths are unique by only appending on a new path.
+				paths = append(paths, p)
+			}
+
+			bypath[p] = append(pfs, f)
+		}
+
+		if regFile, isRegFile := f.(RegularFile); isRegFile {
+			prototypeRegFile, prototypeIsRegFile := prototype.(RegularFile)
+			if !prototypeIsRegFile {
+				return nil, errors.New("prototype is not a regular file")
+			}
+
+			if regFile.Size() != prototypeRegFile.Size() {
+				return nil, fmt.Errorf("size does not match: %v != %v", regFile.Size(), prototypeRegFile.Size())
+			}
+
+			digests = append(digests, regFile.Digests()...)
+		} else if device, isDevice := f.(Device); isDevice {
+			prototypeDevice, prototypeIsDevice := prototype.(Device)
+			if !prototypeIsDevice {
+				return nil, errors.New("prototype is not a device")
+			}
+
+			if device.Major() != prototypeDevice.Major() {
+				return nil, fmt.Errorf("major number does not match: %v != %v", device.Major(), prototypeDevice.Major())
+			}
+			if device.Minor() != prototypeDevice.Minor() {
+				return nil, fmt.Errorf("minor number does not match: %v != %v", device.Minor(), prototypeDevice.Minor())
+			}
+		} else if _, isNamedPipe := f.(NamedPipe); isNamedPipe {
+			_, prototypeIsNamedPipe := prototype.(NamedPipe)
+			if !prototypeIsNamedPipe {
+				return nil, errors.New("prototype is not a named pipe")
+			}
+		} else {
+			return nil, errNotAHardLink
+		}
+	}
+
+	sort.Stable(sort.StringSlice(paths))
+
+	// Choose a "canonical" file. Really, it is just the first file to sort
+	// against. We also effectively select the very first digest as the
+	// "canonical" one for this file.
+	first := bypath[paths[0]][0]
+
+	resource := resource{
+		paths:  paths,
+		mode:   first.Mode(),
+		uid:    first.UID(),
+		gid:    first.GID(),
+		xattrs: xattrs,
+	}
+
+	switch typedF := first.(type) {
+	case RegularFile:
+		var err error
+		digests, err = uniqifyDigests(digests...)
+		if err != nil {
+			return nil, err
+		}
+
+		return &regularFile{
+			resource: resource,
+			size:     typedF.Size(),
+			digests:  digests,
+		}, nil
+	case Device:
+		return &device{
+			resource: resource,
+			major:    typedF.Major(),
+			minor:    typedF.Minor(),
+		}, nil
+
+	case NamedPipe:
+		return &namedPipe{
+			resource: resource,
+		}, nil
+
+	default:
+		return nil, errNotAHardLink
+	}
+}
+
+type Directory interface {
+	Resource
+	XAttrer
+
+	// Directory is a no-op method to identify directory objects by interface.
+	Directory()
+}
+
+type SymLink interface {
+	Resource
+
+	// Target returns the target of the symlink contained in the .
+	Target() string
+}
+
+type NamedPipe interface {
+	Resource
+	Hardlinkable
+	XAttrer
+
+	// Pipe is a no-op method to allow consistent resolution of NamedPipe
+	// interface.
+	Pipe()
+}
+
+type Device interface {
+	Resource
+	Hardlinkable
+	XAttrer
+
+	Major() uint64
+	Minor() uint64
+}
+
+type resource struct {
+	paths    []string
+	mode     os.FileMode
+	uid, gid int64
+	xattrs   map[string][]byte
+}
+
+var _ Resource = &resource{}
+
+func (r *resource) Path() string {
+	if len(r.paths) < 1 {
+		return ""
+	}
+
+	return r.paths[0]
+}
+
+func (r *resource) Mode() os.FileMode {
+	return r.mode
+}
+
+func (r *resource) UID() int64 {
+	return r.uid
+}
+
+func (r *resource) GID() int64 {
+	return r.gid
+}
+
+type regularFile struct {
+	resource
+	size    int64
+	digests []digest.Digest
+}
+
+var _ RegularFile = &regularFile{}
+
+// newRegularFile returns the RegularFile, using the populated base resource
+// and one or more digests of the content.
+func newRegularFile(base resource, paths []string, size int64, dgsts ...digest.Digest) (RegularFile, error) {
+	if !base.Mode().IsRegular() {
+		return nil, fmt.Errorf("not a regular file")
+	}
+
+	base.paths = make([]string, len(paths))
+	copy(base.paths, paths)
+
+	// make our own copy of digests
+	ds := make([]digest.Digest, len(dgsts))
+	copy(ds, dgsts)
+
+	return &regularFile{
+		resource: base,
+		size:     size,
+		digests:  ds,
+	}, nil
+}
+
+func (rf *regularFile) Paths() []string {
+	paths := make([]string, len(rf.paths))
+	copy(paths, rf.paths)
+	return paths
+}
+
+func (rf *regularFile) Size() int64 {
+	return rf.size
+}
+
+func (rf *regularFile) Digests() []digest.Digest {
+	digests := make([]digest.Digest, len(rf.digests))
+	copy(digests, rf.digests)
+	return digests
+}
+
+func (rf *regularFile) XAttrs() map[string][]byte {
+	xattrs := make(map[string][]byte, len(rf.xattrs))
+
+	for attr, value := range rf.xattrs {
+		xattrs[attr] = append(xattrs[attr], value...)
+	}
+
+	return xattrs
+}
+
+type directory struct {
+	resource
+}
+
+var _ Directory = &directory{}
+
+func newDirectory(base resource) (Directory, error) {
+	if !base.Mode().IsDir() {
+		return nil, fmt.Errorf("not a directory")
+	}
+
+	return &directory{
+		resource: base,
+	}, nil
+}
+
+func (d *directory) Directory() {}
+
+func (d *directory) XAttrs() map[string][]byte {
+	xattrs := make(map[string][]byte, len(d.xattrs))
+
+	for attr, value := range d.xattrs {
+		xattrs[attr] = append(xattrs[attr], value...)
+	}
+
+	return xattrs
+}
+
+type symLink struct {
+	resource
+	target string
+}
+
+var _ SymLink = &symLink{}
+
+func newSymLink(base resource, target string) (SymLink, error) {
+	if base.Mode()&os.ModeSymlink == 0 {
+		return nil, fmt.Errorf("not a symlink")
+	}
+
+	return &symLink{
+		resource: base,
+		target:   target,
+	}, nil
+}
+
+func (l *symLink) Target() string {
+	return l.target
+}
+
+type namedPipe struct {
+	resource
+}
+
+var _ NamedPipe = &namedPipe{}
+
+func newNamedPipe(base resource, paths []string) (NamedPipe, error) {
+	if base.Mode()&os.ModeNamedPipe == 0 {
+		return nil, fmt.Errorf("not a namedpipe")
+	}
+
+	base.paths = make([]string, len(paths))
+	copy(base.paths, paths)
+
+	return &namedPipe{
+		resource: base,
+	}, nil
+}
+
+func (np *namedPipe) Pipe() {}
+
+func (np *namedPipe) Paths() []string {
+	paths := make([]string, len(np.paths))
+	copy(paths, np.paths)
+	return paths
+}
+
+func (np *namedPipe) XAttrs() map[string][]byte {
+	xattrs := make(map[string][]byte, len(np.xattrs))
+
+	for attr, value := range np.xattrs {
+		xattrs[attr] = append(xattrs[attr], value...)
+	}
+
+	return xattrs
+}
+
+type device struct {
+	resource
+	major, minor uint64
+}
+
+var _ Device = &device{}
+
+func newDevice(base resource, paths []string, major, minor uint64) (Device, error) {
+	if base.Mode()&os.ModeDevice == 0 {
+		return nil, fmt.Errorf("not a device")
+	}
+
+	base.paths = make([]string, len(paths))
+	copy(base.paths, paths)
+
+	return &device{
+		resource: base,
+		major:    major,
+		minor:    minor,
+	}, nil
+}
+
+func (d *device) Paths() []string {
+	paths := make([]string, len(d.paths))
+	copy(paths, d.paths)
+	return paths
+}
+
+func (d *device) XAttrs() map[string][]byte {
+	xattrs := make(map[string][]byte, len(d.xattrs))
+
+	for attr, value := range d.xattrs {
+		xattrs[attr] = append(xattrs[attr], value...)
+	}
+
+	return xattrs
+}
+
+func (d device) Major() uint64 {
+	return d.major
+}
+
+func (d device) Minor() uint64 {
+	return d.minor
+}
+
+// toProto converts a resource to a protobuf record. We'd like to push this
+// the individual types but we want to keep this all together during
+// prototyping.
+func toProto(resource Resource) *pb.Resource {
+	b := &pb.Resource{
+		Path: []string{resource.Path()},
+		Mode: uint32(resource.Mode()),
+		Uid:  resource.UID(),
+		Gid:  resource.GID(),
+	}
+
+	if xattrer, ok := resource.(XAttrer); ok {
+		// Sorts the XAttrs by name for consistent ordering.
+		keys := []string{}
+		xattrs := xattrer.XAttrs()
+		for k := range xattrs {
+			keys = append(keys, k)
+		}
+		sort.Strings(keys)
+
+		for _, k := range keys {
+			b.Xattr = append(b.Xattr, &pb.XAttr{Name: k, Data: xattrs[k]})
+		}
+	}
+
+	switch r := resource.(type) {
+	case RegularFile:
+		b.Path = r.Paths()
+		b.Size = uint64(r.Size())
+
+		for _, dgst := range r.Digests() {
+			b.Digest = append(b.Digest, dgst.String())
+		}
+	case SymLink:
+		b.Target = r.Target()
+	case Device:
+		b.Major, b.Minor = r.Major(), r.Minor()
+		b.Path = r.Paths()
+	case NamedPipe:
+		b.Path = r.Paths()
+	}
+
+	// enforce a few stability guarantees that may not be provided by the
+	// resource implementation.
+	sort.Strings(b.Path)
+
+	return b
+}
+
+// fromProto converts from a protobuf Resource to a Resource interface.
+func fromProto(b *pb.Resource) (Resource, error) {
+	base := &resource{
+		paths: b.Path,
+		mode:  os.FileMode(b.Mode),
+		uid:   b.Uid,
+		gid:   b.Gid,
+	}
+
+	base.xattrs = make(map[string][]byte, len(b.Xattr))
+
+	for _, attr := range b.Xattr {
+		base.xattrs[attr.Name] = attr.Data
+	}
+
+	switch {
+	case base.Mode().IsRegular():
+		dgsts := make([]digest.Digest, len(b.Digest))
+		for i, dgst := range b.Digest {
+			// TODO(stevvooe): Should we be validating at this point?
+			dgsts[i] = digest.Digest(dgst)
+		}
+
+		return newRegularFile(*base, b.Path, int64(b.Size), dgsts...)
+	case base.Mode().IsDir():
+		return newDirectory(*base)
+	case base.Mode()&os.ModeSymlink != 0:
+		return newSymLink(*base, b.Target)
+	case base.Mode()&os.ModeNamedPipe != 0:
+		return newNamedPipe(*base, b.Path)
+	case base.Mode()&os.ModeDevice != 0:
+		return newDevice(*base, b.Path, b.Major, b.Minor)
+	}
+
+	return nil, fmt.Errorf("unknown resource record (%#v): %s", b, base.Mode())
+}
+
+// NOTE(stevvooe): An alternative model that supports inline declaration.
+// Convenient for unit testing where inline declarations may be desirable but
+// creates an awkward API for the standard use case.
+
+// type ResourceKind int
+
+// const (
+// 	ResourceRegularFile = iota + 1
+// 	ResourceDirectory
+// 	ResourceSymLink
+// 	Resource
+// )
+
+// type Resource struct {
+// 	Kind         ResourceKind
+// 	Paths        []string
+// 	Mode         os.FileMode
+// 	UID          string
+// 	GID          string
+// 	Size         int64
+// 	Digests      []digest.Digest
+// 	Target       string
+// 	Major, Minor int
+// 	XAttrs       map[string][]byte
+// }
+
+// type RegularFile struct {
+// 	Paths   []string
+//  Size 	int64
+// 	Digests []digest.Digest
+// 	Perm    os.FileMode // os.ModePerm + sticky, setuid, setgid
+// }

+ 37 - 0
vendor/github.com/containerd/continuity/resource_unix.go

@@ -0,0 +1,37 @@
+// +build linux darwin freebsd solaris
+
+package continuity
+
+import (
+	"fmt"
+	"os"
+	"syscall"
+)
+
+// newBaseResource returns a *resource, populated with data from p and fi,
+// where p will be populated directly.
+func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
+	// TODO(stevvooe): This need to be resolved for the container's root,
+	// where here we are really getting the host OS's value. We need to allow
+	// this be passed in and fixed up to make these uid/gid mappings portable.
+	// Either this can be part of the driver or we can achieve it through some
+	// other mechanism.
+	sys, ok := fi.Sys().(*syscall.Stat_t)
+	if !ok {
+		// TODO(stevvooe): This may not be a hard error for all platforms. We
+		// may want to move this to the driver.
+		return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi)
+	}
+
+	return &resource{
+		paths: []string{p},
+		mode:  fi.Mode(),
+
+		uid: int64(sys.Uid),
+		gid: int64(sys.Gid),
+
+		// NOTE(stevvooe): Population of shared xattrs field is deferred to
+		// the resource types that populate it. Since they are a property of
+		// the context, they must set there.
+	}, nil
+}

+ 12 - 0
vendor/github.com/containerd/continuity/resource_windows.go

@@ -0,0 +1,12 @@
+package continuity
+
+import "os"
+
+// newBaseResource returns a *resource, populated with data from p and fi,
+// where p will be populated directly.
+func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
+	return &resource{
+		paths: []string{p},
+		mode:  fi.Mode(),
+	}, nil
+}

+ 2 - 0
vendor/github.com/containerd/go-runc/console.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 /*
    Copyright The containerd Authors.
 

+ 44 - 55
vendor/github.com/containerd/go-runc/io.go

@@ -20,9 +20,6 @@ import (
 	"io"
 	"os"
 	"os/exec"
-
-	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 type IO interface {
@@ -37,49 +34,22 @@ type StartCloser interface {
 	CloseAfterStart() error
 }
 
-// NewPipeIO creates pipe pairs to be used with runc
-func NewPipeIO(uid, gid int) (i IO, err error) {
-	var pipes []*pipe
-	// cleanup in case of an error
-	defer func() {
-		if err != nil {
-			for _, p := range pipes {
-				p.Close()
-			}
-		}
-	}()
-	stdin, err := newPipe()
-	if err != nil {
-		return nil, err
-	}
-	pipes = append(pipes, stdin)
-	if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil {
-		return nil, errors.Wrap(err, "failed to chown stdin")
-	}
+// IOOpt sets I/O creation options
+type IOOpt func(*IOOption)
 
-	stdout, err := newPipe()
-	if err != nil {
-		return nil, err
-	}
-	pipes = append(pipes, stdout)
-	if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil {
-		return nil, errors.Wrap(err, "failed to chown stdout")
-	}
+// IOOption holds I/O creation options
+type IOOption struct {
+	OpenStdin  bool
+	OpenStdout bool
+	OpenStderr bool
+}
 
-	stderr, err := newPipe()
-	if err != nil {
-		return nil, err
+func defaultIOOption() *IOOption {
+	return &IOOption{
+		OpenStdin:  true,
+		OpenStdout: true,
+		OpenStderr: true,
 	}
-	pipes = append(pipes, stderr)
-	if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil {
-		return nil, errors.Wrap(err, "failed to chown stderr")
-	}
-
-	return &pipeIO{
-		in:  stdin,
-		out: stdout,
-		err: stderr,
-	}, nil
 }
 
 func newPipe() (*pipe, error) {
@@ -99,9 +69,9 @@ type pipe struct {
 }
 
 func (p *pipe) Close() error {
-	err := p.r.Close()
-	if werr := p.w.Close(); err == nil {
-		err = werr
+	err := p.w.Close()
+	if rerr := p.r.Close(); err == nil {
+		err = rerr
 	}
 	return err
 }
@@ -113,14 +83,23 @@ type pipeIO struct {
 }
 
 func (i *pipeIO) Stdin() io.WriteCloser {
+	if i.in == nil {
+		return nil
+	}
 	return i.in.w
 }
 
 func (i *pipeIO) Stdout() io.ReadCloser {
+	if i.out == nil {
+		return nil
+	}
 	return i.out.r
 }
 
 func (i *pipeIO) Stderr() io.ReadCloser {
+	if i.err == nil {
+		return nil
+	}
 	return i.err.r
 }
 
@@ -131,28 +110,38 @@ func (i *pipeIO) Close() error {
 		i.out,
 		i.err,
 	} {
-		if cerr := v.Close(); err == nil {
-			err = cerr
+		if v != nil {
+			if cerr := v.Close(); err == nil {
+				err = cerr
+			}
 		}
 	}
 	return err
 }
 
 func (i *pipeIO) CloseAfterStart() error {
-	for _, f := range []*os.File{
-		i.out.w,
-		i.err.w,
+	for _, f := range []*pipe{
+		i.out,
+		i.err,
 	} {
-		f.Close()
+		if f != nil {
+			f.w.Close()
+		}
 	}
 	return nil
 }
 
 // Set sets the io to the exec.Cmd
 func (i *pipeIO) Set(cmd *exec.Cmd) {
-	cmd.Stdin = i.in.r
-	cmd.Stdout = i.out.w
-	cmd.Stderr = i.err.w
+	if i.in != nil {
+		cmd.Stdin = i.in.r
+	}
+	if i.out != nil {
+		cmd.Stdout = i.out.w
+	}
+	if i.err != nil {
+		cmd.Stderr = i.err.w
+	}
 }
 
 func NewSTDIO() (IO, error) {

+ 76 - 0
vendor/github.com/containerd/go-runc/io_unix.go

@@ -0,0 +1,76 @@
+// +build !windows
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package runc
+
+import (
+	"github.com/pkg/errors"
+	"golang.org/x/sys/unix"
+)
+
+// NewPipeIO creates pipe pairs to be used with runc
+func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
+	option := defaultIOOption()
+	for _, o := range opts {
+		o(option)
+	}
+	var (
+		pipes                 []*pipe
+		stdin, stdout, stderr *pipe
+	)
+	// cleanup in case of an error
+	defer func() {
+		if err != nil {
+			for _, p := range pipes {
+				p.Close()
+			}
+		}
+	}()
+	if option.OpenStdin {
+		if stdin, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stdin)
+		if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil {
+			return nil, errors.Wrap(err, "failed to chown stdin")
+		}
+	}
+	if option.OpenStdout {
+		if stdout, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stdout)
+		if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil {
+			return nil, errors.Wrap(err, "failed to chown stdout")
+		}
+	}
+	if option.OpenStderr {
+		if stderr, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stderr)
+		if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil {
+			return nil, errors.Wrap(err, "failed to chown stderr")
+		}
+	}
+	return &pipeIO{
+		in:  stdin,
+		out: stdout,
+		err: stderr,
+	}, nil
+}

+ 62 - 0
vendor/github.com/containerd/go-runc/io_windows.go

@@ -0,0 +1,62 @@
+// +build windows
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package runc
+
+// NewPipeIO creates pipe pairs to be used with runc
+func NewPipeIO(opts ...IOOpt) (i IO, err error) {
+	option := defaultIOOption()
+	for _, o := range opts {
+		o(option)
+	}
+	var (
+		pipes                 []*pipe
+		stdin, stdout, stderr *pipe
+	)
+	// cleanup in case of an error
+	defer func() {
+		if err != nil {
+			for _, p := range pipes {
+				p.Close()
+			}
+		}
+	}()
+	if option.OpenStdin {
+		if stdin, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stdin)
+	}
+	if option.OpenStdout {
+		if stdout, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stdout)
+	}
+	if option.OpenStderr {
+		if stderr, err = newPipe(); err != nil {
+			return nil, err
+		}
+		pipes = append(pipes, stderr)
+	}
+	return &pipeIO{
+		in:  stdin,
+		out: stdout,
+		err: stderr,
+	}, nil
+}

+ 1 - 2
vendor/github.com/containerd/go-runc/runc.go

@@ -608,9 +608,8 @@ func parseVersion(data []byte) (Version, error) {
 	var v Version
 	parts := strings.Split(strings.TrimSpace(string(data)), "\n")
 	if len(parts) != 3 {
-		return v, ErrParseRuncVersion
+		return v, nil
 	}
-
 	for i, p := range []struct {
 		dest  *string
 		split string