瀏覽代碼

LCOW: Re-coalesce stores

Signed-off-by: John Howard <jhoward@microsoft.com>

The re-coalesces the daemon stores which were split as part of the
original LCOW implementation.

This is part of the work discussed in https://github.com/moby/moby/issues/34617,
in particular see the document linked to in that issue.
John Howard 7 年之前
父節點
當前提交
ce8e529e18
共有 66 個文件被更改,包括 548 次插入634 次删除
  1. 1 1
      api/server/backend/build/backend.go
  2. 1 8
      api/server/backend/build/tag.go
  3. 2 2
      builder/builder.go
  4. 4 9
      builder/dockerfile/builder.go
  5. 3 3
      builder/dockerfile/dispatchers_test.go
  6. 2 2
      builder/dockerfile/imageprobe.go
  7. 1 1
      builder/dockerfile/internals.go
  8. 4 4
      builder/dockerfile/mockbackend_test.go
  9. 17 20
      daemon/build.go
  10. 3 3
      daemon/cache.go
  11. 6 6
      daemon/commit.go
  12. 2 2
      daemon/create.go
  13. 91 116
      daemon/daemon.go
  14. 3 11
      daemon/daemon_unix.go
  15. 1 1
      daemon/daemon_windows.go
  16. 1 1
      daemon/delete.go
  17. 17 20
      daemon/disk_usage.go
  18. 2 2
      daemon/getsize_unix.go
  19. 15 16
      daemon/image.go
  20. 20 20
      daemon/image_delete.go
  21. 2 14
      daemon/image_exporter.go
  22. 2 9
      daemon/image_history.go
  23. 8 8
      daemon/image_inspect.go
  24. 8 8
      daemon/image_pull.go
  25. 3 11
      daemon/image_push.go
  26. 4 4
      daemon/image_tag.go
  27. 14 33
      daemon/images.go
  28. 4 4
      daemon/import.go
  29. 9 15
      daemon/info.go
  30. 2 2
      daemon/list.go
  31. 10 7
      daemon/oci_windows.go
  32. 4 12
      daemon/prune.go
  33. 1 1
      daemon/start.go
  34. 10 10
      distribution/config.go
  35. 1 3
      distribution/metadata/metadata.go
  36. 1 2
      distribution/metadata/v1_id_service_test.go
  37. 1 2
      distribution/metadata/v2_metadata_service_test.go
  38. 5 5
      distribution/pull.go
  39. 1 1
      distribution/pull_v1.go
  40. 12 12
      distribution/pull_v2.go
  41. 13 19
      distribution/xfer/download.go
  42. 11 15
      distribution/xfer/download_test.go
  43. 1 13
      image/store.go
  44. 2 3
      image/store_test.go
  45. 15 16
      image/tarexport/load.go
  46. 3 2
      layer/empty.go
  47. 5 3
      layer/filestore_unix.go
  48. 3 3
      layer/filestore_windows.go
  49. 11 16
      layer/layer.go
  50. 91 66
      layer/layer_store.go
  51. 1 1
      layer/layer_store_windows.go
  52. 17 15
      layer/layer_test.go
  53. 5 2
      layer/layer_unix_test.go
  54. 3 4
      layer/layer_windows.go
  55. 17 5
      layer/migration.go
  56. 13 9
      layer/migration_test.go
  57. 3 3
      layer/mount_test.go
  58. 16 6
      layer/mounted_layer.go
  59. 3 3
      layer/ro_layer.go
  60. 4 2
      layer/ro_layer_unix.go
  61. 1 1
      layer/ro_layer_windows.go
  62. 4 4
      migrate/v1/migratev1_test.go
  63. 1 1
      pkg/system/init_windows.go
  64. 2 2
      plugin/backend_linux.go
  65. 2 3
      plugin/blobstore.go
  66. 3 6
      plugin/manager.go

+ 1 - 1
api/server/backend/build/backend.go

@@ -17,7 +17,7 @@ import (
 // ImageComponent provides an interface for working with images
 type ImageComponent interface {
 	SquashImage(from string, to string) (string, error)
-	TagImageWithReference(image.ID, string, reference.Named) error
+	TagImageWithReference(image.ID, reference.Named) error
 }
 
 // Builder defines interface for running a build

+ 1 - 8
api/server/backend/build/tag.go

@@ -3,11 +3,9 @@ package build
 import (
 	"fmt"
 	"io"
-	"runtime"
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/pkg/system"
 	"github.com/pkg/errors"
 )
 
@@ -35,12 +33,7 @@ func NewTagger(backend ImageComponent, stdout io.Writer, names []string) (*Tagge
 // TagImages creates image tags for the imageID
 func (bt *Tagger) TagImages(imageID image.ID) error {
 	for _, rt := range bt.repoAndTags {
-		// TODO @jhowardmsft LCOW support. Will need revisiting.
-		platform := runtime.GOOS
-		if system.LCOWSupported() {
-			platform = "linux"
-		}
-		if err := bt.imageComponent.TagImageWithReference(imageID, platform, rt); err != nil {
+		if err := bt.imageComponent.TagImageWithReference(imageID, rt); err != nil {
 			return err
 		}
 		fmt.Fprintf(bt.stdout, "Successfully tagged %s\n", reference.FamiliarString(rt))

+ 2 - 2
builder/builder.go

@@ -44,7 +44,7 @@ type Backend interface {
 	// ContainerCreateWorkdir creates the workdir
 	ContainerCreateWorkdir(containerID string) error
 
-	CreateImage(config []byte, parent string, platform string) (Image, error)
+	CreateImage(config []byte, parent string) (Image, error)
 
 	ImageCacheBuilder
 }
@@ -79,7 +79,7 @@ type Result struct {
 // ImageCacheBuilder represents a generator for stateful image cache.
 type ImageCacheBuilder interface {
 	// MakeImageCache creates a stateful image cache.
-	MakeImageCache(cacheFrom []string, platform string) ImageCache
+	MakeImageCache(cacheFrom []string) ImageCache
 }
 
 // ImageCache abstracts an image cache.

+ 4 - 9
builder/dockerfile/builder.go

@@ -123,7 +123,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
 		PathCache:      bm.pathCache,
 		IDMappings:     bm.idMappings,
 	}
-	return newBuilder(ctx, builderOptions, os).build(source, dockerfile)
+	return newBuilder(ctx, builderOptions).build(source, dockerfile)
 }
 
 func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) {
@@ -190,7 +190,7 @@ type Builder struct {
 }
 
 // newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
-func newBuilder(clientCtx context.Context, options builderOptions, os string) *Builder {
+func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
 	config := options.Options
 	if config == nil {
 		config = new(types.ImageBuildOptions)
@@ -207,7 +207,7 @@ func newBuilder(clientCtx context.Context, options builderOptions, os string) *B
 		idMappings:       options.IDMappings,
 		imageSources:     newImageSources(clientCtx, options),
 		pathCache:        options.PathCache,
-		imageProber:      newImageProber(options.Backend, config.CacheFrom, os, config.NoCache),
+		imageProber:      newImageProber(options.Backend, config.CacheFrom, config.NoCache),
 		containerManager: newContainerManager(options.Backend),
 	}
 
@@ -367,14 +367,9 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
 		return nil, errdefs.InvalidParameter(err)
 	}
 
-	os := runtime.GOOS
-	if dockerfile.OS != "" {
-		os = dockerfile.OS
-	}
-
 	b := newBuilder(context.Background(), builderOptions{
 		Options: &types.ImageBuildOptions{NoCache: true},
-	}, os)
+	})
 
 	// ensure that the commands are valid
 	for _, n := range dockerfile.AST.Children {

+ 3 - 3
builder/dockerfile/dispatchers_test.go

@@ -31,7 +31,7 @@ func newBuilderWithMockBackend() *Builder {
 			Options: &types.ImageBuildOptions{Platform: runtime.GOOS},
 			Backend: mockBackend,
 		}),
-		imageProber:      newImageProber(mockBackend, nil, runtime.GOOS, false),
+		imageProber:      newImageProber(mockBackend, nil, false),
 		containerManager: newContainerManager(mockBackend),
 	}
 	return b
@@ -427,10 +427,10 @@ func TestRunWithBuildArgs(t *testing.T) {
 	}
 
 	mockBackend := b.docker.(*MockBackend)
-	mockBackend.makeImageCacheFunc = func(_ []string, _ string) builder.ImageCache {
+	mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache {
 		return imageCache
 	}
-	b.imageProber = newImageProber(mockBackend, nil, runtime.GOOS, false)
+	b.imageProber = newImageProber(mockBackend, nil, false)
 	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ReleaseableLayer, error) {
 		return &mockImage{
 			id:     "abcdef",

+ 2 - 2
builder/dockerfile/imageprobe.go

@@ -19,13 +19,13 @@ type imageProber struct {
 	cacheBusted bool
 }
 
-func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, platform string, noCache bool) ImageProber {
+func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) ImageProber {
 	if noCache {
 		return &nopProber{}
 	}
 
 	reset := func() builder.ImageCache {
-		return cacheBuilder.MakeImageCache(cacheFrom, platform)
+		return cacheBuilder.MakeImageCache(cacheFrom)
 	}
 	return &imageProber{cache: reset(), reset: reset}
 }

+ 1 - 1
builder/dockerfile/internals.go

@@ -154,7 +154,7 @@ func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runC
 		return errors.Wrap(err, "failed to encode image config")
 	}
 
-	exportedImage, err := b.docker.CreateImage(config, state.imageID, parentImage.OS)
+	exportedImage, err := b.docker.CreateImage(config, state.imageID)
 	if err != nil {
 		return errors.Wrapf(err, "failed to export image")
 	}

+ 4 - 4
builder/dockerfile/mockbackend_test.go

@@ -20,7 +20,7 @@ type MockBackend struct {
 	containerCreateFunc func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
 	commitFunc          func(string, *backend.ContainerCommitConfig) (string, error)
 	getImageFunc        func(string) (builder.Image, builder.ReleaseableLayer, error)
-	makeImageCacheFunc  func(cacheFrom []string, platform string) builder.ImageCache
+	makeImageCacheFunc  func(cacheFrom []string) builder.ImageCache
 }
 
 func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error {
@@ -73,14 +73,14 @@ func (m *MockBackend) GetImageAndReleasableLayer(ctx context.Context, refOrID st
 	return &mockImage{id: "theid"}, &mockLayer{}, nil
 }
 
-func (m *MockBackend) MakeImageCache(cacheFrom []string, platform string) builder.ImageCache {
+func (m *MockBackend) MakeImageCache(cacheFrom []string) builder.ImageCache {
 	if m.makeImageCacheFunc != nil {
-		return m.makeImageCacheFunc(cacheFrom, platform)
+		return m.makeImageCacheFunc(cacheFrom)
 	}
 	return nil
 }
 
-func (m *MockBackend) CreateImage(config []byte, parent string, platform string) (builder.Image, error) {
+func (m *MockBackend) CreateImage(config []byte, parent string) (builder.Image, error) {
 	return nil, nil
 }
 

+ 17 - 20
daemon/build.go

@@ -2,7 +2,6 @@ package daemon
 
 import (
 	"io"
-	"runtime"
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
@@ -24,6 +23,7 @@ type releaseableLayer struct {
 	layerStore layer.Store
 	roLayer    layer.Layer
 	rwLayer    layer.RWLayer
+	os         string
 }
 
 func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
@@ -35,7 +35,7 @@ func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
 	}
 
 	mountID := stringid.GenerateRandomID()
-	rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil)
+	rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, rl.os, nil)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create rwlayer")
 	}
@@ -67,12 +67,12 @@ func (rl *releaseableLayer) Commit(os string) (builder.ReleaseableLayer, error)
 	}
 	defer stream.Close()
 
-	newLayer, err := rl.layerStore.Register(stream, chainID, layer.OS(os))
+	newLayer, err := rl.layerStore.Register(stream, chainID, os)
 	if err != nil {
 		return nil, err
 	}
-	// TODO: An optimization would be to handle empty layers before returning
-	return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil
+	// TODO: An optimization woudld be to handle empty layers before returning
+	return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer, os: os}, nil
 }
 
 func (rl *releaseableLayer) DiffID() layer.DiffID {
@@ -128,9 +128,9 @@ func (rl *releaseableLayer) releaseROLayer() error {
 	return err
 }
 
-func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) {
+func newReleasableLayerForImage(img *image.Image, layerStore layer.Store, os string) (builder.ReleaseableLayer, error) {
 	if img == nil || img.RootFS.ChainID() == "" {
-		return &releaseableLayer{layerStore: layerStore}, nil
+		return &releaseableLayer{layerStore: layerStore, os: os}, nil
 	}
 	// Hold a reference to the image layer so that it can't be removed before
 	// it is released
@@ -138,11 +138,11 @@ func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (build
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
 	}
-	return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil
+	return &releaseableLayer{layerStore: layerStore, roLayer: roLayer, os: os}, nil
 }
 
 // TODO: could this use the regular daemon PullImage ?
-func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, platform string) (*image.Image, error) {
+func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) {
 	ref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 		return nil, err
@@ -161,7 +161,7 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
 		pullRegistryAuth = &resolvedConfig
 	}
 
-	if err := daemon.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
+	if err := daemon.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil {
 		return nil, err
 	}
 	return daemon.GetImage(name)
@@ -172,7 +172,7 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
 // leaking of layers.
 func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
 	if refOrID == "" {
-		layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.OS].layerStore)
+		layer, err := newReleasableLayerForImage(nil, daemon.layerStore, opts.OS)
 		return nil, layer, err
 	}
 
@@ -183,7 +183,7 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
 		}
 		// TODO: shouldn't we error out if error is different from "not found" ?
 		if image != nil {
-			layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
+			layer, err := newReleasableLayerForImage(image, daemon.layerStore, image.OperatingSystem())
 			return image, layer, err
 		}
 	}
@@ -192,29 +192,26 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
 	if err != nil {
 		return nil, nil, err
 	}
-	layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
+	layer, err := newReleasableLayerForImage(image, daemon.layerStore, image.OperatingSystem())
 	return image, layer, err
 }
 
 // CreateImage creates a new image by adding a config and ID to the image store.
 // This is similar to LoadImage() except that it receives JSON encoded bytes of
 // an image instead of a tar archive.
-func (daemon *Daemon) CreateImage(config []byte, parent string, platform string) (builder.Image, error) {
-	if platform == "" {
-		platform = runtime.GOOS
-	}
-	id, err := daemon.stores[platform].imageStore.Create(config)
+func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) {
+	id, err := daemon.imageStore.Create(config)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to create image")
 	}
 
 	if parent != "" {
-		if err := daemon.stores[platform].imageStore.SetParent(id, image.ID(parent)); err != nil {
+		if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil {
 			return nil, errors.Wrapf(err, "failed to set parent %s", parent)
 		}
 	}
 
-	return daemon.stores[platform].imageStore.Get(id)
+	return daemon.imageStore.Get(id)
 }
 
 // IDMappings returns uid/gid mappings for the builder

