Merge pull request #410 from thaJeztah/19.03_backport_fix_buildkit_prunegc_filter_config

[19.03 backport] daemon/config: fix filter type in BuildKit GC config
diff --git a/contrib/dockerd-rootless.sh b/contrib/dockerd-rootless.sh
index 214162f..b14a0dd 100755
--- a/contrib/dockerd-rootless.sh
+++ b/contrib/dockerd-rootless.sh
@@ -39,6 +39,9 @@
 
 : "${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:=}"
 : "${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:=}"
+# if slirp4netns v0.4.0+ is installed, slirp4netns is hardened using sandbox (mount namespace) and seccomp
+: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX:=auto}"
+: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP:=auto}"
 net=$DOCKERD_ROOTLESS_ROOTLESSKIT_NET
 mtu=$DOCKERD_ROOTLESS_ROOTLESSKIT_MTU
 if [ -z $net ]; then
@@ -77,6 +80,8 @@
 	# * /run: copy-up is required so that we can create /run/docker (hardcoded for plugins) in our namespace
 	exec $rootlesskit \
 		--net=$net --mtu=$mtu \
+		--slirp4netns-sandbox=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX \
+		--slirp4netns-seccomp=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP \
 		--disable-host-loopback --port-driver=builtin \
 		--copy-up=/etc --copy-up=/run \
 		$DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS \
diff --git a/daemon/monitor.go b/daemon/monitor.go
index 2f47497..c294742 100644
--- a/daemon/monitor.go
+++ b/daemon/monitor.go
@@ -2,8 +2,6 @@
 
 import (
 	"context"
-	"errors"
-	"fmt"
 	"runtime"
 	"strconv"
 	"time"
@@ -12,6 +10,7 @@
 	"github.com/docker/docker/container"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 	"github.com/docker/docker/restartmanager"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
@@ -29,8 +28,8 @@
 // ProcessEvent is called by libcontainerd whenever an event occurs
 func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) error {
 	c, err := daemon.GetContainer(id)
-	if c == nil || err != nil {
-		return fmt.Errorf("no such container: %s", id)
+	if err != nil {
+		return errors.Wrapf(err, "could not find container %s", id)
 	}
 
 	switch e {
diff --git a/distribution/oci.go b/distribution/oci.go
deleted file mode 100644
index 92a8561..0000000
--- a/distribution/oci.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package distribution
-
-import (
-	"fmt"
-
-	"github.com/docker/distribution"
-	"github.com/docker/distribution/manifest/schema2"
-	digest "github.com/opencontainers/go-digest"
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
-)
-
-func init() {
-	// TODO: Remove this registration if distribution is included with OCI support
-
-	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
-		m := new(schema2.DeserializedManifest)
-		err := m.UnmarshalJSON(b)
-		if err != nil {
-			return nil, distribution.Descriptor{}, err
-		}
-
-		dgst := digest.FromBytes(b)
-		return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: ocispec.MediaTypeImageManifest}, err
-	}
-	err := distribution.RegisterManifestSchema(ocispec.MediaTypeImageManifest, ocischemaFunc)
-	if err != nil {
-		panic(fmt.Sprintf("Unable to register manifest: %s", err))
-	}
-}
diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go
index 77c5f64..3307458 100644
--- a/distribution/pull_v2.go
+++ b/distribution/pull_v2.go
@@ -14,6 +14,7 @@
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/manifestlist"
+	"github.com/docker/distribution/manifest/ocischema"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/reference"
@@ -410,6 +411,11 @@
 		if err != nil {
 			return false, err
 		}
+	case *ocischema.DeserializedManifest:
+		id, manifestDigest, err = p.pullOCI(ctx, ref, v, platform)
+		if err != nil {
+			return false, err
+		}
 	case *manifestlist.DeserializedManifestList:
 		id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
 		if err != nil {
@@ -557,24 +563,18 @@
 	return imageID, manifestDigest, nil
 }
 
-func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
-	manifestDigest, err = schema2ManifestDigest(ref, mfst)
-	if err != nil {
-		return "", "", err
-	}
-
-	target := mfst.Target()
+func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) {
 	if _, err := p.config.ImageStore.Get(target.Digest); err == nil {
 		// If the image already exists locally, no need to pull
 		// anything.
-		return target.Digest, manifestDigest, nil
+		return target.Digest, nil
 	}
 
 	var descriptors []xfer.DownloadDescriptor
 
 	// Note that the order of this loop is in the direction of bottom-most
 	// to top-most, so that the downloads slice gets ordered correctly.
-	for _, d := range mfst.Layers {
+	for _, d := range layers {
 		layerDescriptor := &v2LayerDescriptor{
 			digest:            d.Digest,
 			repo:              p.repo,
@@ -629,23 +629,23 @@
 	if runtime.GOOS == "windows" {
 		configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
 		if err != nil {
-			return "", "", err
+			return "", err
 		}
 		if configRootFS == nil {
-			return "", "", errRootFSInvalid
+			return "", errRootFSInvalid
 		}
 		if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil {
-			return "", "", err
+			return "", err
 		}
 
 		if len(descriptors) != len(configRootFS.DiffIDs) {
-			return "", "", errRootFSMismatch
+			return "", errRootFSMismatch
 		}
 		if platform == nil {
 			// Early bath if the requested OS doesn't match that of the configuration.
 			// This avoids doing the download, only to potentially fail later.
 			if !system.IsOSSupported(configPlatform.OS) {
-				return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
+				return "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
 			}
 			layerStoreOS = configPlatform.OS
 		}
@@ -692,14 +692,14 @@
 			case <-downloadsDone:
 			case <-layerErrChan:
 			}
-			return "", "", err
+			return "", err
 		}
 	}
 
 	select {
 	case <-downloadsDone:
 	case err = <-layerErrChan:
-		return "", "", err
+		return "", err
 	}
 
 	if release != nil {
@@ -711,22 +711,40 @@
 		// Otherwise the image config could be referencing layers that aren't
 		// included in the manifest.
 		if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) {
-			return "", "", errRootFSMismatch
+			return "", errRootFSMismatch
 		}
 
 		for i := range downloadedRootFS.DiffIDs {
 			if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] {
-				return "", "", errRootFSMismatch
+				return "", errRootFSMismatch
 			}
 		}
 	}
 
 	imageID, err := p.config.ImageStore.Put(configJSON)
 	if err != nil {
-		return "", "", err
+		return "", err
 	}
 