+ 3 - 3
daemon/cache.go

@@ -7,12 +7,12 @@ import (
 )
 
 // MakeImageCache creates a stateful image cache.
-func (daemon *Daemon) MakeImageCache(sourceRefs []string, platform string) builder.ImageCache {
+func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache {
 	if len(sourceRefs) == 0 {
-		return cache.NewLocal(daemon.stores[platform].imageStore)
+		return cache.NewLocal(daemon.imageStore)
 	}
 
-	cache := cache.New(daemon.stores[platform].imageStore)
+	cache := cache.New(daemon.imageStore)
 
 	for _, ref := range sourceRefs {
 		img, err := daemon.GetImage(ref)

+ 6 - 6
daemon/commit.go

@@ -180,17 +180,17 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 		parent = new(image.Image)
 		parent.RootFS = image.NewRootFS()
 	} else {
-		parent, err = daemon.stores[container.OS].imageStore.Get(container.ImageID)
+		parent, err = daemon.imageStore.Get(container.ImageID)
 		if err != nil {
 			return "", err
 		}
 	}
 
-	l, err := daemon.stores[container.OS].layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.OS(container.OS))
+	l, err := daemon.layerStore.Register(rwTar, parent.RootFS.ChainID(), container.OS)
 	if err != nil {
 		return "", err
 	}
-	defer layer.ReleaseAndLog(daemon.stores[container.OS].layerStore, l)
+	defer layer.ReleaseAndLog(daemon.layerStore, l)
 
 	containerConfig := c.ContainerConfig
 	if containerConfig == nil {
@@ -209,13 +209,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 		return "", err
 	}
 
-	id, err := daemon.stores[container.OS].imageStore.Create(config)
+	id, err := daemon.imageStore.Create(config)
 	if err != nil {
 		return "", err
 	}
 
 	if container.ImageID != "" {
-		if err := daemon.stores[container.OS].imageStore.SetParent(id, container.ImageID); err != nil {
+		if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil {
 			return "", err
 		}
 	}
@@ -234,7 +234,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 				return "", err
 			}
 		}
-		if err := daemon.TagImageWithReference(id, container.OS, newTag); err != nil {
+		if err := daemon.TagImageWithReference(id, newTag); err != nil {
 			return "", err
 		}
 		imageRef = reference.FamiliarString(newTag)

+ 2 - 2
daemon/create.go

@@ -257,7 +257,7 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
 func (daemon *Daemon) setRWLayer(container *container.Container) error {
 	var layerID layer.ChainID
 	if container.ImageID != "" {
-		img, err := daemon.stores[container.OS].imageStore.Get(container.ImageID)
+		img, err := daemon.imageStore.Get(container.ImageID)
 		if err != nil {
 			return err
 		}
@@ -270,7 +270,7 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error {
 		StorageOpt: container.HostConfig.StorageOpt,
 	}
 
-	rwLayer, err := daemon.stores[container.OS].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts)
+	rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.OS, rwLayerOpts)
 	if err != nil {
 		return err
 	}

+ 91 - 116
daemon/daemon.go

@@ -69,50 +69,46 @@ var (
 	errSystemNotSupported = errors.New("the Docker daemon is not supported on this platform")
 )
 
-type daemonStore struct {
-	graphDriver               string
-	imageRoot                 string
+// Daemon holds information about the Docker daemon.
+type Daemon struct {
+	ID                        string
+	repository                string
+	containers                container.Store
+	containersReplica         container.ViewDB
+	execCommands              *exec.Store
+	downloadManager           *xfer.LayerDownloadManager
+	uploadManager             *xfer.LayerUploadManager
+	trustKey                  libtrust.PrivateKey
+	idIndex                   *truncindex.TruncIndex
+	configStore               *config.Config
+	statsCollector            *stats.Collector
+	defaultLogConfig          containertypes.LogConfig
+	RegistryService           registry.Service
+	EventsService             *events.Events
+	netController             libnetwork.NetworkController
+	volumes                   *store.VolumeStore
+	discoveryWatcher          discovery.Reloader
+	root                      string
+	seccompEnabled            bool
+	apparmorEnabled           bool
+	shutdown                  bool
+	idMappings                *idtools.IDMappings
+	graphDrivers              map[string]string // By operating system
+	referenceStore            refstore.Store
 	imageStore                image.Store
+	imageRoot                 string
 	layerStore                layer.Store
 	distributionMetadataStore dmetadata.Store
-}
-
-// Daemon holds information about the Docker daemon.
-type Daemon struct {
-	ID                    string
-	repository            string
-	containers            container.Store
-	containersReplica     container.ViewDB
-	execCommands          *exec.Store
-	downloadManager       *xfer.LayerDownloadManager
-	uploadManager         *xfer.LayerUploadManager
-	trustKey              libtrust.PrivateKey
-	idIndex               *truncindex.TruncIndex
-	configStore           *config.Config
-	statsCollector        *stats.Collector
-	defaultLogConfig      containertypes.LogConfig
-	RegistryService       registry.Service
-	EventsService         *events.Events
-	netController         libnetwork.NetworkController
-	volumes               *store.VolumeStore
-	discoveryWatcher      discovery.Reloader
-	root                  string
-	seccompEnabled        bool
-	apparmorEnabled       bool
-	shutdown              bool
-	idMappings            *idtools.IDMappings
-	stores                map[string]daemonStore // By container target platform
-	referenceStore        refstore.Store
-	PluginStore           *plugin.Store // todo: remove
-	pluginManager         *plugin.Manager
-	linkIndex             *linkIndex
-	containerd            libcontainerd.Client
-	containerdRemote      libcontainerd.Remote
-	defaultIsolation      containertypes.Isolation // Default isolation mode on Windows
-	clusterProvider       cluster.Provider
-	cluster               Cluster
-	genericResources      []swarm.GenericResource
-	metricsPluginListener net.Listener
+	PluginStore               *plugin.Store // todo: remove
+	pluginManager             *plugin.Manager
+	linkIndex                 *linkIndex
+	containerd                libcontainerd.Client
+	containerdRemote          libcontainerd.Remote
+	defaultIsolation          containertypes.Isolation // Default isolation mode on Windows
+	clusterProvider           cluster.Provider
+	cluster                   Cluster
+	genericResources          []swarm.GenericResource
+	metricsPluginListener     net.Listener
 
 	machineMemory uint64
 
@@ -161,9 +157,9 @@ func (daemon *Daemon) restore() error {
 		}
 
 		// Ignore the container if it does not support the current driver being used by the graph
-		currentDriverForContainerOS := daemon.stores[container.OS].graphDriver
+		currentDriverForContainerOS := daemon.graphDrivers[container.OS]
 		if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS {
-			rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID)
+			rwlayer, err := daemon.layerStore.GetRWLayer(container.ID)
 			if err != nil {
 				logrus.Errorf("Failed to load container mount %v: %v", id, err)
 				continue
@@ -706,11 +702,11 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	// lcow. Unix platforms however run a single graphdriver for all containers, and it can
 	// be set through an environment variable, a daemon start parameter, or chosen through
 	// initialization of the layerstore through driver priority order for example.
-	d.stores = make(map[string]daemonStore)
+	d.graphDrivers = make(map[string]string)
 	if runtime.GOOS == "windows" {
-		d.stores["windows"] = daemonStore{graphDriver: "windowsfilter"}
+		d.graphDrivers[runtime.GOOS] = "windowsfilter"
 		if system.LCOWSupported() {
-			d.stores["linux"] = daemonStore{graphDriver: "lcow"}
+			d.graphDrivers["linux"] = "lcow"
 		}
 	} else {
 		driverName := os.Getenv("DOCKER_DRIVER")
@@ -719,7 +715,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 		} else {
 			logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName)
 		}
-		d.stores[runtime.GOOS] = daemonStore{graphDriver: driverName} // May still be empty. Layerstore init determines instead.
+		d.graphDrivers[runtime.GOOS] = driverName // May still be empty. Layerstore init determines instead.
 	}
 
 	d.RegistryService = registryService
@@ -750,55 +746,43 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 		return nil, errors.Wrap(err, "couldn't create plugin manager")
 	}
 
-	var graphDrivers []string
-	for operatingSystem, ds := range d.stores {
-		ls, err := layer.NewStoreFromOptions(layer.StoreOptions{
-			StorePath:                 config.Root,
-			MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
-			GraphDriver:               ds.graphDriver,
-			GraphDriverOptions:        config.GraphOptions,
-			IDMappings:                idMappings,
-			PluginGetter:              d.PluginStore,
-			ExperimentalEnabled:       config.Experimental,
-			OS:                        operatingSystem,
-		})
-		if err != nil {
-			return nil, err
-		}
-		ds.graphDriver = ls.DriverName() // As layerstore may set the driver
-		ds.layerStore = ls
-		d.stores[operatingSystem] = ds
-		graphDrivers = append(graphDrivers, ls.DriverName())
+	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
+		Root: config.Root,
+		MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
+		GraphDrivers:              d.graphDrivers,
+		GraphDriverOptions:        config.GraphOptions,
+		IDMappings:                idMappings,
+		PluginGetter:              d.PluginStore,
+		ExperimentalEnabled:       config.Experimental,
+	})
+	if err != nil {
+		return nil, err
 	}
 
-	// Configure and validate the kernels security support
-	if err := configureKernelSecuritySupport(config, graphDrivers); err != nil {
+	// As layerstore may set the driver
+	for os := range d.graphDrivers {
+		d.graphDrivers[os] = d.layerStore.DriverName(os)
+	}
+
+	// Configure and validate the kernels security support. Note this is a Linux/FreeBSD
+	// operation only, so it is safe to pass *just* the runtime OS graphdriver.
+	if err := configureKernelSecuritySupport(config, d.graphDrivers[runtime.GOOS]); err != nil {
 		return nil, err
 	}
 
 	logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads)
-	lsMap := make(map[string]layer.Store)
-	for operatingSystem, ds := range d.stores {
-		lsMap[operatingSystem] = ds.layerStore
-	}
-	d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads)
+	d.downloadManager = xfer.NewLayerDownloadManager(d.layerStore, *config.MaxConcurrentDownloads)
 	logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads)
 	d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads)
-	for operatingSystem, ds := range d.stores {
-		imageRoot := filepath.Join(config.Root, "image", ds.graphDriver)
-		ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
-		if err != nil {
-			return nil, err
-		}
 
-		var is image.Store
-		is, err = image.NewImageStore(ifs, operatingSystem, ds.layerStore)
-		if err != nil {
-			return nil, err
-		}
-		ds.imageRoot = imageRoot
-		ds.imageStore = is
-		d.stores[operatingSystem] = ds
+	d.imageRoot = filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS])
+	ifs, err := image.NewFSStoreBackend(filepath.Join(d.imageRoot, "imagedb"))
+	if err != nil {
+		return nil, err
+	}
+	d.imageStore, err = image.NewImageStore(ifs, d.layerStore)
+	if err != nil {
+		return nil, err
 	}
 
 	// Configure the volumes driver
@@ -830,30 +814,25 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	// operating systems, the list of graphdrivers available isn't user configurable.
 	// For backwards compatibility, we just put it under the windowsfilter
 	// directory regardless.
-	refStoreLocation := filepath.Join(d.stores[runtime.GOOS].imageRoot, `repositories.json`)
+	refStoreLocation := filepath.Join(d.imageRoot, `repositories.json`)
 	rs, err := refstore.NewReferenceStore(refStoreLocation)
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't create reference store repository: %s", err)
 	}
 	d.referenceStore = rs
 
-	for platform, ds := range d.stores {
-		dms, err := dmetadata.NewFSMetadataStore(filepath.Join(ds.imageRoot, "distribution"), platform)
-		if err != nil {
-			return nil, err
-		}
-
-		ds.distributionMetadataStore = dms
-		d.stores[platform] = ds
+	d.distributionMetadataStore, err = dmetadata.NewFSMetadataStore(filepath.Join(d.imageRoot, "distribution"))
+	if err != nil {
+		return nil, err
+	}
 
-		// No content-addressability migration on Windows as it never supported pre-CA
-		if runtime.GOOS != "windows" {
-			migrationStart := time.Now()
-			if err := v1.Migrate(config.Root, ds.graphDriver, ds.layerStore, ds.imageStore, rs, dms); err != nil {
-				logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err)
-			}
-			logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds())
+	// No content-addressability migration on Windows as it never supported pre-CA
+	if runtime.GOOS != "windows" {
+		migrationStart := time.Now()
+		if err := v1.Migrate(config.Root, d.graphDrivers[runtime.GOOS], d.layerStore, d.imageStore, rs, d.distributionMetadataStore); err != nil {
+			logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err)
 		}
+		logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds())
 	}
 
 	// Discovery is only enabled when the daemon is launched with an address to advertise.  When
@@ -922,13 +901,13 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	engineMemory.Set(float64(info.MemTotal))
 
 	gd := ""