-	return imageID, manifestDigest, nil
+	return imageID, nil
+}
+
+func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
+	manifestDigest, err = schema2ManifestDigest(ref, mfst)
+	if err != nil {
+		return "", "", err
+	}
+	id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
+	return id, manifestDigest, err
+}
+
+func (p *v2Puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
+	manifestDigest, err = schema2ManifestDigest(ref, mfst)
+	if err != nil {
+		return "", "", err
+	}
+	id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
+	return id, manifestDigest, err
 }
 
 func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) {
@@ -811,6 +829,12 @@
 		if err != nil {
 			return "", "", err
 		}
+	case *ocischema.DeserializedManifest:
+		platform := toOCIPlatform(manifestMatches[0].Platform)
+		id, _, err = p.pullOCI(ctx, manifestRef, v, &platform)
+		if err != nil {
+			return "", "", err
+		}
 	default:
 		return "", "", errors.New("unsupported manifest format")
 	}
diff --git a/docs/rootless.md b/docs/rootless.md
index 7efdd86..698f581 100644
--- a/docs/rootless.md
+++ b/docs/rootless.md
@@ -20,43 +20,107 @@
 penguin:231072:65536
 ```
 
-
 ### Distribution-specific hint
 
-#### Debian (excluding Ubuntu)
-* `sudo sh -c "echo 1 > /proc/sys/kernel/unprivileged_userns_clone"` is required
+Using Ubuntu kernel is recommended.
+
+#### Ubuntu
+* No preparation is needed.
+* `overlay2` is enabled by default ([Ubuntu-specific kernel patch](https://kernel.ubuntu.com/git/ubuntu/ubuntu-bionic.git/commit/fs/overlayfs?id=3b7da90f28fe1ed4b79ef2d994c81efbc58f1144)).
+* Known to work on Ubuntu 16.04 and 18.04.
+
+#### Debian GNU/Linux
+* Add `kernel.unprivileged_userns_clone=1` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl -p`
+* To use `overlay2` storage driver (recommended), run `sudo modprobe overlay permit_mounts_in_userns=1` ([Debian-specific kernel patch, introduced in Debian 10](https://salsa.debian.org/kernel-team/linux/blob/283390e7feb21b47779b48e0c8eb0cc409d2c815/debian/patches/debian/overlayfs-permit-mounts-in-userns.patch)). Put the configuration to `/etc/modprobe.d` for persistence.
+* Known to work on Debian 9 and 10. `overlay2` is only supported since Debian 10 and needs `modprobe` configuration described above.
 
 #### Arch Linux
-* `sudo sh -c "echo 1 > /proc/sys/kernel/unprivileged_userns_clone"` is required
+* Add `kernel.unprivileged_userns_clone=1` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl -p`
 
 #### openSUSE
 * `sudo modprobe ip_tables iptable_mangle iptable_nat iptable_filter` is required. (This is likely to be required on other distros as well)
+* Known to work on openSUSE 15.
+
+#### Fedora 31 and later
+* Run `sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0"` and reboot.
+
+#### Fedora 30
+* No preparation is needed
+
+#### RHEL/CentOS 8
+* No preparation is needed
 
 #### RHEL/CentOS 7
-* `sudo sh -c "echo 28633 > /proc/sys/user/max_user_namespaces"` is required
-* [COPR package `vbatts/shadow-utils-newxidmap`](https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/) needs to be installed
+* Add `user.max_user_namespaces=28633` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl -p`
+* `systemctl --user` does not work by default. Run the daemon directly without systemd: `dockerd-rootless.sh --experimental --storage-driver vfs`
+* Known to work on RHEL/CentOS 7.7. Older releases require extra configuration steps.
+* RHEL/CentOS 7.6 and older releases require [COPR package `vbatts/shadow-utils-newxidmap`](https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/) to be installed.
+* RHEL/CentOS 7.5 and older releases require running `sudo grubby --update-kernel=ALL --args="user_namespace.enable=1"` and reboot.
 
-## Restrictions
+## Known limitations
 
-* Only `vfs` graphdriver is supported. However, on [Ubuntu](http://kernel.ubuntu.com/git/ubuntu/ubuntu-artful.git/commit/fs/overlayfs?h=Ubuntu-4.13.0-25.29&id=0a414bdc3d01f3b61ed86cfe3ce8b63a9240eba7) and a few distros, `overlay2` and `overlay` are also supported.
+* Only `vfs` graphdriver is supported. However, on Ubuntu and Debian 10, `overlay2` and `overlay` are also supported.
 * Following features are not supported:
   * Cgroups (including `docker top`, which depends on the cgroups device controller)
   * Apparmor
   * Checkpoint
   * Overlay network
   * Exposing SCTP ports
-* To expose a TCP/UDP port, the host port number needs to be set to >= 1024.
+* To use `ping` command, see [Routing ping packets](#routing-ping-packets)
+* To expose privileged TCP/UDP ports (< 1024), see [Exposing privileged ports](#exposing-privileged-ports)
+
+## Install
+
+The installation script is available at https://get.docker.com/rootless .
+
+```console
+$ curl -fsSL https://get.docker.com/rootless | sh
+```
+
+Make sure to run the script as a non-root user.
+
+The script will show the environment variables that are needed to be set:
+
+```console
+$ curl -fsSL https://get.docker.com/rootless | sh
+...
+# Docker binaries are installed in /home/penguin/bin
+# WARN: dockerd is not in your current PATH or pointing to /home/penguin/bin/dockerd
+# Make sure the following environment variables are set (or add them to ~/.bashrc):
+
+export PATH=/home/penguin/bin:$PATH
+export PATH=$PATH:/sbin
+export DOCKER_HOST=unix:///run/user/1001/docker.sock
+
+#
+# To control docker service run:
+# systemctl --user (start|stop|restart) docker
+#
+```
+
+To install the binaries manually without using the installer, extract `docker-rootless-extras-<version>.tar.gz` along with `docker-<version>.tar.gz`: https://download.docker.com/linux/static/stable/x86_64/
 
 ## Usage
 
 ### Daemon
 
-You need to run `dockerd-rootless.sh` instead of `dockerd`.
-
+Use `systemctl --user` to manage the lifecycle of the daemon:
 ```console
-$ dockerd-rootless.sh --experimental
+$ systemctl --user start docker
 ```
-As Rootless mode is experimental per se, currently you always need to run `dockerd-rootless.sh` with `--experimental`.
+
+To launch the daemon on system startup, enable systemd lingering:
+```console
+$ sudo loginctl enable-linger $(whoami)
+```
+
+To run the daemon directly without systemd, you need to run `dockerd-rootless.sh` instead of `dockerd`:
+```console
+$ dockerd-rootless.sh --experimental --storage-driver vfs
+```
+
+As Rootless mode is experimental, currently you always need to run `dockerd-rootless.sh` with `--experimental`.
+You also need `--storage-driver vfs` unless using Ubuntu or Debian 10 kernel.
 
 Remarks:
 * The socket path is set to `$XDG_RUNTIME_DIR/docker.sock` by default. `$XDG_RUNTIME_DIR` is typically set to `/run/user/$UID`.
@@ -69,12 +133,24 @@
 
 ### Client
 
-You can just use the upstream Docker client but you need to set the socket path explicitly.
+You need to set the socket path explicitly.
 
 ```console
-$ docker -H unix://$XDG_RUNTIME_DIR/docker.sock run -d nginx
+$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
+$ docker run -d nginx
 ```
 
+### Rootless Docker in Docker
+
+To run Rootless Docker inside "rootful" Docker, use `docker:<version>-dind-rootless` image instead of `docker:<version>-dind` image.
+
+```console
+$ docker run -d --name dind-rootless --privileged docker:19.03-dind-rootless --experimental
+```
+
+`docker:<version>-dind-rootless` image runs as a non-root user (UID 1000).
+However, `--privileged` is required for disabling seccomp, AppArmor, and mount masks.
+
 ### Expose Docker API socket via TCP
 
 To expose the Docker API socket via TCP, you need to launch `dockerd-rootless.sh` with `DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp"`.
@@ -88,12 +164,23 @@
 
 ### Routing ping packets
 
-To route ping packets, you need to set up `net.ipv4.ping_group_range` properly as the root.
+Add `net.ipv4.ping_group_range = 0   2147483647` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl -p`.
+
+### Exposing privileged ports
+
+To expose privileged ports (< 1024), set `CAP_NET_BIND_SERVICE` on `rootlesskit` binary.
 
 ```console
-$ sudo sh -c "echo 0   2147483647  > /proc/sys/net/ipv4/ping_group_range"
+$ sudo setcap cap_net_bind_service=ep $HOME/bin/rootlesskit
 ```
 
+Or add `net.ipv4.ip_unprivileged_port_start=0` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl -p`.
+
+### Limiting resources
+
+Currently rootless mode ignores cgroup-related `docker run` flags such as `--cpus` and `memory`.
+However, traditional `ulimit` and [`cpulimit`](https://github.com/opsengine/cpulimit) can be still used, though it works in process-granularity rather than container-granularity.
+
 ### Changing network stack
 
 `dockerd-rootless.sh` uses [slirp4netns](https://github.com/rootless-containers/slirp4netns) (if installed) or [VPNKit](https://github.com/moby/vpnkit) as the network stack by default.
diff --git a/hack/dockerfile/install/rootlesskit.installer b/hack/dockerfile/install/rootlesskit.installer
index 964207e..45dae93 100755
--- a/hack/dockerfile/install/rootlesskit.installer
+++ b/hack/dockerfile/install/rootlesskit.installer
@@ -1,7 +1,7 @@
 #!/bin/sh
 
-# v0.6.0
-ROOTLESSKIT_COMMIT=2fcff6ceae968a1d895e6205e5154b107247356f
+# v0.7.0
+ROOTLESSKIT_COMMIT=791ac8cb209a107505cd1ca5ddf23a49913e176c
 
 install_rootlesskit() {
 	case "$1" in
diff --git a/vendor/github.com/docker/distribution/manifest/ocischema/builder.go b/vendor/github.com/docker/distribution/manifest/ocischema/builder.go
new file mode 100644
index 0000000..d90453b
--- /dev/null
+++ b/vendor/github.com/docker/distribution/manifest/ocischema/builder.go
@@ -0,0 +1,107 @@
+package ocischema
+
+import (
+	"context"
+	"errors"
+
+	"github.com/docker/distribution"
+	"github.com/docker/distribution/manifest"
+	"github.com/opencontainers/go-digest"
+	"github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// Builder is a type for constructing manifests.
+type Builder struct {
+	// bs is a BlobService used to publish the configuration blob.
+	bs distribution.BlobService
+
+	// configJSON references
+	configJSON []byte
+
+	// layers is a list of layer descriptors that gets built by successive
+	// calls to AppendReference.
+	layers []distribution.Descriptor
+
+	// Annotations contains arbitrary metadata relating to the targeted content.
+	annotations map[string]string
+
+	// For testing purposes
+	mediaType string
+}
+
+// NewManifestBuilder is used to build new manifests for the current schema
+// version. It takes a BlobService so it can publish the configuration blob
+// as part of the Build process, and annotations.
+func NewManifestBuilder(bs distribution.BlobService, configJSON []byte, annotations map[string]string) distribution.ManifestBuilder {
+	mb := &Builder{
+		bs:          bs,
+		configJSON:  make([]byte, len(configJSON)),
+		annotations: annotations,
+		mediaType:   v1.MediaTypeImageManifest,
+	}
+	copy(mb.configJSON, configJSON)
+
+	return mb
+}
+
+// SetMediaType assigns the passed mediatype or error if the mediatype is not a
+// valid media type for oci image manifests currently: "" or "application/vnd.oci.image.manifest.v1+json"
+func (mb *Builder) SetMediaType(mediaType string) error {
+	if mediaType != "" && mediaType != v1.MediaTypeImageManifest {
+		return errors.New("invalid media type for OCI image manifest")
+	}
+
+	mb.mediaType = mediaType
+	return nil
+}
+
+// Build produces a final manifest from the given references.
+func (mb *Builder) Build(ctx context.Context) (distribution.Manifest, error) {
+	m := Manifest{
+		Versioned: manifest.Versioned{
+			SchemaVersion: 2,
+			MediaType:     mb.mediaType,
+		},
+		Layers:      make([]distribution.Descriptor, len(mb.layers)),
+		Annotations: mb.annotations,
+	}
+	copy(m.Layers, mb.layers)
+
+	configDigest := digest.FromBytes(mb.configJSON)
+
+	var err error
+	m.Config, err = mb.bs.Stat(ctx, configDigest)
+	switch err {
+	case nil:
+		// Override MediaType, since Put always replaces the specified media
+		// type with application/octet-stream in the descriptor it returns.
+		m.Config.MediaType = v1.MediaTypeImageConfig
+		return FromStruct(m)
+	case distribution.ErrBlobUnknown:
+		// nop
+	default:
+		return nil, err
+	}
+
+	// Add config to the blob store
+	m.Config, err = mb.bs.Put(ctx, v1.MediaTypeImageConfig, mb.configJSON)
+	// Override MediaType, since Put always replaces the specified media
+	// type with application/octet-stream in the descriptor it returns.
+	m.Config.MediaType = v1.MediaTypeImageConfig
+	if err != nil {
+		return nil, err
+	}
+
+	return FromStruct(m)
+}
+
+// AppendReference adds a reference to the current ManifestBuilder.
+func (mb *Builder) AppendReference(d distribution.Describable) error {
+	mb.layers = append(mb.layers, d.Descriptor())
+	return nil
+}
+
+// References returns the current references added to this builder.
+func (mb *Builder) References() []distribution.Descriptor {
+	return mb.layers
+}
diff --git a/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go b/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go
new file mode 100644
index 0000000..b8c4bab
--- /dev/null
+++ b/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go
@@ -0,0 +1,124 @@
+package ocischema
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"github.com/docker/distribution"
+	"github.com/docker/distribution/manifest"
+	"github.com/opencontainers/go-digest"
+	"github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+var (
+	// SchemaVersion provides a pre-initialized version structure for this
+	// packages version of the manifest.
+	SchemaVersion = manifest.Versioned{
+		SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version
+		MediaType:     v1.MediaTypeImageManifest,
+	}
+)
+
+func init() {
+	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
+		m := new(DeserializedManifest)
+		err := m.UnmarshalJSON(b)
+		if err != nil {
+			return nil, distribution.Descriptor{}, err
+		}
+
+		dgst := digest.FromBytes(b)
+		return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageManifest}, err
+	}
+	err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc)
+	if err != nil {
+		panic(fmt.Sprintf("Unable to register manifest: %s", err))
+	}
+}
+
+// Manifest defines a ocischema manifest.
+type Manifest struct {
+	manifest.Versioned
+
+	// Config references the image configuration as a blob.
+	Config distribution.Descriptor `json:"config"`
+
+	// Layers lists descriptors for the layers referenced by the
+	// configuration.
+	Layers []distribution.Descriptor `json:"layers"`
+
+	// Annotations contains arbitrary metadata for the image manifest.
+	Annotations map[string]string `json:"annotations,omitempty"`
+}
+
+// References returns the descriptors of this manifests references.
+func (m Manifest) References() []distribution.Descriptor {
+	references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
+	references = append(references, m.Config)
+	references = append(references, m.Layers...)
+	return references
+}
+
+// Target returns the target of this manifest.
+func (m Manifest) Target() distribution.Descriptor {
+	return m.Config
+}
+
+// DeserializedManifest wraps Manifest with a copy of the original JSON.
+// It satisfies the distribution.Manifest interface.
+type DeserializedManifest struct {
+	Manifest
+
+	// canonical is the canonical byte representation of the Manifest.
+	canonical []byte
+}
+
+// FromStruct takes a Manifest structure, marshals it to JSON, and returns a
+// DeserializedManifest which contains the manifest and its JSON representation.
+func FromStruct(m Manifest) (*DeserializedManifest, error) {
+	var deserialized DeserializedManifest
+	deserialized.Manifest = m
+
+	var err error
+	deserialized.canonical, err = json.MarshalIndent(&m, "", "   ")
+	return &deserialized, err
+}
+
+// UnmarshalJSON populates a new Manifest struct from JSON data.
+func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
+	m.canonical = make([]byte, len(b))
+	// store manifest in canonical
+	copy(m.canonical, b)
+
+	// Unmarshal canonical JSON into Manifest object
+	var manifest Manifest
+	if err := json.Unmarshal(m.canonical, &manifest); err != nil {
+		return err
+	}
+
+	if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
+		return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
+			v1.MediaTypeImageManifest, manifest.MediaType)
+	}
+
+	m.Manifest = manifest
+
+	return nil
+}
+
+// MarshalJSON returns the contents of canonical. If canonical is empty,
+// marshals the inner contents.
+func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
+	if len(m.canonical) > 0 {
+		return m.canonical, nil
+	}
+
+	return nil, errors.New("JSON representation not initialized in DeserializedManifest")
+}
+
+// Payload returns the raw content of the manifest. The contents can be used to
+// calculate the content identifier.
+func (m DeserializedManifest) Payload() (string, []byte, error) {
+	return v1.MediaTypeImageManifest, m.canonical, nil
+}