-	for platform, ds := range d.stores {
+	for os, driver := range d.graphDrivers {
 		if len(gd) > 0 {
 			gd += ", "
 		}
-		gd += ds.graphDriver
-		if len(d.stores) > 1 {
-			gd = fmt.Sprintf("%s (%s)", gd, platform)
+		gd += driver
+		if len(d.graphDrivers) > 1 {
+			gd = fmt.Sprintf("%s (%s)", gd, os)
 		}
 	}
 	logrus.WithFields(logrus.Fields{
@@ -1009,7 +988,7 @@ func (daemon *Daemon) Shutdown() error {
 				logrus.Errorf("Stop container error: %v", err)
 				return
 			}
-			if mountid, err := daemon.stores[c.OS].layerStore.GetMountID(c.ID); err == nil {
+			if mountid, err := daemon.layerStore.GetMountID(c.ID); err == nil {
 				daemon.cleanupMountsByID(mountid)
 			}
 			logrus.Debugf("container stopped %s", c.ID)
@@ -1022,12 +1001,8 @@ func (daemon *Daemon) Shutdown() error {
 		}
 	}
 
-	for platform, ds := range daemon.stores {
-		if ds.layerStore != nil {
-			if err := ds.layerStore.Cleanup(); err != nil {
-				logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, platform)
-			}
-		}
+	if err := daemon.layerStore.Cleanup(); err != nil {
+		logrus.Errorf("Error during layer Store.Cleanup(): %v", err)
 	}
 
 	// If we are part of a cluster, clean up cluster's stuff
@@ -1107,8 +1082,8 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) {
 }
 
 // GraphDriverName returns the name of the graph driver used by the layer.Store
-func (daemon *Daemon) GraphDriverName(platform string) string {
-	return daemon.stores[platform].layerStore.DriverName()
+func (daemon *Daemon) GraphDriverName(os string) string {
+	return daemon.layerStore.DriverName(os)
 }
 
 // prepareTempDir prepares and returns the default directory to use

+ 3 - 11
daemon/daemon_unix.go

@@ -814,22 +814,14 @@ func overlaySupportsSelinux() (bool, error) {
 }
 
 // configureKernelSecuritySupport configures and validates security support for the kernel
-func configureKernelSecuritySupport(config *config.Config, driverNames []string) error {
+func configureKernelSecuritySupport(config *config.Config, driverName string) error {
 	if config.EnableSelinuxSupport {
 		if !selinuxEnabled() {
 			logrus.Warn("Docker could not enable SELinux on the host system")
 			return nil
 		}
 
-		overlayFound := false
-		for _, d := range driverNames {
-			if d == "overlay" || d == "overlay2" {
-				overlayFound = true
-				break
-			}
-		}
-
-		if overlayFound {
+		if driverName == "overlay" || driverName == "overlay2" {
 			// If driver is overlay or overlay2, make sure kernel
 			// supports selinux with overlay.
 			supported, err := overlaySupportsSelinux()
@@ -838,7 +830,7 @@ func configureKernelSecuritySupport(config *config.Config, driverNames []string)
 			}
 
 			if !supported {
-				logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverNames)
+				logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverName)
 			}
 		}
 	} else {

+ 1 - 1
daemon/daemon_windows.go

@@ -262,7 +262,7 @@ func ensureServicesInstalled(services []string) error {
 }
 
 // configureKernelSecuritySupport configures and validate security support for the kernel
-func configureKernelSecuritySupport(config *config.Config, driverNames []string) error {
+func configureKernelSecuritySupport(config *config.Config, driverName string) error {
 	return nil
 }
 

+ 1 - 1
daemon/delete.go

@@ -118,7 +118,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
 	// When container creation fails and `RWLayer` has not been created yet, we
 	// do not call `ReleaseRWLayer`
 	if container.RWLayer != nil {
-		metadata, err := daemon.stores[container.OS].layerStore.ReleaseRWLayer(container.RWLayer)
+		metadata, err := daemon.layerStore.ReleaseRWLayer(container.RWLayer)
 		layer.LogReleaseMetadata(metadata)
 		if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) {
 			e := errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.OS), container.ID)

+ 17 - 20
daemon/disk_usage.go

@@ -15,12 +15,12 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
-func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int {
-	tmpImages := daemon.stores[platform].imageStore.Map()
+func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int {
+	tmpImages := daemon.imageStore.Map()
 	layerRefs := map[layer.ChainID]int{}
 	for id, img := range tmpImages {
 		dgst := digest.Digest(id)
-		if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 {
+		if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
 			continue
 		}
 
@@ -53,7 +53,6 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
 	}
 
 	// Get all top images with extra attributes
-	// TODO @jhowardmsft LCOW. This may need revisiting
 	allImages, err := daemon.Images(filters.NewArgs(), false, true)
 	if err != nil {
 		return nil, fmt.Errorf("failed to retrieve image list: %v", err)
@@ -96,24 +95,22 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
 
 	// Get total layers size on disk
 	var allLayersSize int64
-	for platform := range daemon.stores {
-		layerRefs := daemon.getLayerRefs(platform)
-		allLayers := daemon.stores[platform].layerStore.Map()
-		for _, l := range allLayers {
-			select {
-			case <-ctx.Done():
-				return nil, ctx.Err()
-			default:
-				size, err := l.DiffSize()
-				if err == nil {
-					if _, ok := layerRefs[l.ChainID()]; ok {
-						allLayersSize += size
-					} else {
-						logrus.Warnf("found leaked image layer %v platform %s", l.ChainID(), platform)
-					}
+	layerRefs := daemon.getLayerRefs()
+	allLayers := daemon.layerStore.Map()
+	for _, l := range allLayers {
+		select {
+		case <-ctx.Done():
+			return nil, ctx.Err()
+		default:
+			size, err := l.DiffSize()
+			if err == nil {
+				if _, ok := layerRefs[l.ChainID()]; ok {
+					allLayersSize += size
 				} else {
-					logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform)
+					logrus.Warnf("found leaked image layer %v", l.ChainID())
 				}
+			} else {
+				logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
 			}
 		}
 	}

+ 2 - 2
daemon/getsize_unix.go

@@ -15,12 +15,12 @@ func (daemon *Daemon) getSize(containerID string) (int64, int64) {
 		err                error
 	)
 
-	rwlayer, err := daemon.stores[runtime.GOOS].layerStore.GetRWLayer(containerID)
+	rwlayer, err := daemon.layerStore.GetRWLayer(containerID)
 	if err != nil {
 		logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err)
 		return sizeRw, sizeRootfs
 	}
-	defer daemon.stores[runtime.GOOS].layerStore.ReleaseRWLayer(rwlayer)
+	defer daemon.layerStore.ReleaseRWLayer(rwlayer)
 
 	sizeRw, err = rwlayer.Size()
 	if err != nil {

+ 15 - 16
daemon/image.go

@@ -38,32 +38,31 @@ func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error)
 			return "", "", errImageDoesNotExist{ref}
 		}
 		id := image.IDFromDigest(digested.Digest())
-		for platform := range daemon.stores {
-			if _, err = daemon.stores[platform].imageStore.Get(id); err == nil {
-				return id, platform, nil
-			}
+		if img, err := daemon.imageStore.Get(id); err == nil {
+			return id, img.OperatingSystem(), nil
 		}
 		return "", "", errImageDoesNotExist{ref}
 	}
 
 	if digest, err := daemon.referenceStore.Get(namedRef); err == nil {
 		// Search the image stores to get the operating system, defaulting to host OS.
-		imageOS := runtime.GOOS
 		id := image.IDFromDigest(digest)
-		for os := range daemon.stores {
-			if img, err := daemon.stores[os].imageStore.Get(id); err == nil {
-				imageOS = img.OperatingSystem()
-				break
-			}
+		if img, err := daemon.imageStore.Get(id); err == nil {
+			return id, img.OperatingSystem(), nil
 		}
-		return id, imageOS, nil
 	}
 
 	// Search based on ID
-	for os := range daemon.stores {
-		if id, err := daemon.stores[os].imageStore.Search(refOrID); err == nil {
-			return id, os, nil
+	if id, err := daemon.imageStore.Search(refOrID); err == nil {
+		img, err := daemon.imageStore.Get(id)
+		if err != nil {
+			return "", "", errImageDoesNotExist{ref}
 		}
+		imageOS := img.OperatingSystem()
+		if imageOS == "" {
+			imageOS = runtime.GOOS
+		}
+		return id, imageOS, nil
 	}
 
 	return "", "", errImageDoesNotExist{ref}
@@ -71,9 +70,9 @@ func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error)
 
 // GetImage returns an image corresponding to the image referred to by refOrID.
 func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
-	imgID, os, err := daemon.GetImageIDAndOS(refOrID)
+	imgID, _, err := daemon.GetImageIDAndOS(refOrID)
 	if err != nil {
 		return nil, err
 	}
-	return daemon.stores[os].imageStore.Get(imgID)
+	return daemon.imageStore.Get(imgID)
 }

+ 20 - 20
daemon/image_delete.go

@@ -66,7 +66,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 	start := time.Now()
 	records := []types.ImageDeleteResponseItem{}
 
-	imgID, os, err := daemon.GetImageIDAndOS(imageRef)
+	imgID, _, err := daemon.GetImageIDAndOS(imageRef)
 	if err != nil {
 		return nil, err
 	}
@@ -95,7 +95,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 			return nil, err
 		}
 
-		parsedRef, err = daemon.removeImageRef(os, parsedRef)
+		parsedRef, err = daemon.removeImageRef(parsedRef)
 		if err != nil {
 			return nil, err
 		}
@@ -123,7 +123,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 				remainingRefs := []reference.Named{}
 				for _, repoRef := range repoRefs {
 					if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() {
-						if _, err := daemon.removeImageRef(os, repoRef); err != nil {
+						if _, err := daemon.removeImageRef(repoRef); err != nil {
 							return records, err
 						}
 
@@ -153,12 +153,12 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 			if !force {
 				c |= conflictSoft &^ conflictActiveReference
 			}
-			if conflict := daemon.checkImageDeleteConflict(imgID, os, c); conflict != nil {
+			if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil {
 				return nil, conflict
 			}
 
 			for _, repoRef := range repoRefs {
-				parsedRef, err := daemon.removeImageRef(os, repoRef)
+				parsedRef, err := daemon.removeImageRef(repoRef)
 				if err != nil {
 					return nil, err
 				}
@@ -171,7 +171,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 		}
 	}
 
-	if err := daemon.imageDeleteHelper(imgID, os, &records, force, prune, removedRepositoryRef); err != nil {
+	if err := daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef); err != nil {
 		return nil, err
 	}
 
@@ -232,7 +232,7 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai
 // repositoryRef must not be an image ID but a repository name followed by an
 // optional tag or digest reference. If tag or digest is omitted, the default
 // tag is used. Returns the resolved image reference and an error.
-func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (reference.Named, error) {
+func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
 	ref = reference.TagNameOnly(ref)
 
 	// Ignore the boolean value returned, as far as we're concerned, this
@@ -248,11 +248,11 @@ func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (refe
 // on the first encountered error. Removed references are logged to this
 // daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the
 // given list of records.
-func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem) error {
+func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDeleteResponseItem) error {
 	imageRefs := daemon.referenceStore.References(imgID.Digest())
 
 	for _, imageRef := range imageRefs {
-		parsedRef, err := daemon.removeImageRef(platform, imageRef)
+		parsedRef, err := daemon.removeImageRef(imageRef)
 		if err != nil {
 			return err
 		}
@@ -299,15 +299,15 @@ func (idc *imageDeleteConflict) Conflict() {}
 // conflict is encountered, it will be returned immediately without deleting
 // the image. If quiet is true, any encountered conflicts will be ignored and
 // the function will return nil immediately without deleting the image.
-func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error {
+func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error {
 	// First, determine if this image has any conflicts. Ignore soft conflicts
 	// if force is true.
 	c := conflictHard
 	if !force {
 		c |= conflictSoft
 	}
-	if conflict := daemon.checkImageDeleteConflict(imgID, platform, c); conflict != nil {
-		if quiet && (!daemon.imageIsDangling(imgID, platform) || conflict.used) {
+	if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil {
+		if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
 			// Ignore conflicts UNLESS the image is "dangling" or not being used in
 			// which case we want the user to know.
 			return nil
@@ -318,18 +318,18 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records
 		return conflict
 	}
 
-	parent, err := daemon.stores[platform].imageStore.GetParent(imgID)
+	parent, err := daemon.imageStore.GetParent(imgID)
 	if err != nil {
 		// There may be no parent
 		parent = ""
 	}
 
 	// Delete all repository tag/digest references to this image.
-	if err := daemon.removeAllReferencesToImageID(imgID, platform, records); err != nil {
+	if err := daemon.removeAllReferencesToImageID(imgID, records); err != nil {
 		return err
 	}
 
-	removedLayers, err := daemon.stores[platform].imageStore.Delete(imgID)
+	removedLayers, err := daemon.imageStore.Delete(imgID)
 	if err != nil {
 		return err
 	}
@@ -349,7 +349,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records
 	// either running or stopped).
 	// Do not force prunings, but do so quietly (stopping on any encountered
 	// conflicts).
-	return daemon.imageDeleteHelper(parent, platform, records, false, true, true)
+	return daemon.imageDeleteHelper(parent, records, false, true, true)
 }
 
 // checkImageDeleteConflict determines whether there are any conflicts
@@ -358,9 +358,9 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records
 // using the image. A soft conflict is any tags/digest referencing the given
 // image or any stopped container using the image. If ignoreSoftConflicts is
 // true, this function will not check for soft conflict conditions.
-func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string, mask conflictType) *imageDeleteConflict {
+func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict {
 	// Check if the image has any descendant images.
-	if mask&conflictDependentChild != 0 && len(daemon.stores[platform].imageStore.Children(imgID)) > 0 {
+	if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 {
 		return &imageDeleteConflict{
 			hard:    true,
 			imgID:   imgID,
@@ -411,6 +411,6 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string,
 // imageIsDangling returns whether the given image is "dangling" which means
 // that there are no repository references to the given image and it has no
 // child images.
-func (daemon *Daemon) imageIsDangling(imgID image.ID, platform string) bool {
-	return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.stores[platform].imageStore.Children(imgID)) > 0)
+func (daemon *Daemon) imageIsDangling(imgID image.ID) bool {
+	return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.imageStore.Children(imgID)) > 0)
 }

+ 2 - 14
daemon/image_exporter.go

@@ -2,10 +2,8 @@ package daemon
 
 import (
 	"io"
-	"runtime"
 
 	"github.com/docker/docker/image/tarexport"
-	"github.com/docker/docker/pkg/system"
 )
 
 // ExportImage exports a list of images to the given output stream. The
@@ -14,12 +12,7 @@ import (
 // the same tag are exported. names is the set of tags to export, and
 // outStream is the writer which the images are written to.
 func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
-	// TODO @jhowardmsft LCOW. This will need revisiting later.
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-	imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon)
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
 	return imageExporter.Save(names, outStream)
 }
 
@@ -27,11 +20,6 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
 // complement of ImageExport.  The input stream is an uncompressed tar
 // ball containing images and metadata.
 func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
-	// TODO @jhowardmsft LCOW. This will need revisiting later.
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-	imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon)
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
 	return imageExporter.Load(inTar, outStream, quiet)
 }

+ 2 - 9
daemon/image_history.go

@@ -2,7 +2,6 @@ package daemon
 
 import (
 	"fmt"
-	"runtime"
 	"time"
 
 	"github.com/docker/distribution/reference"
@@ -19,12 +18,6 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
 		return nil, err
 	}
 
-	// If the image OS isn't set, assume it's the host OS
-	platform := img.OS
-	if platform == "" {
-		platform = runtime.GOOS
-	}
-
 	history := []*image.HistoryResponseItem{}
 
 	layerCounter := 0
@@ -40,12 +33,12 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
 			}
 
 			rootFS.Append(img.RootFS.DiffIDs[layerCounter])
-			l, err := daemon.stores[platform].layerStore.Get(rootFS.ChainID())
+			l, err := daemon.layerStore.Get(rootFS.ChainID())
 			if err != nil {
 				return nil, err
 			}
 			layerSize, err = l.DiffSize()
-			layer.ReleaseAndLog(daemon.stores[platform].layerStore, l)
+			layer.ReleaseAndLog(daemon.layerStore, l)
 			if err != nil {
 				return nil, err
 			}

+ 8 - 8
daemon/image_inspect.go

@@ -19,9 +19,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 	}
 
 	// If the image OS isn't set, assume it's the host OS
-	platform := img.OS
-	if platform == "" {
-		platform = runtime.GOOS
+	os := img.OS
+	if os == "" {
+		os = runtime.GOOS
 	}
 
 	refs := daemon.referenceStore.References(img.ID().Digest())
@@ -40,11 +40,11 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 	var layerMetadata map[string]string
 	layerID := img.RootFS.ChainID()
 	if layerID != "" {
-		l, err := daemon.stores[platform].layerStore.Get(layerID)
+		l, err := daemon.layerStore.Get(layerID)
 		if err != nil {
 			return nil, err
 		}
-		defer layer.ReleaseAndLog(daemon.stores[platform].layerStore, l)
+		defer layer.ReleaseAndLog(daemon.layerStore, l)
 		size, err = l.Size()
 		if err != nil {
 			return nil, err
@@ -61,7 +61,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 		comment = img.History[len(img.History)-1].Comment
 	}
 
-	lastUpdated, err := daemon.stores[platform].imageStore.GetLastUpdated(img.ID())
+	lastUpdated, err := daemon.imageStore.GetLastUpdated(img.ID())
 	if err != nil {
 		return nil, err
 	}
@@ -79,7 +79,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 		Author:          img.Author,
 		Config:          img.Config,
 		Architecture:    img.Architecture,
-		Os:              platform,
+		Os:              os,
 		OsVersion:       img.OSVersion,
 		Size:            size,
 		VirtualSize:     size, // TODO: field unused, deprecate
@@ -89,7 +89,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 		},
 	}
 
-	imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform)
+	imageInspect.GraphDriver.Name = daemon.GraphDriverName(os)
 	imageInspect.GraphDriver.Data = layerMetadata
 
 	return imageInspect, nil

+ 8 - 8
daemon/image_pull.go

@@ -19,7 +19,7 @@ import (
 
 // PullImage initiates a pull operation. image is the repository name to pull, and
 // tag may be either empty, or indicate a specific tag to pull.
-func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
+func (daemon *Daemon) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
 	// Special case: "pull -a" may send an image name with a
 	// trailing :. This is ugly, but let's not break API
 	// compatibility.
@@ -44,10 +44,10 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string
 		}
 	}
 
-	return daemon.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream)
+	return daemon.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream)
 }
 
-func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
+func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
 	// Include a buffer so that slow client connections don't affect
 	// transfer performance.
 	progressChan := make(chan progress.Progress, 100)
@@ -62,8 +62,8 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
 	}()
 
 	// Default to the host OS platform in case it hasn't been populated with an explicit value.
-	if platform == "" {
-		platform = runtime.GOOS
+	if os == "" {
+		os = runtime.GOOS
 	}
 
 	imagePullConfig := &distribution.ImagePullConfig{
@@ -73,13 +73,13 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
 			ProgressOutput:   progress.ChanOutput(progressChan),
 			RegistryService:  daemon.RegistryService,
 			ImageEventLogger: daemon.LogImageEvent,
-			MetadataStore:    daemon.stores[platform].distributionMetadataStore,
-			ImageStore:       distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore),
+			MetadataStore:    daemon.distributionMetadataStore,
+			ImageStore:       distribution.NewImageConfigStoreFromStore(daemon.imageStore),
 			ReferenceStore:   daemon.referenceStore,
 		},
 		DownloadManager: daemon.downloadManager,
 		Schema2Types:    distribution.ImageTypes,
-		Platform:        platform,
+		OS:              os,
 	}
 
 	err := distribution.Pull(ctx, ref, imagePullConfig)

+ 3 - 11
daemon/image_push.go

@@ -2,7 +2,6 @@ package daemon
 
 import (
 	"io"
-	"runtime"
 
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/reference"
@@ -10,7 +9,6 @@ import (
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/pkg/system"
 	"golang.org/x/net/context"
 )
 
@@ -41,12 +39,6 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead
 		close(writesDone)
 	}()
 
-	// TODO @jhowardmsft LCOW Support. This will require revisiting. For now, hard-code.
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-
 	imagePushConfig := &distribution.ImagePushConfig{
 		Config: distribution.Config{
 			MetaHeaders:      metaHeaders,
@@ -54,12 +46,12 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead
 			ProgressOutput:   progress.ChanOutput(progressChan),
 			RegistryService:  daemon.RegistryService,
 			ImageEventLogger: daemon.LogImageEvent,
-			MetadataStore:    daemon.stores[platform].distributionMetadataStore,
-			ImageStore:       distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore),
+			MetadataStore:    daemon.distributionMetadataStore,
+			ImageStore:       distribution.NewImageConfigStoreFromStore(daemon.imageStore),
 			ReferenceStore:   daemon.referenceStore,
 		},
 		ConfigMediaType: schema2.MediaTypeImageConfig,
-		LayerStore:      distribution.NewLayerProviderFromStore(daemon.stores[platform].layerStore),
+		LayerStore:      distribution.NewLayerProviderFromStore(daemon.layerStore),
 		TrustKey:        daemon.trustKey,
 		UploadManager:   daemon.uploadManager,
 	}

+ 4 - 4
daemon/image_tag.go

@@ -8,7 +8,7 @@ import (
 // TagImage creates the tag specified by newTag, pointing to the image named
 // imageName (alternatively, imageName can also be an image ID).
 func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
-	imageID, os, err := daemon.GetImageIDAndOS(imageName)
+	imageID, _, err := daemon.GetImageIDAndOS(imageName)
 	if err != nil {
 		return err
 	}
@@ -23,16 +23,16 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
 		}
 	}
 
-	return daemon.TagImageWithReference(imageID, os, newTag)
+	return daemon.TagImageWithReference(imageID, newTag)
 }
 
 // TagImageWithReference adds the given reference to the image ID provided.
-func (daemon *Daemon) TagImageWithReference(imageID image.ID, os string, newTag reference.Named) error {
+func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.Named) error {
 	if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil {
 		return err
 	}
 
-	if err := daemon.stores[os].imageStore.SetLastUpdated(imageID); err != nil {
+	if err := daemon.imageStore.SetLastUpdated(imageID); err != nil {
 		return err
 	}
 	daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")

+ 14 - 33
daemon/images.go

@@ -3,7 +3,6 @@ package daemon
 import (
 	"encoding/json"
 	"fmt"
-	"runtime"
 	"sort"
 	"time"
 
@@ -15,7 +14,6 @@ import (
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/pkg/system"
 )
 
 var acceptedImageFilterTags = map[string]bool{
@@ -36,12 +34,7 @@ func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
 
 // Map returns a map of all images in the ImageStore
 func (daemon *Daemon) Map() map[image.ID]*image.Image {
-	// TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-	return daemon.stores[platform].imageStore.Map()
+	return daemon.imageStore.Map()
 }
 
 // Images returns a filtered list of images. filterArgs is a JSON-encoded set
@@ -50,13 +43,6 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image {
 // named all controls whether all images in the graph are filtered, or just
 // the heads.
 func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
-
-	// TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-
 	var (
 		allImages    map[image.ID]*image.Image
 		err          error
@@ -75,9 +61,9 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 		}
 	}
 	if danglingOnly {
-		allImages = daemon.stores[platform].imageStore.Heads()
+		allImages = daemon.imageStore.Heads()
 	} else {
-		allImages = daemon.stores[platform].imageStore.Map()
+		allImages = daemon.imageStore.Map()
 	}
 
 	var beforeFilter, sinceFilter *image.Image
@@ -130,7 +116,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 		layerID := img.RootFS.ChainID()
 		var size int64
 		if layerID != "" {
-			l, err := daemon.stores[platform].layerStore.Get(layerID)
+			l, err := daemon.layerStore.Get(layerID)
 			if err != nil {
 				// The layer may have been deleted between the call to `Map()` or
 				// `Heads()` and the call to `Get()`, so we just ignore this error
@@ -141,7 +127,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 			}
 
 			size, err = l.Size()
-			layer.ReleaseAndLog(daemon.stores[platform].layerStore, l)
+			layer.ReleaseAndLog(daemon.layerStore, l)
 			if err != nil {
 				return nil, err
 			}
@@ -171,7 +157,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 			}
 		}
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
-			if all || len(daemon.stores[platform].imageStore.Children(id)) == 0 {
+			if all || len(daemon.imageStore.Children(id)) == 0 {
 
 				if imageFilters.Contains("dangling") && !danglingOnly {
 					//dangling=false case, so dangling image is not needed
@@ -193,7 +179,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 			// lazily init variables
 			if imagesMap == nil {
 				allContainers = daemon.List()
-				allLayers = daemon.stores[platform].layerStore.Map()
+				allLayers = daemon.layerStore.Map()
 				imagesMap = make(map[*image.Image]*types.ImageSummary)
 				layerRefs = make(map[layer.ChainID]int)
 			}
@@ -261,19 +247,14 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
 		img *image.Image
 		err error
 	)
-	for _, ds := range daemon.stores {
-		if img, err = ds.imageStore.Get(image.ID(id)); err == nil {
-			break
-		}
-	}
-	if err != nil {
+	if img, err = daemon.imageStore.Get(image.ID(id)); err != nil {
 		return "", err
 	}
 
 	var parentImg *image.Image
 	var parentChainID layer.ChainID
 	if len(parent) != 0 {
-		parentImg, err = daemon.stores[img.OperatingSystem()].imageStore.Get(image.ID(parent))
+		parentImg, err = daemon.imageStore.Get(image.ID(parent))
 		if err != nil {
 			return "", errors.Wrap(err, "error getting specified parent layer")
 		}
@@ -283,11 +264,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
 		parentImg = &image.Image{RootFS: rootFS}
 	}
 
-	l, err := daemon.stores[img.OperatingSystem()].layerStore.Get(img.RootFS.ChainID())
+	l, err := daemon.layerStore.Get(img.RootFS.ChainID())
 	if err != nil {
 		return "", errors.Wrap(err, "error getting image layer")
 	}
-	defer daemon.stores[img.OperatingSystem()].layerStore.Release(l)
+	defer daemon.layerStore.Release(l)
 
 	ts, err := l.TarStreamFrom(parentChainID)
 	if err != nil {
@@ -295,11 +276,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
 	}
 	defer ts.Close()
 
-	newL, err := daemon.stores[img.OperatingSystem()].layerStore.Register(ts, parentChainID, layer.OS(img.OperatingSystem()))
+	newL, err := daemon.layerStore.Register(ts, parentChainID, img.OperatingSystem())
 	if err != nil {
 		return "", errors.Wrap(err, "error registering layer")
 	}
-	defer daemon.stores[img.OperatingSystem()].layerStore.Release(newL)
+	defer daemon.layerStore.Release(newL)
 
 	newImage := *img
 	newImage.RootFS = nil
@@ -334,7 +315,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
 		return "", errors.Wrap(err, "error marshalling image config")
 	}
 
-	newImgID, err := daemon.stores[img.OperatingSystem()].imageStore.Create(b)
+	newImgID, err := daemon.imageStore.Create(b)
 	if err != nil {
 		return "", errors.Wrap(err, "error creating new image after squash")
 	}

+ 4 - 4
daemon/import.go

@@ -91,11 +91,11 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
 	if err != nil {
 		return err
 	}
-	l, err := daemon.stores[os].layerStore.Register(inflatedLayerData, "", layer.OS(os))
+	l, err := daemon.layerStore.Register(inflatedLayerData, "", os)
 	if err != nil {
 		return err
 	}
-	defer layer.ReleaseAndLog(daemon.stores[os].layerStore, l)
+	defer layer.ReleaseAndLog(daemon.layerStore, l)
 
 	created := time.Now().UTC()
 	imgConfig, err := json.Marshal(&image.Image{
@@ -120,14 +120,14 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
 		return err
 	}
 
-	id, err := daemon.stores[os].imageStore.Create(imgConfig)
+	id, err := daemon.imageStore.Create(imgConfig)
 	if err != nil {
 		return err
 	}
 
 	// FIXME: connect with commit code and call refstore directly
 	if newRef != nil {
-		if err := daemon.TagImageWithReference(id, os, newRef); err != nil {
+		if err := daemon.TagImageWithReference(id, newRef); err != nil {
 			return err
 		}
 	}

+ 9 - 15
daemon/info.go

@@ -78,32 +78,26 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 		securityOptions = append(securityOptions, "name=userns")
 	}
 
-	imageCount := 0
+	var ds [][2]string
 	drivers := ""
-	for p, ds := range daemon.stores {
-		imageCount += len(ds.imageStore.Map())
-		drivers += daemon.GraphDriverName(p)
-		if len(daemon.stores) > 1 {
-			drivers += fmt.Sprintf(" (%s) ", p)
+	for os, gd := range daemon.graphDrivers {
+		ds = append(ds, daemon.layerStore.DriverStatus(os)...)
+		drivers += gd
+		if len(daemon.graphDrivers) > 1 {
+			drivers += fmt.Sprintf(" (%s) ", os)
 		}
 	}
-
-	// TODO @jhowardmsft LCOW support. For now, hard-code the platform shown for the driver status
-	p := runtime.GOOS
-	if system.LCOWSupported() {
-		p = "linux"
-	}
-
 	drivers = strings.TrimSpace(drivers)
+
 	v := &types.Info{
 		ID:                 daemon.ID,
 		Containers:         cRunning + cPaused + cStopped,
 		ContainersRunning:  cRunning,
 		ContainersPaused:   cPaused,
 		ContainersStopped:  cStopped,
-		Images:             imageCount,
+		Images:             len(daemon.imageStore.Map()),
 		Driver:             drivers,
-		DriverStatus:       daemon.stores[p].layerStore.DriverStatus(),
+		DriverStatus:       ds,
 		Plugins:            daemon.showPluginsInfo(),
 		IPv4Forwarding:     !sysInfo.IPv4ForwardingDisabled,
 		BridgeNfIptables:   !sysInfo.BridgeNFCallIPTablesDisabled,

+ 2 - 2
daemon/list.go

@@ -323,7 +323,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
 	if psFilters.Contains("ancestor") {
 		ancestorFilter = true
 		psFilters.WalkValues("ancestor", func(ancestor string) error {
-			id, os, err := daemon.GetImageIDAndOS(ancestor)
+			id, _, err := daemon.GetImageIDAndOS(ancestor)
 			if err != nil {
 				logrus.Warnf("Error while looking up for image %v", ancestor)
 				return nil
@@ -333,7 +333,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
 				return nil
 			}
 			// Then walk down the graph and put the imageIds in imagesFilter
-			populateImageFilterByParents(imagesFilter, id, daemon.stores[os].imageStore.Children)
+			populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
 			return nil
 		})
 	}

+ 10 - 7
daemon/oci_windows.go

@@ -138,9 +138,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
 	max := len(img.RootFS.DiffIDs)
 	for i := 1; i <= max; i++ {
 		img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
-		layerPath, err := layer.GetLayerPath(daemon.stores[c.OS].layerStore, img.RootFS.ChainID())
+		layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
 		if err != nil {
-			return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.stores[c.OS].layerStore, img.RootFS.ChainID(), err)
+			return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
 		}
 		// Reverse order, expecting parent most first
 		s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...)
@@ -210,15 +210,18 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
 		NetworkSharedContainerName: networkSharedContainerID,
 	}
 
-	if img.OS == "windows" {
+	switch img.OS {
+	case "windows":
 		if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil {
 			return nil, err
 		}
-	} else {
-		// TODO @jhowardmsft LCOW Support. Modify this check when running in dual-mode
-		if system.LCOWSupported() && img.OS == "linux" {
-			daemon.createSpecLinuxFields(c, &s)
+	case "linux":
+		if !system.LCOWSupported() {
+			return nil, fmt.Errorf("Linux containers on Windows are not supported")
 		}
+		daemon.createSpecLinuxFields(c, &s)
+	default:
+		return nil, fmt.Errorf("Unsupported platform %q", img.OS)
 	}
 
 	return (*specs.Spec)(&s), nil

+ 4 - 12
daemon/prune.go

@@ -3,7 +3,6 @@ package daemon
 import (
 	"fmt"
 	"regexp"
-	"runtime"
 	"sync/atomic"
 	"time"
 
@@ -14,7 +13,6 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/directory"
-	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
 	"github.com/docker/libnetwork"
@@ -162,12 +160,6 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg
 
 // ImagesPrune removes unused images
 func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
-	// TODO @jhowardmsft LCOW Support: This will need revisiting later.
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-
 	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
 		return nil, errPruneRunning
 	}
@@ -197,9 +189,9 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
 
 	var allImages map[image.ID]*image.Image
 	if danglingOnly {
-		allImages = daemon.stores[platform].imageStore.Heads()
+		allImages = daemon.imageStore.Heads()
 	} else {
-		allImages = daemon.stores[platform].imageStore.Map()
+		allImages = daemon.imageStore.Map()
 	}
 	allContainers := daemon.List()
 	imageRefs := map[string]bool{}
@@ -213,7 +205,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
 	}
 
 	// Filter intermediary images and get their unique size
-	allLayers := daemon.stores[platform].layerStore.Map()
+	allLayers := daemon.layerStore.Map()
 	topImages := map[image.ID]*image.Image{}
 	for id, img := range allImages {
 		select {
@@ -221,7 +213,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
 			return nil, ctx.Err()
 		default:
 			dgst := digest.Digest(id)
-			if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 {
+			if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
 				continue
 			}
 			if !until.IsZero() && img.Created.After(until) {

+ 1 - 1
daemon/start.go

@@ -222,7 +222,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
 	if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
 		// FIXME: remove once reference counting for graphdrivers has been refactored
 		// Ensure that all the mounts are gone
-		if mountid, err := daemon.stores[container.OS].layerStore.GetMountID(container.ID); err == nil {
+		if mountid, err := daemon.layerStore.GetMountID(container.ID); err == nil {
 			daemon.cleanupMountsByID(mountid)
 		}
 	}

+ 10 - 10
distribution/config.go

@@ -59,9 +59,9 @@ type ImagePullConfig struct {
 	// Schema2Types is the valid schema2 configuration types allowed
 	// by the pull operation.
 	Schema2Types []string
-	// Platform is the requested platform of the image being pulled to ensure it can be validated
-	// when the host platform supports multiple image operating systems.
-	Platform string
+	// OS is the requested operating system of the image being pulled to ensure it can be validated
+	// when the host OS supports multiple image operating systems.
+	OS string
 }
 
 // ImagePushConfig stores push configuration.
@@ -86,7 +86,7 @@ type ImagePushConfig struct {
 type ImageConfigStore interface {
 	Put([]byte) (digest.Digest, error)
 	Get(digest.Digest) ([]byte, error)
-	RootFSAndOSFromConfig([]byte) (*image.RootFS, layer.OS, error)
+	RootFSAndOSFromConfig([]byte) (*image.RootFS, string, error)
 }
 
 // PushLayerProvider provides layers to be pushed by ChainID.
@@ -112,7 +112,7 @@ type RootFSDownloadManager interface {
 	// returns the final rootfs.
 	// Given progress output to track download progress
 	// Returns function to release download resources
-	Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
+	Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
 }
 
 type imageConfigStore struct {
@@ -140,7 +140,7 @@ func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) {
 	return img.RawJSON(), nil
 }
 
-func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
+func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
 	var unmarshalledConfig image.Image
 	if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
 		return nil, "", err
@@ -154,11 +154,11 @@ func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer
 		return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
 	}
 
-	os := ""
-	if runtime.GOOS == "windows" {
-		os = unmarshalledConfig.OS
+	os := unmarshalledConfig.OS
+	if os == "" {
+		os = runtime.GOOS
 	}
-	return unmarshalledConfig.RootFS, layer.OS(os), nil
+	return unmarshalledConfig.RootFS, os, nil
 }
 
 type storeLayerProvider struct {

+ 1 - 3
distribution/metadata/metadata.go

@@ -26,17 +26,15 @@ type Store interface {
 type FSMetadataStore struct {
 	sync.RWMutex
 	basePath string
-	platform string
 }
 
 // NewFSMetadataStore creates a new filesystem-based metadata store.
-func NewFSMetadataStore(basePath, platform string) (*FSMetadataStore, error) {
+func NewFSMetadataStore(basePath string) (*FSMetadataStore, error) {
 	if err := os.MkdirAll(basePath, 0700); err != nil {
 		return nil, err
 	}
 	return &FSMetadataStore{
 		basePath: basePath,
-		platform: platform,
 	}, nil
 }
 

+ 1 - 2
distribution/metadata/v1_id_service_test.go

@@ -3,7 +3,6 @@ package metadata
 import (
 	"io/ioutil"
 	"os"
-	"runtime"
 	"testing"
 
 	"github.com/docker/docker/layer"
@@ -17,7 +16,7 @@ func TestV1IDService(t *testing.T) {
 	}
 	defer os.RemoveAll(tmpDir)
 
-	metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS)
+	metadataStore, err := NewFSMetadataStore(tmpDir)
 	if err != nil {
 		t.Fatalf("could not create metadata store: %v", err)
 	}

+ 1 - 2
distribution/metadata/v2_metadata_service_test.go

@@ -6,7 +6,6 @@ import (
 	"math/rand"
 	"os"
 	"reflect"
-	"runtime"
 	"testing"
 
 	"github.com/docker/docker/layer"
@@ -20,7 +19,7 @@ func TestV2MetadataService(t *testing.T) {
 	}
 	defer os.RemoveAll(tmpDir)
 
-	metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS)
+	metadataStore, err := NewFSMetadataStore(tmpDir)
 	if err != nil {
 		t.Fatalf("could not create metadata store: %v", err)
 	}

+ 5 - 5
distribution/pull.go

@@ -21,7 +21,7 @@ type Puller interface {
 	// Pull tries to pull the image referenced by `tag`
 	// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
 	//
-	Pull(ctx context.Context, ref reference.Named, platform string) error
+	Pull(ctx context.Context, ref reference.Named, os string) error
 }
 
 // newPuller returns a Puller interface that will pull from either a v1 or v2
@@ -115,12 +115,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 			continue
 		}
 
-		// Make sure we default the platform if it hasn't been supplied
-		if imagePullConfig.Platform == "" {
-			imagePullConfig.Platform = runtime.GOOS
+		// Make sure we default the OS if it hasn't been supplied
+		if imagePullConfig.OS == "" {
+			imagePullConfig.OS = runtime.GOOS
 		}
 
-		if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil {
+		if err := puller.Pull(ctx, ref, imagePullConfig.OS); err != nil {
 			// Was this pull cancelled? If so, don't try to fall
 			// back.
 			fallback := false

+ 1 - 1
distribution/pull_v1.go

@@ -36,7 +36,7 @@ type v1Puller struct {
 	session     *registry.Session
 }
 
-func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, platform string) error {
+func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, os string) error {
 	if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		// Allowing fallback, because HTTPS v1 is before HTTP v2
 		return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}}

+ 12 - 12
distribution/pull_v2.go

@@ -62,7 +62,7 @@ type v2Puller struct {
 	confirmedV2 bool
 }
 
-func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform string) (err error) {
+func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (err error) {
 	// TODO(tiborvass): was ReceiveTimeout
 	p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
 	if err != nil {
@@ -70,7 +70,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform strin
 		return err
 	}
 
-	if err = p.pullV2Repository(ctx, ref, platform); err != nil {
+	if err = p.pullV2Repository(ctx, ref, os); err != nil {
 		if _, ok := err.(fallbackError); ok {
 			return err
 		}
@@ -85,10 +85,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform strin
 	return err
 }
 
-func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform string) (err error) {
+func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os string) (err error) {
 	var layersDownloaded bool
 	if !reference.IsNameOnly(ref) {
-		layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
+		layersDownloaded, err = p.pullV2Tag(ctx, ref, os)
 		if err != nil {
 			return err
 		}
@@ -110,7 +110,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, pl
 			if err != nil {
 				return err
 			}
-			pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
+			pulledNew, err := p.pullV2Tag(ctx, tagRef, os)
 			if err != nil {
 				// Since this is the pull-all-tags case, don't
 				// allow an error pulling a particular tag to
@@ -488,9 +488,9 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
 		descriptors = append(descriptors, layerDescriptor)
 	}
 
-	// The v1 manifest itself doesn't directly contain a platform. However,
+	// The v1 manifest itself doesn't directly contain an OS. However,
 	// the history does, but unfortunately that's a string, so search through
-	// all the history until hopefully we find one which indicates the os.
+	// all the history until hopefully we find one which indicates the OS.
 	// supertest2014/nyan is an example of a registry image with schemav1.
 	configOS := runtime.GOOS
 	if system.LCOWSupported() {
@@ -514,7 +514,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
 		return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
 	}
 
-	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, layer.OS(configOS), descriptors, p.config.ProgressOutput)
+	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput)
 	if err != nil {
 		return "", "", err
 	}
@@ -588,7 +588,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
 		downloadedRootFS *image.RootFS // rootFS from registered layers
 		configRootFS     *image.RootFS // rootFS from configuration
 		release          func()        // release resources from rootFS download
-		configOS         layer.OS      // for LCOW when registering downloaded layers
+		configOS         string        // for LCOW when registering downloaded layers
 	)
 
 	// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
@@ -615,7 +615,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
 
 		// Early bath if the requested OS doesn't match that of the configuration.
 		// This avoids doing the download, only to potentially fail later.
-		if !strings.EqualFold(string(configOS), requestedOS) {
+		if !strings.EqualFold(configOS, requestedOS) {
 			return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
 		}
 
@@ -633,7 +633,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
 				rootFS image.RootFS
 			)
 			downloadRootFS := *image.NewRootFS()
-			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layer.OS(requestedOS), descriptors, p.config.ProgressOutput)
+			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, requestedOS, descriptors, p.config.ProgressOutput)
 			if err != nil {
 				// Intentionally do not cancel the config download here
 				// as the error from config download (if there is one)
@@ -698,7 +698,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
 	return imageID, manifestDigest, nil
 }
 
-func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, layer.OS, error) {
+func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, string, error) {
 	select {
 	case configJSON := <-configChan:
 		rootfs, os, err := s.RootFSAndOSFromConfig(configJSON)

+ 13 - 19
distribution/xfer/download.go

@@ -4,7 +4,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"runtime"
 	"time"
 
 	"github.com/docker/distribution"
@@ -23,7 +22,7 @@ const maxDownloadAttempts = 5
 // registers and downloads those, taking into account dependencies between
 // layers.
 type LayerDownloadManager struct {
-	layerStores  map[string]layer.Store
+	layerStore   layer.Store
 	tm           TransferManager
 	waitDuration time.Duration
 }
@@ -34,9 +33,9 @@ func (ldm *LayerDownloadManager) SetConcurrency(concurrency int) {
 }
 
 // NewLayerDownloadManager returns a new LayerDownloadManager.
-func NewLayerDownloadManager(layerStores map[string]layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager {
+func NewLayerDownloadManager(layerStore layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager {
 	manager := LayerDownloadManager{
-		layerStores:  layerStores,
+		layerStore:   layerStore,
 		tm:           NewTransferManager(concurrencyLimit),
 		waitDuration: time.Second,
 	}
@@ -95,7 +94,7 @@ type DownloadDescriptorWithRegistered interface {
 // Download method is called to get the layer tar data. Layers are then
 // registered in the appropriate order.  The caller must call the returned
 // release function once it is done with the returned RootFS object.
-func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
+func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
 	var (
 		topLayer       layer.Layer
 		topDownload    *downloadTransfer
@@ -105,11 +104,6 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
 		downloadsByKey = make(map[string]*downloadTransfer)
 	)
 
-	// Assume that the operating system is the host OS if blank
-	if os == "" {
-		os = layer.OS(runtime.GOOS)
-	}
-
 	rootFS := initialRootFS
 	for _, descriptor := range layers {
 		key := descriptor.Key()
@@ -121,20 +115,20 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
 			if err == nil {
 				getRootFS := rootFS
 				getRootFS.Append(diffID)
-				l, err := ldm.layerStores[string(os)].Get(getRootFS.ChainID())
+				l, err := ldm.layerStore.Get(getRootFS.ChainID())
 				if err == nil {
 					// Layer already exists.
 					logrus.Debugf("Layer already exists: %s", descriptor.ID())
 					progress.Update(progressOutput, descriptor.ID(), "Already exists")
 					if topLayer != nil {
-						layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
+						layer.ReleaseAndLog(ldm.layerStore, topLayer)
 					}
 					topLayer = l
 					missingLayer = false
 					rootFS.Append(diffID)
 					// Register this repository as a source of this layer.
 					withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered)
-					if hasRegistered {
+					if hasRegistered { // As layerstore may set the driver
 						withRegistered.Registered(diffID)
 					}
 					continue
@@ -171,7 +165,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
 	if topDownload == nil {
 		return rootFS, func() {
 			if topLayer != nil {
-				layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
+				layer.ReleaseAndLog(ldm.layerStore, topLayer)
 			}
 		}, nil
 	}
@@ -182,7 +176,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
 
 	defer func() {
 		if topLayer != nil {
-			layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
+			layer.ReleaseAndLog(ldm.layerStore, topLayer)
 		}
 	}()
 
@@ -218,11 +212,11 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
 // complete before the registration step, and registers the downloaded data
 // on top of parentDownload's resulting layer. Otherwise, it registers the
 // layer on top of the ChainID given by parentLayer.
-func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os layer.OS) DoFunc {
+func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os string) DoFunc {
 	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
 		d := &downloadTransfer{
 			Transfer:   NewTransfer(),
-			layerStore: ldm.layerStores[string(os)],
+			layerStore: ldm.layerStore,
 		}
 
 		go func() {
@@ -382,11 +376,11 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
 // parentDownload. This function does not log progress output because it would
 // interfere with the progress reporting for sourceDownload, which has the same
 // Key.
-func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os layer.OS) DoFunc {
+func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os string) DoFunc {
 	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
 		d := &downloadTransfer{
 			Transfer:   NewTransfer(),
-			layerStore: ldm.layerStores[string(os)],
+			layerStore: ldm.layerStore,
 		}
 
 		go func() {

+ 11 - 15
distribution/xfer/download_test.go

@@ -26,7 +26,7 @@ type mockLayer struct {
 	diffID    layer.DiffID
 	chainID   layer.ChainID
 	parent    layer.Layer
-	os        layer.OS
+	os        string
 }
 
 func (ml *mockLayer) TarStream() (io.ReadCloser, error) {
@@ -57,7 +57,7 @@ func (ml *mockLayer) DiffSize() (size int64, err error) {
 	return 0, nil
 }
 
-func (ml *mockLayer) OS() layer.OS {
+func (ml *mockLayer) OS() string {
 	return ml.os
 }
 
@@ -91,7 +91,7 @@ func (ls *mockLayerStore) Map() map[layer.ChainID]layer.Layer {
 	return layers
 }
 
-func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, os layer.OS) (layer.Layer, error) {
+func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, os string) (layer.Layer, error) {
 	return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{})
 }
 
@@ -131,7 +131,7 @@ func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) {
 func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) {
 	return []layer.Metadata{}, nil
 }
-func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, *layer.CreateRWLayerOpts) (layer.RWLayer, error) {
+func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, *layer.CreateRWLayerOpts) (layer.RWLayer, error) {
 	return nil, errors.New("not implemented")
 }
 
@@ -150,11 +150,11 @@ func (ls *mockLayerStore) Cleanup() error {
 	return nil
 }
 
-func (ls *mockLayerStore) DriverStatus() [][2]string {
+func (ls *mockLayerStore) DriverStatus(string) [][2]string {
 	return [][2]string{}
 }
 
-func (ls *mockLayerStore) DriverName() string {
+func (ls *mockLayerStore) DriverName(string) string {
 	return "mock"
 }
 
@@ -272,9 +272,7 @@ func TestSuccessfulDownload(t *testing.T) {
 	}
 
 	layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)}
-	lsMap := make(map[string]layer.Store)
-	lsMap[runtime.GOOS] = layerStore
-	ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
+	ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
 
 	progressChan := make(chan progress.Progress)
 	progressDone := make(chan struct{})
@@ -293,13 +291,13 @@ func TestSuccessfulDownload(t *testing.T) {
 	firstDescriptor := descriptors[0].(*mockDownloadDescriptor)
 
 	// Pre-register the first layer to simulate an already-existing layer
-	l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.OS(runtime.GOOS))
+	l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
 	firstDescriptor.diffID = l.DiffID()
 
-	rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
+	rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), runtime.GOOS, descriptors, progress.ChanOutput(progressChan))
 	if err != nil {
 		t.Fatalf("download error: %v", err)
 	}
@@ -336,9 +334,7 @@ func TestSuccessfulDownload(t *testing.T) {
 
 func TestCancelledDownload(t *testing.T) {
 	layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)}
-	lsMap := make(map[string]layer.Store)
-	lsMap[runtime.GOOS] = layerStore
-	ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
+	ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
 
 	progressChan := make(chan progress.Progress)
 	progressDone := make(chan struct{})
@@ -357,7 +353,7 @@ func TestCancelledDownload(t *testing.T) {
 	}()
 
 	descriptors := downloadDescriptors(nil)
-	_, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
+	_, _, err := ldm.Download(ctx, *image.NewRootFS(), runtime.GOOS, descriptors, progress.ChanOutput(progressChan))
 	if err != context.Canceled {
 		t.Fatal("expected download to be cancelled")
 	}

+ 1 - 13
image/store.go

@@ -3,13 +3,11 @@ package image
 import (
 	"encoding/json"
 	"fmt"
-	"strings"
 	"sync"
 	"time"
 
 	"github.com/docker/distribution/digestset"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/pkg/system"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -47,17 +45,15 @@ type store struct {
 	images    map[ID]*imageMeta
 	fs        StoreBackend
 	digestSet *digestset.Set
-	os        string
 }
 
 // NewImageStore returns new store object for given layer store
-func NewImageStore(fs StoreBackend, os string, ls LayerGetReleaser) (Store, error) {
+func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) {
 	is := &store{
 		ls:        ls,
 		images:    make(map[ID]*imageMeta),
 		fs:        fs,
 		digestSet: digestset.NewSet(),
-		os:        os,
 	}
 
 	// load all current images and retain layers
@@ -118,14 +114,6 @@ func (is *store) Create(config []byte) (ID, error) {
 		return "", err
 	}
 
-	// TODO @jhowardmsft - LCOW Support. This will need revisiting when coalescing the image stores.
-	// Integrity check - ensure we are creating something for the correct platform
-	if system.LCOWSupported() {
-		if strings.ToLower(img.OperatingSystem()) != strings.ToLower(is.os) {
-			return "", fmt.Errorf("cannot create entry for operating system %q in image store for operating system %q", img.OperatingSystem(), is.os)
-		}
-	}
-
 	// Must reject any config that references diffIDs from the history
 	// which aren't among the rootfs layers.
 	rootFSLayers := make(map[layer.DiffID]struct{})

+ 2 - 3
image/store_test.go

@@ -1,7 +1,6 @@
 package image
 
 import (
-	"runtime"
 	"testing"
 
 	"github.com/docker/docker/internal/testutil"
@@ -26,7 +25,7 @@ func TestRestore(t *testing.T) {
 	err = fs.SetMetadata(id2, "parent", []byte(id1))
 	assert.NoError(t, err)
 
-	is, err := NewImageStore(fs, runtime.GOOS, &mockLayerGetReleaser{})
+	is, err := NewImageStore(fs, &mockLayerGetReleaser{})
 	assert.NoError(t, err)
 
 	assert.Len(t, is.Map(), 2)
@@ -143,7 +142,7 @@ func TestParentReset(t *testing.T) {
 func defaultImageStore(t *testing.T) (Store, func()) {
 	fsBackend, cleanup := defaultFSStoreBackend(t)
 
-	store, err := NewImageStore(fsBackend, runtime.GOOS, &mockLayerGetReleaser{})
+	store, err := NewImageStore(fsBackend, &mockLayerGetReleaser{})
 	assert.NoError(t, err)
 
 	return store, cleanup

+ 15 - 16
image/tarexport/load.go

@@ -90,11 +90,11 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		}
 
 		// On Windows, validate the platform, defaulting to windows if not present.
-		os := layer.OS(img.OS)
+		os := img.OS
+		if os == "" {
+			os = runtime.GOOS
+		}
 		if runtime.GOOS == "windows" {
-			if os == "" {
-				os = "windows"
-			}
 			if (os != "windows") && (os != "linux") {
 				return fmt.Errorf("configuration for this image has an unsupported operating system: %s", os)
 			}
@@ -176,7 +176,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
 	return l.is.SetParent(id, parentID)
 }
 
-func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os layer.OS, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
+func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
 	// We use system.OpenSequential to use sequential file access on Windows, avoiding
 	// depleting the standby list. On Linux, this equates to a regular os.Open.
 	rawTar, err := system.OpenSequential(filename)
@@ -409,19 +409,18 @@ func checkValidParent(img, parent *image.Image) bool {
 	return true
 }
 
-func checkCompatibleOS(os string) error {
-	// TODO @jhowardmsft LCOW - revisit for simultaneous platforms
-	platform := runtime.GOOS
-	if system.LCOWSupported() {
-		platform = "linux"
-	}
-	// always compatible if the OS matches; also match an empty OS
-	if os == platform || os == "" {
+func checkCompatibleOS(imageOS string) error {
+	// always compatible if the images OS matches the host OS; also match an empty image OS
+	if imageOS == runtime.GOOS || imageOS == "" {
 		return nil
 	}
-	// for compatibility, only fail if the image or runtime OS is Windows
-	if os == "windows" || platform == "windows" {
-		return fmt.Errorf("cannot load %s image on %s", os, platform)
+	// On non-Windows hosts, for compatibility, fail if the image is Windows.
+	if runtime.GOOS != "windows" && imageOS == "windows" {
+		return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS)
+	}
+	// Finally, check the image OS is supported for the platform.
+	if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil {
+		return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err)
 	}
 	return nil
 }

+ 3 - 2
layer/empty.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"runtime"
 )
 
 // DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
@@ -55,8 +56,8 @@ func (el *emptyLayer) Metadata() (map[string]string, error) {
 	return make(map[string]string), nil
 }
 
-func (el *emptyLayer) OS() OS {
-	return ""
+func (el *emptyLayer) OS() string {
+	return runtime.GOOS
 }
 
 // IsEmpty returns true if the layer is an EmptyLayer

+ 5 - 3
layer/filestore_unix.go

@@ -2,12 +2,14 @@
 
 package layer
 
+import "runtime"
+
 // SetOS writes the "os" file to the layer filestore
-func (fm *fileMetadataTransaction) SetOS(os OS) error {
+func (fm *fileMetadataTransaction) SetOS(os string) error {
 	return nil
 }
 
 // GetOS reads the "os" file from the layer filestore
-func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) {
-	return "", nil
+func (fms *fileMetadataStore) GetOS(layer ChainID) (string, error) {
+	return runtime.GOOS, nil
 }

+ 3 - 3
layer/filestore_windows.go

@@ -8,7 +8,7 @@ import (
 )
 
 // SetOS writes the "os" file to the layer filestore
-func (fm *fileMetadataTransaction) SetOS(os OS) error {
+func (fm *fileMetadataTransaction) SetOS(os string) error {
 	if os == "" {
 		return nil
 	}
@@ -16,7 +16,7 @@ func (fm *fileMetadataTransaction) SetOS(os OS) error {
 }
 
 // GetOS reads the "os" file from the layer filestore
-func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) {
+func (fms *fileMetadataStore) GetOS(layer ChainID) (string, error) {
 	contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "os"))
 	if err != nil {
 		// For backwards compatibility, the os file may not exist. Default to "windows" if missing.
@@ -31,5 +31,5 @@ func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) {
 		return "", fmt.Errorf("invalid operating system value: %s", content)
 	}
 
-	return OS(content), nil
+	return content, nil
 }

+ 11 - 16
layer/layer.go

@@ -65,14 +65,6 @@ func (id ChainID) String() string {
 	return string(id)
 }
 
-// OS is the operating system of a layer
-type OS string
-
-// String returns a string rendition of layers target operating system
-func (id OS) String() string {
-	return string(id)
-}
-
 // DiffID is the hash of an individual layer tar.
 type DiffID digest.Digest
 
@@ -109,7 +101,7 @@ type Layer interface {
 	Parent() Layer
 
 	// OS returns the operating system of the layer
-	OS() OS
+	OS() string
 
 	// Size returns the size of the entire layer chain. The size
 	// is calculated from the total size of all files in the layers.
@@ -156,6 +148,9 @@ type RWLayer interface {
 
 	// Metadata returns the low level metadata for the mutable layer
 	Metadata() (map[string]string, error)
+
+	// OS returns the operating system of the writable layer
+	OS() string
 }
 
 // Metadata holds information about a
@@ -191,25 +186,25 @@ type CreateRWLayerOpts struct {
 // Store represents a backend for managing both
 // read-only and read-write layers.
 type Store interface {
-	Register(io.Reader, ChainID, OS) (Layer, error)
+	Register(io.Reader, ChainID, string) (Layer, error)
 	Get(ChainID) (Layer, error)
 	Map() map[ChainID]Layer
 	Release(Layer) ([]Metadata, error)
 
-	CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error)
+	CreateRWLayer(id string, parent ChainID, os string, opts *CreateRWLayerOpts) (RWLayer, error)
 	GetRWLayer(id string) (RWLayer, error)
 	GetMountID(id string) (string, error)
 	ReleaseRWLayer(RWLayer) ([]Metadata, error)
 
 	Cleanup() error
-	DriverStatus() [][2]string
-	DriverName() string
+	DriverStatus(os string) [][2]string
+	DriverName(os string) string
 }
 
 // DescribableStore represents a layer store capable of storing
 // descriptors for layers.
 type DescribableStore interface {
-	RegisterWithDescriptor(io.Reader, ChainID, OS, distribution.Descriptor) (Layer, error)
+	RegisterWithDescriptor(io.Reader, ChainID, string, distribution.Descriptor) (Layer, error)
 }
 
 // MetadataTransaction represents functions for setting layer metadata
@@ -220,7 +215,7 @@ type MetadataTransaction interface {
 	SetDiffID(DiffID) error
 	SetCacheID(string) error
 	SetDescriptor(distribution.Descriptor) error
-	SetOS(OS) error
+	SetOS(string) error
 	TarSplitWriter(compressInput bool) (io.WriteCloser, error)
 
 	Commit(ChainID) error
@@ -241,7 +236,7 @@ type MetadataStore interface {
 	GetDiffID(ChainID) (DiffID, error)
 	GetCacheID(ChainID) (string, error)
 	GetDescriptor(ChainID) (distribution.Descriptor, error)
-	GetOS(ChainID) (OS, error)
+	GetOS(ChainID) (string, error)
 	TarSplitReader(ChainID) (io.ReadCloser, error)
 
 	SetMountID(string, string) error

+ 91 - 66
layer/layer_store.go

@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"strings"
+	"runtime"
 	"sync"
 
 	"github.com/docker/distribution"
@@ -28,70 +28,77 @@ import (
 const maxLayerDepth = 125
 
 type layerStore struct {
-	store  MetadataStore
-	driver graphdriver.Driver
+	store       MetadataStore
+	drivers     map[string]graphdriver.Driver
+	useTarSplit map[string]bool
 
 	layerMap map[ChainID]*roLayer
 	layerL   sync.Mutex
 
 	mounts map[string]*mountedLayer
 	mountL sync.Mutex
-
-	useTarSplit bool
-
-	os string
 }
 
 // StoreOptions are the options used to create a new Store instance
 type StoreOptions struct {
-	StorePath                 string
+	Root                      string
+	GraphDrivers              map[string]string
 	MetadataStorePathTemplate string
-	GraphDriver               string
 	GraphDriverOptions        []string
 	IDMappings                *idtools.IDMappings
 	PluginGetter              plugingetter.PluginGetter
 	ExperimentalEnabled       bool
-	OS                        string
 }
 
 // NewStoreFromOptions creates a new Store instance
 func NewStoreFromOptions(options StoreOptions) (Store, error) {
-	driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
-		Root:                options.StorePath,
-		DriverOptions:       options.GraphDriverOptions,
-		UIDMaps:             options.IDMappings.UIDs(),
-		GIDMaps:             options.IDMappings.GIDs(),
-		ExperimentalEnabled: options.ExperimentalEnabled,
-	})
-	if err != nil {
-		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
+	drivers := make(map[string]graphdriver.Driver)
+	for os, drivername := range options.GraphDrivers {
+		var err error
+		drivers[os], err = graphdriver.New(drivername,
+			options.PluginGetter,
+			graphdriver.Options{
+				Root:                options.Root,
+				DriverOptions:       options.GraphDriverOptions,
+				UIDMaps:             options.IDMappings.UIDs(),
+				GIDMaps:             options.IDMappings.GIDs(),
+				ExperimentalEnabled: options.ExperimentalEnabled,
+			})
+		if err != nil {
+			return nil, fmt.Errorf("error initializing graphdriver: %v", err)
+		}
+		logrus.Debugf("Initialized graph driver %s", drivername)
 	}
-	logrus.Debugf("Using graph driver %s", driver)
 
-	fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, driver))
+	fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, options.GraphDrivers[runtime.GOOS]))
 	if err != nil {
 		return nil, err
 	}
 
-	return NewStoreFromGraphDriver(fms, driver, options.OS)
+	return NewStoreFromGraphDrivers(fms, drivers)
 }
 
-// NewStoreFromGraphDriver creates a new Store instance using the provided
-// metadata store and graph driver. The metadata store will be used to restore
+// NewStoreFromGraphDrivers creates a new Store instance using the provided
+// metadata store and graph drivers. The metadata store will be used to restore
 // the Store.
-func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, os string) (Store, error) {
-	caps := graphdriver.Capabilities{}
-	if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok {
-		caps = capDriver.Capabilities()
+func NewStoreFromGraphDrivers(store MetadataStore, drivers map[string]graphdriver.Driver) (Store, error) {
+
+	useTarSplit := make(map[string]bool)
+	for os, driver := range drivers {
+
+		caps := graphdriver.Capabilities{}
+		if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok {
+			caps = capDriver.Capabilities()
+		}
+		useTarSplit[os] = !caps.ReproducesExactDiffs
 	}
 
 	ls := &layerStore{
 		store:       store,
-		driver:      driver,
+		drivers:     drivers,
 		layerMap:    map[ChainID]*roLayer{},
 		mounts:      map[string]*mountedLayer{},
-		useTarSplit: !caps.ReproducesExactDiffs,
-		os:          os,
+		useTarSplit: useTarSplit,
 	}
 
 	ids, mounts, err := store.List()
@@ -227,7 +234,7 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
 	tr := io.TeeReader(ts, digester.Hash())
 
 	rdr := tr
-	if ls.useTarSplit {
+	if ls.useTarSplit[layer.os] {
 		tsw, err := tx.TarSplitWriter(true)
 		if err != nil {
 			return err
@@ -243,7 +250,7 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
 		}
 	}
 
-	applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr)
+	applySize, err := ls.drivers[layer.os].ApplyDiff(layer.cacheID, parent, rdr)
 	if err != nil {
 		return err
 	}
@@ -259,11 +266,11 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
 	return nil
 }
 
-func (ls *layerStore) Register(ts io.Reader, parent ChainID, os OS) (Layer, error) {
+func (ls *layerStore) Register(ts io.Reader, parent ChainID, os string) (Layer, error) {
 	return ls.registerWithDescriptor(ts, parent, os, distribution.Descriptor{})
 }
 
-func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) {
+func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os string, descriptor distribution.Descriptor) (Layer, error) {
 	// err is used to hold the error which will always trigger
 	// cleanup of creates sources but may not be an error returned
 	// to the caller (already exists).
@@ -271,13 +278,6 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS
 	var pid string
 	var p *roLayer
 
-	// Integrity check - ensure we are creating something for the correct operating system
-	if system.LCOWSupported() {
-		if strings.ToLower(ls.os) != strings.ToLower(string(os)) {
-			return nil, fmt.Errorf("cannot create entry for operating system %q in layer store for operating system %q", os, ls.os)
-		}
-	}
-
 	if string(parent) != "" {
 		p = ls.get(parent)
 		if p == nil {
@@ -298,6 +298,14 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS
 		}
 	}
 
+	// Validate the operating system is valid
+	if os == "" {
+		os = runtime.GOOS
+	}
+	if err := system.ValidatePlatform(system.ParsePlatform(os)); err != nil {
+		return nil, err
+	}
+
 	// Create new roLayer
 	layer := &roLayer{
 		parent:         p,
@@ -309,7 +317,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS
 		os:             os,
 	}
 
-	if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil {
+	if err = ls.drivers[os].Create(layer.cacheID, pid, nil); err != nil {
 		return nil, err
 	}
 
@@ -321,7 +329,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS
 	defer func() {
 		if err != nil {
 			logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, err)
-			if err := ls.driver.Remove(layer.cacheID); err != nil {
+			if err := ls.drivers[os].Remove(layer.cacheID); err != nil {
 				logrus.Errorf("Error cleaning up cache layer %s: %v", layer.cacheID, err)
 			}
 			if err := tx.Cancel(); err != nil {
@@ -405,7 +413,7 @@ func (ls *layerStore) Map() map[ChainID]Layer {
 }
 
 func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error {
-	err := ls.driver.Remove(layer.cacheID)
+	err := ls.drivers[layer.os].Remove(layer.cacheID)
 	if err != nil {
 		return err
 	}
@@ -475,7 +483,7 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) {
 	return ls.releaseLayer(layer)
 }
 
-func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) {
+func (ls *layerStore) CreateRWLayer(name string, parent ChainID, os string, opts *CreateRWLayerOpts) (RWLayer, error) {
 	var (
 		storageOpt map[string]string
 		initFunc   MountInit
@@ -515,16 +523,21 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 		}()
 	}
 
+	// Ensure the operating system is set to the host OS if not populated.
+	if os == "" {
+		os = runtime.GOOS
+	}
 	m = &mountedLayer{
 		name:       name,
 		parent:     p,
 		mountID:    ls.mountID(name),
 		layerStore: ls,
 		references: map[RWLayer]*referencedRWLayer{},
+		os:         os,
 	}
 
 	if initFunc != nil {
-		pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)
+		pid, err = ls.initMount(m.mountID, m.os, pid, mountLabel, initFunc, storageOpt)
 		if err != nil {
 			return nil, err
 		}
@@ -535,7 +548,7 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 		StorageOpt: storageOpt,
 	}
 
-	if err = ls.driver.CreateReadWrite(m.mountID, pid, createOpts); err != nil {
+	if err = ls.drivers[os].CreateReadWrite(m.mountID, pid, createOpts); err != nil {
 		return nil, err
 	}
 	if err = ls.saveMount(m); err != nil {
@@ -584,14 +597,14 @@ func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) {
 		return []Metadata{}, nil
 	}
 
-	if err := ls.driver.Remove(m.mountID); err != nil {
+	if err := ls.drivers[l.OS()].Remove(m.mountID); err != nil {
 		logrus.Errorf("Error removing mounted layer %s: %s", m.name, err)
 		m.retakeReference(l)
 		return nil, err
 	}
 
 	if m.initID != "" {
-		if err := ls.driver.Remove(m.initID); err != nil {
+		if err := ls.drivers[l.OS()].Remove(m.initID); err != nil {
 			logrus.Errorf("Error removing init layer %s: %s", m.name, err)
 			m.retakeReference(l)
 			return nil, err
@@ -637,7 +650,7 @@ func (ls *layerStore) saveMount(mount *mountedLayer) error {
 	return nil
 }
 
-func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
+func (ls *layerStore) initMount(graphID, os, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
 	// Use "<graph-id>-init" to maintain compatibility with graph drivers
 	// which are expecting this layer with this special name. If all
 	// graph drivers can be updated to not rely on knowing about this layer
@@ -649,20 +662,20 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou
 		StorageOpt: storageOpt,
 	}
 
-	if err := ls.driver.CreateReadWrite(initID, parent, createOpts); err != nil {
+	if err := ls.drivers[os].CreateReadWrite(initID, parent, createOpts); err != nil {
 		return "", err
 	}
-	p, err := ls.driver.Get(initID, "")
+	p, err := ls.drivers[os].Get(initID, "")
 	if err != nil {
 		return "", err
 	}
 
 	if err := initFunc(p); err != nil {
-		ls.driver.Put(initID)
+		ls.drivers[os].Put(initID)
 		return "", err
 	}
 
-	if err := ls.driver.Put(initID); err != nil {
+	if err := ls.drivers[os].Put(initID); err != nil {
 		return "", err
 	}
 
@@ -670,13 +683,13 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou
 }
 
 func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) {
-	if !ls.useTarSplit {
+	if !ls.useTarSplit[rl.os] {
 		var parentCacheID string
 		if rl.parent != nil {
 			parentCacheID = rl.parent.cacheID
 		}
 
-		return ls.driver.Diff(rl.cacheID, parentCacheID)
+		return ls.drivers[rl.os].Diff(rl.cacheID, parentCacheID)
 	}
 
 	r, err := ls.store.TarSplitReader(rl.chainID)
@@ -686,7 +699,7 @@ func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) {
 
 	pr, pw := io.Pipe()
 	go func() {
-		err := ls.assembleTarTo(rl.cacheID, r, nil, pw)
+		err := ls.assembleTarTo(rl.cacheID, rl.os, r, nil, pw)
 		if err != nil {
 			pw.CloseWithError(err)
 		} else {
@@ -697,10 +710,10 @@ func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) {
 	return pr, nil
 }
 
-func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size *int64, w io.Writer) error {
-	diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver)
+func (ls *layerStore) assembleTarTo(graphID, os string, metadata io.ReadCloser, size *int64, w io.Writer) error {
+	diffDriver, ok := ls.drivers[os].(graphdriver.DiffGetterDriver)
 	if !ok {
-		diffDriver = &naiveDiffPathDriver{ls.driver}
+		diffDriver = &naiveDiffPathDriver{ls.drivers[os]}
 	}
 
 	defer metadata.Close()
@@ -719,15 +732,27 @@ func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size
 }
 
 func (ls *layerStore) Cleanup() error {
-	return ls.driver.Cleanup()
+	var err error
+	for _, driver := range ls.drivers {
+		if e := driver.Cleanup(); e != nil {
+			err = fmt.Errorf("%s - %s", err.Error(), e.Error())
+		}
+	}
+	return err
 }
 
-func (ls *layerStore) DriverStatus() [][2]string {
-	return ls.driver.Status()
+func (ls *layerStore) DriverStatus(os string) [][2]string {
+	if os == "" {
+		os = runtime.GOOS
+	}
+	return ls.drivers[os].Status()
 }
 
-func (ls *layerStore) DriverName() string {
-	return ls.driver.String()
+func (ls *layerStore) DriverName(os string) string {
+	if os == "" {
+		os = runtime.GOOS
+	}
+	return ls.drivers[os].String()
 }
 
 type naiveDiffPathDriver struct {

+ 1 - 1
layer/layer_store_windows.go

@@ -6,6 +6,6 @@ import (
 	"github.com/docker/distribution"
 )
 
-func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) {
+func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, os string, descriptor distribution.Descriptor) (Layer, error) {
 	return ls.registerWithDescriptor(ts, parent, os, descriptor)
 }

+ 17 - 15
layer/layer_test.go

@@ -73,7 +73,9 @@ func newTestStore(t *testing.T) (Store, string, func()) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS)
+	graphs := make(map[string]graphdriver.Driver)
+	graphs[runtime.GOOS] = graph
+	ls, err := NewStoreFromGraphDrivers(fms, graphs)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -88,7 +90,7 @@ type layerInit func(root containerfs.ContainerFS) error
 
 func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
 	containerID := stringid.GenerateRandomID()
-	mount, err := ls.CreateRWLayer(containerID, parent, nil)
+	mount, err := ls.CreateRWLayer(containerID, parent, runtime.GOOS, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -108,7 +110,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
 	}
 	defer ts.Close()
 
-	layer, err := ls.Register(ts, parent, OS(runtime.GOOS))
+	layer, err := ls.Register(ts, parent, runtime.GOOS)
 	if err != nil {
 		return nil, err
 	}
@@ -277,7 +279,7 @@ func TestMountAndRegister(t *testing.T) {
 	size, _ := layer.Size()
 	t.Logf("Layer size: %d", size)
 
-	mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), nil)
+	mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), runtime.GOOS, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -385,7 +387,7 @@ func TestStoreRestore(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), nil)
+	m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), runtime.GOOS, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -403,7 +405,7 @@ func TestStoreRestore(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	ls2, err := NewStoreFromGraphDriver(ls.(*layerStore).store, ls.(*layerStore).driver, runtime.GOOS)
+	ls2, err := NewStoreFromGraphDrivers(ls.(*layerStore).store, ls.(*layerStore).drivers)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -416,7 +418,7 @@ func TestStoreRestore(t *testing.T) {
 	assertLayerEqual(t, layer3b, layer3)
 
 	// Create again with same name, should return error
-	if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), nil); err == nil {
+	if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), runtime.GOOS, nil); err == nil {
 		t.Fatal("Expected error creating mount with same name")
 	} else if err != ErrMountNameConflict {
 		t.Fatal(err)
@@ -498,13 +500,13 @@ func TestTarStreamStability(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
+	layer1, err := ls.Register(bytes.NewReader(tar1), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
 
 	// hack layer to add file
-	p, err := ls.(*layerStore).driver.Get(layer1.(*referencedCacheLayer).cacheID, "")
+	p, err := ls.(*layerStore).drivers[runtime.GOOS].Get(layer1.(*referencedCacheLayer).cacheID, "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -513,11 +515,11 @@ func TestTarStreamStability(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if err := ls.(*layerStore).driver.Put(layer1.(*referencedCacheLayer).cacheID); err != nil {
+	if err := ls.(*layerStore).drivers[runtime.GOOS].Put(layer1.(*referencedCacheLayer).cacheID); err != nil {
 		t.Fatal(err)
 	}
 
-	layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), OS(runtime.GOOS))
+	layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -685,12 +687,12 @@ func TestRegisterExistingLayer(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS))
+	layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS))
+	layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -725,12 +727,12 @@ func TestTarStreamVerification(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
+	layer1, err := ls.Register(bytes.NewReader(tar1), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	layer2, err := ls.Register(bytes.NewReader(tar2), "", OS(runtime.GOOS))
+	layer2, err := ls.Register(bytes.NewReader(tar2), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 5 - 2
layer/layer_unix_test.go

@@ -2,7 +2,10 @@
 
 package layer
 
-import "testing"
+import (
+	"runtime"
+	"testing"
+)
 
 func graphDiffSize(ls Store, l Layer) (int64, error) {
 	cl := getCachedLayer(l)
@@ -10,7 +13,7 @@ func graphDiffSize(ls Store, l Layer) (int64, error) {
 	if cl.parent != nil {
 		parent = cl.parent.cacheID
 	}
-	return ls.(*layerStore).driver.DiffSize(cl.cacheID, parent)
+	return ls.(*layerStore).drivers[runtime.GOOS].DiffSize(cl.cacheID, parent)
 }
 
 // Unix as Windows graph driver does not support Changes which is indirectly

+ 3 - 4
layer/layer_windows.go

@@ -25,16 +25,15 @@ func GetLayerPath(s Store, layer ChainID) (string, error) {
 		return "", ErrLayerDoesNotExist
 	}
 
-	if layerGetter, ok := ls.driver.(Getter); ok {
+	if layerGetter, ok := ls.drivers[rl.os].(Getter); ok {
 		return layerGetter.GetLayerPath(rl.cacheID)
 	}
-
-	path, err := ls.driver.Get(rl.cacheID, "")
+	path, err := ls.drivers[rl.os].Get(rl.cacheID, "")
 	if err != nil {
 		return "", err
 	}
 
-	if err := ls.driver.Put(rl.cacheID); err != nil {
+	if err := ls.drivers[rl.os].Put(rl.cacheID); err != nil {
 		return "", err
 	}
 

+ 17 - 5
layer/migration.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"runtime"
 
 	"github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
@@ -16,7 +17,7 @@ import (
 // CreateRWLayerByGraphID creates a RWLayer in the layer store using
 // the provided name with the given graphID. To get the RWLayer
 // after migration the layer may be retrieved by the given name.
-func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent ChainID) (err error) {
+func (ls *layerStore) CreateRWLayerByGraphID(name, graphID, os string, parent ChainID) (err error) {
 	ls.mountL.Lock()
 	defer ls.mountL.Unlock()
 	m, ok := ls.mounts[name]
@@ -31,7 +32,11 @@ func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent
 		return nil
 	}
 
-	if !ls.driver.Exists(graphID) {
+	// Ensure the operating system is set to the host OS if not populated.
+	if os == "" {
+		os = runtime.GOOS
+	}
+	if !ls.drivers[os].Exists(graphID) {
 		return fmt.Errorf("graph ID does not exist: %q", graphID)
 	}
 
@@ -60,11 +65,12 @@ func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent
 		mountID:    graphID,
 		layerStore: ls,
 		references: map[RWLayer]*referencedRWLayer{},
+		os:         os,
 	}
 
 	// Check for existing init layer
 	initID := fmt.Sprintf("%s-init", graphID)
-	if ls.driver.Exists(initID) {
+	if ls.drivers[os].Exists(initID) {
 		m.initID = initID
 	}
 
@@ -95,7 +101,10 @@ func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataP
 	}
 
 	dgst := digest.Canonical.Digester()
-	err = ls.assembleTarTo(id, uncompressed, &size, dgst.Hash())
+	// Note - we use the host OS here. This is a safe assumption as its during migration, and
+	// no host OS which supports migration also supports multiple image OS's. In other words,
+	// it's only on Linux, not on Windows.
+	err = ls.assembleTarTo(id, runtime.GOOS, uncompressed, &size, dgst.Hash())
 	if err != nil {
 		return
 	}
@@ -111,7 +120,10 @@ func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataP
 }
 
 func (ls *layerStore) checksumForGraphIDNoTarsplit(id, parent, newTarDataPath string) (diffID DiffID, size int64, err error) {
-	rawarchive, err := ls.driver.Diff(id, parent)
+	// Note - we use the host OS here. This is a safe assumption as its during migration, and
+	// no host OS which supports migration also supports multiple image OS's. In other words,
+	// it's only on Linux, not on Windows.
+	rawarchive, err := ls.drivers[runtime.GOOS].Diff(id, parent)
 	if err != nil {
 		return
 	}

+ 13 - 9
layer/migration_test.go

@@ -94,7 +94,9 @@ func TestLayerMigration(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS)
+	graphs := make(map[string]graphdriver.Driver)
+	graphs[runtime.GOOS] = graph
+	ls, err := NewStoreFromGraphDrivers(fms, graphs)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -110,14 +112,14 @@ func TestLayerMigration(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
+	layer1b, err := ls.Register(bytes.NewReader(tar1), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
 
 	assertReferences(t, layer1a, layer1b)
 	// Attempt register, should be same
-	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
+	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -222,7 +224,9 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS)
+	graphs := make(map[string]graphdriver.Driver)
+	graphs[runtime.GOOS] = graph
+	ls, err := NewStoreFromGraphDrivers(fms, graphs)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -238,7 +242,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
+	layer1b, err := ls.Register(bytes.NewReader(tar1), "", runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -246,7 +250,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
 	assertReferences(t, layer1a, layer1b)
 
 	// Attempt register, should be same
-	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
+	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), runtime.GOOS)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -308,7 +312,7 @@ func TestMountMigration(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	graph := ls.(*layerStore).driver
+	graph := ls.(*layerStore).drivers[runtime.GOOS]
 
 	layer1, err := createLayer(ls, "", initWithFiles(baseFiles...))
 	if err != nil {
@@ -334,7 +338,7 @@ func TestMountMigration(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil {
+	if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, runtime.GOOS, layer1.ChainID()); err != nil {
 		t.Fatal(err)
 	}
 
@@ -380,7 +384,7 @@ func TestMountMigration(t *testing.T) {
 		Kind: archive.ChangeAdd,
 	})
 
-	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), nil); err == nil {
+	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), runtime.GOOS, nil); err == nil {
 		t.Fatal("Expected error creating mount with same name")
 	} else if err != ErrMountNameConflict {
 		t.Fatal(err)

+ 3 - 3
layer/mount_test.go

@@ -35,7 +35,7 @@ func TestMountInit(t *testing.T) {
 	rwLayerOpts := &CreateRWLayerOpts{
 		InitFunc: mountInit,
 	}
-	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), rwLayerOpts)
+	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), runtime.GOOS, rwLayerOpts)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -95,7 +95,7 @@ func TestMountSize(t *testing.T) {
 		InitFunc: mountInit,
 	}
 
-	m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), rwLayerOpts)
+	m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), runtime.GOOS, rwLayerOpts)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -147,7 +147,7 @@ func TestMountChanges(t *testing.T) {
 		InitFunc: mountInit,
 	}
 
-	m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), rwLayerOpts)
+	m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), runtime.GOOS, rwLayerOpts)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 16 - 6
layer/mounted_layer.go

@@ -2,6 +2,7 @@ package layer
 
 import (
 	"io"
+	"runtime"
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/containerfs"
@@ -14,6 +15,7 @@ type mountedLayer struct {
 	parent     *roLayer
 	path       string
 	layerStore *layerStore
+	os         string
 
 	references map[RWLayer]*referencedRWLayer
 }
@@ -29,7 +31,7 @@ func (ml *mountedLayer) cacheParent() string {
 }
 
 func (ml *mountedLayer) TarStream() (io.ReadCloser, error) {
-	return ml.layerStore.driver.Diff(ml.mountID, ml.cacheParent())
+	return ml.layerStore.drivers[ml.OS()].Diff(ml.mountID, ml.cacheParent())
 }
 
 func (ml *mountedLayer) Name() string {
@@ -46,16 +48,24 @@ func (ml *mountedLayer) Parent() Layer {
 	return nil
 }
 
+func (ml *mountedLayer) OS() string {
+	// For backwards compatibility, return the host OS if not set.
+	if ml.os == "" {
+		return runtime.GOOS
+	}
+	return ml.os
+}
+
 func (ml *mountedLayer) Size() (int64, error) {
-	return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
+	return ml.layerStore.drivers[ml.OS()].DiffSize(ml.mountID, ml.cacheParent())
 }
 
 func (ml *mountedLayer) Changes() ([]archive.Change, error) {
-	return ml.layerStore.driver.Changes(ml.mountID, ml.cacheParent())
+	return ml.layerStore.drivers[ml.OS()].Changes(ml.mountID, ml.cacheParent())
 }
 
 func (ml *mountedLayer) Metadata() (map[string]string, error) {
-	return ml.layerStore.driver.GetMetadata(ml.mountID)
+	return ml.layerStore.drivers[ml.OS()].GetMetadata(ml.mountID)
 }
 
 func (ml *mountedLayer) getReference() RWLayer {
@@ -90,11 +100,11 @@ type referencedRWLayer struct {
 }
 
 func (rl *referencedRWLayer) Mount(mountLabel string) (containerfs.ContainerFS, error) {
-	return rl.layerStore.driver.Get(rl.mountedLayer.mountID, mountLabel)
+	return rl.layerStore.drivers[rl.OS()].Get(rl.mountedLayer.mountID, mountLabel)
 }
 
 // Unmount decrements the activity count and unmounts the underlying layer
 // Callers should only call `Unmount` once per call to `Mount`, even on error.
 func (rl *referencedRWLayer) Unmount() error {
-	return rl.layerStore.driver.Put(rl.mountedLayer.mountID)
+	return rl.layerStore.drivers[rl.OS()].Put(rl.mountedLayer.mountID)
 }

+ 3 - 3
layer/ro_layer.go

@@ -16,7 +16,7 @@ type roLayer struct {
 	size       int64
 	layerStore *layerStore
 	descriptor distribution.Descriptor
-	os         OS
+	os         string
 
 	referenceCount int
 	references     map[Layer]struct{}
@@ -52,7 +52,7 @@ func (rl *roLayer) TarStreamFrom(parent ChainID) (io.ReadCloser, error) {
 	if parent != ChainID("") && parentCacheID == "" {
 		return nil, fmt.Errorf("layer ID '%s' is not a parent of the specified layer: cannot provide diff to non-parent", parent)
 	}
-	return rl.layerStore.driver.Diff(rl.cacheID, parentCacheID)
+	return rl.layerStore.drivers[rl.OS()].Diff(rl.cacheID, parentCacheID)
 }
 
 func (rl *roLayer) ChainID() ChainID {
@@ -86,7 +86,7 @@ func (rl *roLayer) DiffSize() (size int64, err error) {
 }
 
 func (rl *roLayer) Metadata() (map[string]string, error) {
-	return rl.layerStore.driver.GetMetadata(rl.cacheID)
+	return rl.layerStore.drivers[rl.OS()].GetMetadata(rl.cacheID)
 }
 
 type referencedCacheLayer struct {

+ 4 - 2
layer/ro_layer_unix.go

@@ -2,6 +2,8 @@
 
 package layer
 
-func (rl *roLayer) OS() OS {
-	return ""
+import "runtime"
+
+func (rl *roLayer) OS() string {
+	return runtime.GOOS
 }

+ 1 - 1
layer/ro_layer_windows.go

@@ -8,7 +8,7 @@ func (rl *roLayer) Descriptor() distribution.Descriptor {
 	return rl.descriptor
 }
 
-func (rl *roLayer) OS() OS {
+func (rl *roLayer) OS() string {
 	if rl.os == "" {
 		return "windows"
 	}

+ 4 - 4
migrate/v1/migratev1_test.go

@@ -94,7 +94,7 @@ func TestMigrateContainers(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	is, err := image.NewImageStore(ifs, runtime.GOOS, ls)
+	is, err := image.NewImageStore(ifs, ls)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -172,12 +172,12 @@ func TestMigrateImages(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	is, err := image.NewImageStore(ifs, runtime.GOOS, ls)
+	is, err := image.NewImageStore(ifs, ls)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	ms, err := metadata.NewFSMetadataStore(filepath.Join(tmpdir, "distribution"), runtime.GOOS)
+	ms, err := metadata.NewFSMetadataStore(filepath.Join(tmpdir, "distribution"))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -430,7 +430,7 @@ func (l *mockLayer) DiffSize() (int64, error) {
 	return 0, nil
 }
 
-func (l *mockLayer) OS() layer.OS {
+func (l *mockLayer) OS() string {
 	return ""
 }
 

+ 1 - 1
pkg/system/init_windows.go

@@ -11,7 +11,7 @@ var lcowSupported = false
 // 2. Remove the getenv check when image-store is coalesced as shouldn't be needed anymore.
 func InitLCOW(experimental bool) {
 	v := GetOSVersion()
-	if experimental && v.Build > 16270 && os.Getenv("LCOW_SUPPORTED") != "" {
+	if experimental && v.Build > 16278 && os.Getenv("LCOW_SUPPORTED") != "" {
 		lcowSupported = true
 	}
 }

+ 2 - 2
plugin/backend_linux.go

@@ -145,7 +145,7 @@ func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) {
 	return s.config, nil
 }
 
-func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
+func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
 	return configToRootFS(c)
 }
 
@@ -532,7 +532,7 @@ func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) {
 	return ioutil.ReadAll(rwc)
 }
 
-func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
+func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
 	return configToRootFS(c)
 }
 

+ 2 - 3
plugin/blobstore.go

@@ -126,8 +126,7 @@ type downloadManager struct {
 	configDigest digest.Digest
 }
 
-func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
-	// TODO @jhowardmsft LCOW: May need revisiting.
+func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
 	for _, l := range layers {
 		b, err := dm.blobStore.New()
 		if err != nil {
@@ -179,6 +178,6 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) {
 func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) {
 	return nil, fmt.Errorf("digest not found")
 }
-func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
+func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
 	return configToRootFS(c)
 }

+ 3 - 6
plugin/manager.go

@@ -375,12 +375,9 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool {
 	return reflect.DeepEqual(a.Value, b.Value)
 }
 
-func configToRootFS(c []byte) (*image.RootFS, layer.OS, error) {
-	// TODO @jhowardmsft LCOW - Will need to revisit this. For now, calculate the operating system.
-	os := layer.OS(runtime.GOOS)
-	if system.LCOWSupported() {
-		os = "linux"
-	}
+func configToRootFS(c []byte) (*image.RootFS, string, error) {
+	// TODO @jhowardmsft LCOW - Will need to revisit this.
+	os := runtime.GOOS
 	var pluginConfig types.PluginConfig
 	if err := json.Unmarshal(c, &pluginConfig); err != nil {
 		return nil, "", err