From a7e686a779523100a092acb2683b849126953931 Mon Sep 17 00:00:00 2001
From: John Howard <jhoward@microsoft.com>
Date: Wed, 9 Sep 2015 19:23:06 -0700
Subject: [PATCH] Windows: Add volume support

Signed-off-by: John Howard <jhoward@microsoft.com>
---
 api/server/router/local/container.go          |   7 +-
 builder/dockerfile/evaluator.go               |   2 +-
 daemon/archive_unix.go                        |   2 +-
 daemon/container.go                           | 116 ++++++-
 daemon/container_unix.go                      | 118 ++-----
 daemon/container_windows.go                   |  18 +-
 daemon/create.go                              |  48 +--
 daemon/create_unix.go                         |   6 +-
 daemon/create_windows.go                      |  72 +++++
 daemon/daemon_unix.go                         |   7 +-
 daemon/daemonbuilder/builder.go               |   7 +-
 daemon/execdriver/driver.go                   |  10 +-
 daemon/execdriver/driver_unix.go              |   9 +
 daemon/execdriver/driver_windows.go           |   7 +
 daemon/execdriver/windows/run.go              |  47 ++-
 daemon/inspect_windows.go                     |  12 +-
 daemon/volumes.go                             | 208 +++++++-----
 daemon/volumes_linux_unit_test.go             |  58 ----
 daemon/volumes_unit_test.go                   |   7 +-
 daemon/volumes_unix.go                        | 250 +++------------
 daemon/volumes_windows.go                     |  60 ++--
 errors/daemon.go                              |  66 +++-
 image/fixtures/pre1.9/expected_config         |   1 +
 integration-cli/docker_cli_run_test.go        |   4 +-
 opts/opts.go                                  |   9 -
 opts/opts_test.go                             |  52 ---
 pkg/system/syscall_unix.go                    |  11 +
 pkg/system/syscall_windows.go                 |   6 +
 runconfig/config.go                           |  39 ++-
 runconfig/config_test.go                      |  37 ++-
 .../{ => unix}/container_config_1_14.json     |   0
 .../{ => unix}/container_config_1_17.json     |   0
 .../{ => unix}/container_config_1_19.json     |   0
 .../{ => unix}/container_hostconfig_1_14.json |   0
 .../{ => unix}/container_hostconfig_1_19.json |   0
 .../windows/container_config_1_19.json        |  58 ++++
 runconfig/hostconfig_test.go                  |   4 +-
 runconfig/parse.go                            |  10 +-
 runconfig/parse_test.go                       | 299 ++++++++++++++----
 volume/drivers/extpoint_test.go               |   1 -
 volume/store/store.go                         |  33 +-
 volume/store/store_unix.go                    |   9 +
 volume/store/store_windows.go                 |  12 +
 volume/volume.go                              | 147 +++++++--
 volume/volume_test.go                         | 261 +++++++++++++++
 volume/volume_unix.go                         | 132 ++++++++
 volume/volume_windows.go                      | 181 +++++++++++
 47 files changed, 1711 insertions(+), 732 deletions(-)
 delete mode 100644 daemon/volumes_linux_unit_test.go
 create mode 100644 pkg/system/syscall_unix.go
 create mode 100644 pkg/system/syscall_windows.go
 rename runconfig/fixtures/{ => unix}/container_config_1_14.json (100%)
 rename runconfig/fixtures/{ => unix}/container_config_1_17.json (100%)
 rename runconfig/fixtures/{ => unix}/container_config_1_19.json (100%)
 rename runconfig/fixtures/{ => unix}/container_hostconfig_1_14.json (100%)
 rename runconfig/fixtures/{ => unix}/container_hostconfig_1_19.json (100%)
 create mode 100644 runconfig/fixtures/windows/container_config_1_19.json
 create mode 100644 volume/store/store_unix.go
 create mode 100644 volume/store/store_windows.go
 create mode 100644 volume/volume_test.go
 create mode 100644 volume/volume_unix.go
 create mode 100644 volume/volume_windows.go

diff --git a/api/server/router/local/container.go b/api/server/router/local/container.go
index 9cf14367db..f43093378b 100644
--- a/api/server/router/local/container.go
+++ b/api/server/router/local/container.go
@@ -331,7 +331,12 @@ func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter
 	version := httputils.VersionFromContext(ctx)
 	adjustCPUShares := version.LessThan("1.19")
 
-	ccr, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares)
+	ccr, err := s.daemon.ContainerCreate(&daemon.ContainerCreateConfig{
+		Name:            name,
+		Config:          config,
+		HostConfig:      hostConfig,
+		AdjustCPUShares: adjustCPUShares,
+	})
 	if err != nil {
 		return err
 	}
diff --git a/builder/dockerfile/evaluator.go b/builder/dockerfile/evaluator.go
index 0629ee9bbc..a27018915f 100644
--- a/builder/dockerfile/evaluator.go
+++ b/builder/dockerfile/evaluator.go
@@ -186,7 +186,7 @@ func platformSupports(command string) error {
 		return nil
 	}
 	switch command {
-	case "expose", "volume", "user", "stopsignal", "arg":
+	case "expose", "user", "stopsignal", "arg":
 		return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
 	}
 	return nil
diff --git a/daemon/archive_unix.go b/daemon/archive_unix.go
index 100fc78880..7588d76d15 100644
--- a/daemon/archive_unix.go
+++ b/daemon/archive_unix.go
@@ -8,7 +8,7 @@ package daemon
 func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
 	var toVolume bool
 	for _, mnt := range container.MountPoints {
-		if toVolume = mnt.hasResource(absPath); toVolume {
+		if toVolume = mnt.HasResource(absPath); toVolume {
 			if mnt.RW {
 				break
 			}
diff --git a/daemon/container.go b/daemon/container.go
index 53807f90e6..a6bb4120ef 100644
--- a/daemon/container.go
+++ b/daemon/container.go
@@ -8,6 +8,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"sync"
 	"syscall"
 	"time"
@@ -30,8 +31,10 @@ import (
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/symlink"
+	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
+	"github.com/docker/docker/volume/store"
 )
 
 var (
@@ -72,6 +75,7 @@ type CommonContainer struct {
 	RestartCount           int
 	HasBeenStartedBefore   bool
 	HasBeenManuallyStopped bool // used for unless-stopped restart policy
+	MountPoints            map[string]*volume.MountPoint
 	hostConfig             *runconfig.HostConfig
 	command                *execdriver.Command
 	monitor                *containerMonitor
@@ -1108,29 +1112,109 @@ func (container *Container) mountVolumes() error {
 	return nil
 }
 
-func (container *Container) copyImagePathContent(v volume.Volume, destination string) error {
-	rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs)
-	if err != nil {
-		return err
-	}
-
-	if _, err = ioutil.ReadDir(rootfs); err != nil {
-		if os.IsNotExist(err) {
-			return nil
+func (container *Container) prepareMountPoints() error {
+	for _, config := range container.MountPoints {
+		if len(config.Driver) > 0 {
+			v, err := container.daemon.createVolume(config.Name, config.Driver, nil)
+			if err != nil {
+				return err
+			}
+			config.Volume = v
 		}
+	}
+	return nil
+}
+
+func (container *Container) removeMountPoints(rm bool) error {
+	var rmErrors []string
+	for _, m := range container.MountPoints {
+		if m.Volume == nil {
+			continue
+		}
+		container.daemon.volumes.Decrement(m.Volume)
+		if rm {
+			err := container.daemon.volumes.Remove(m.Volume)
+			// ErrVolumeInUse is ignored because having this
+			// volume being referenced by other container is
+			// not an error, but an implementation detail.
+			// This prevents docker from logging "ERROR: Volume in use"
+			// where there is another container using the volume.
+			if err != nil && err != store.ErrVolumeInUse {
+				rmErrors = append(rmErrors, err.Error())
+			}
+		}
+	}
+	if len(rmErrors) > 0 {
+		return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n"))
+	}
+	return nil
+}
+
+func (container *Container) unmountVolumes(forceSyscall bool) error {
+	var (
+		volumeMounts []volume.MountPoint
+		err          error
+	)
+
+	for _, mntPoint := range container.MountPoints {
+		dest, err := container.GetResourcePath(mntPoint.Destination)
+		if err != nil {
+			return err
+		}
+
+		volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume})
+	}
+
+	// Append any network mounts to the list (this is a no-op on Windows)
+	if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil {
 		return err
 	}
 
-	path, err := v.Mount()
-	if err != nil {
-		return err
+	for _, volumeMount := range volumeMounts {
+		if forceSyscall {
+			system.UnmountWithSyscall(volumeMount.Destination)
+		}
+
+		if volumeMount.Volume != nil {
+			if err := volumeMount.Volume.Unmount(); err != nil {
+				return err
+			}
+		}
 	}
 
-	if err := copyExistingContents(rootfs, path); err != nil {
-		return err
-	}
+	return nil
+}
 
-	return v.Unmount()
+func (container *Container) addBindMountPoint(name, source, destination string, rw bool) {
+	container.MountPoints[destination] = &volume.MountPoint{
+		Name:        name,
+		Source:      source,
+		Destination: destination,
+		RW:          rw,
+	}
+}
+
+func (container *Container) addLocalMountPoint(name, destination string, rw bool) {
+	container.MountPoints[destination] = &volume.MountPoint{
+		Name:        name,
+		Driver:      volume.DefaultDriverName,
+		Destination: destination,
+		RW:          rw,
+	}
+}
+
+func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
+	container.MountPoints[destination] = &volume.MountPoint{
+		Name:        vol.Name(),
+		Driver:      vol.DriverName(),
+		Destination: destination,
+		RW:          rw,
+		Volume:      vol,
+	}
+}
+
+func (container *Container) isDestinationMounted(destination string) bool {
+	return container.MountPoints[destination] != nil
 }
 
 func (container *Container) stopSignal() int {
diff --git a/daemon/container_unix.go b/daemon/container_unix.go
index 4141f982c3..aa7aa92a1d 100644
--- a/daemon/container_unix.go
+++ b/daemon/container_unix.go
@@ -23,12 +23,12 @@ import (
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/ulimit"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/volume"
-	"github.com/docker/docker/volume/store"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork/drivers/bridge"
 	"github.com/docker/libnetwork/netlabel"
@@ -54,9 +54,8 @@ type Container struct {
 	AppArmorProfile string
 	HostnamePath    string
 	HostsPath       string
-	ShmPath         string
-	MqueuePath      string
-	MountPoints     map[string]*mountPoint
+	ShmPath         string // TODO Windows - Factor this out (GH15862)
+	MqueuePath      string // TODO Windows - Factor this out (GH15862)
 	ResolvConfPath  string
 
 	Volumes   map[string]string // Deprecated since 1.7, kept for backwards compatibility
@@ -1197,40 +1196,16 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network) error {
 	return nil
 }
 
-func (container *Container) unmountVolumes(forceSyscall bool) error {
-	var volumeMounts []mountPoint
-
-	for _, mntPoint := range container.MountPoints {
-		dest, err := container.GetResourcePath(mntPoint.Destination)
-		if err != nil {
-			return err
-		}
-
-		volumeMounts = append(volumeMounts, mountPoint{Destination: dest, Volume: mntPoint.Volume})
-	}
-
+// appendNetworkMounts appends any network mounts to the array of mount points passed in
+func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
 	for _, mnt := range container.networkMounts() {
 		dest, err := container.GetResourcePath(mnt.Destination)
 		if err != nil {
-			return err
+			return nil, err
 		}
-
-		volumeMounts = append(volumeMounts, mountPoint{Destination: dest})
+		volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest})
 	}
-
-	for _, volumeMount := range volumeMounts {
-		if forceSyscall {
-			syscall.Unmount(volumeMount.Destination, 0)
-		}
-
-		if volumeMount.Volume != nil {
-			if err := volumeMount.Volume.Unmount(); err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
+	return volumeMounts, nil
 }
 
 func (container *Container) networkMounts() []execdriver.Mount {
@@ -1290,74 +1265,29 @@ func (container *Container) networkMounts() []execdriver.Mount {
 	return mounts
 }
 
-func (container *Container) addBindMountPoint(name, source, destination string, rw bool) {
-	container.MountPoints[destination] = &mountPoint{
-		Name:        name,
-		Source:      source,
-		Destination: destination,
-		RW:          rw,
+func (container *Container) copyImagePathContent(v volume.Volume, destination string) error {
+	rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs)
+	if err != nil {
+		return err
 	}
-}
 
-func (container *Container) addLocalMountPoint(name, destination string, rw bool) {
-	container.MountPoints[destination] = &mountPoint{
-		Name:        name,
-		Driver:      volume.DefaultDriverName,
-		Destination: destination,
-		RW:          rw,
-	}
-}
-
-func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
-	container.MountPoints[destination] = &mountPoint{
-		Name:        vol.Name(),
-		Driver:      vol.DriverName(),
-		Destination: destination,
-		RW:          rw,
-		Volume:      vol,
-	}
-}
-
-func (container *Container) isDestinationMounted(destination string) bool {
-	return container.MountPoints[destination] != nil
-}
-
-func (container *Container) prepareMountPoints() error {
-	for _, config := range container.MountPoints {
-		if len(config.Driver) > 0 {
-			v, err := container.daemon.createVolume(config.Name, config.Driver, nil)
-			if err != nil {
-				return err
-			}
-			config.Volume = v
+	if _, err = ioutil.ReadDir(rootfs); err != nil {
+		if os.IsNotExist(err) {
+			return nil
 		}
+		return err
 	}
-	return nil
-}
 
-func (container *Container) removeMountPoints(rm bool) error {
-	var rmErrors []string
-	for _, m := range container.MountPoints {
-		if m.Volume == nil {
-			continue
-		}
-		container.daemon.volumes.Decrement(m.Volume)
-		if rm {
-			err := container.daemon.volumes.Remove(m.Volume)
-			// ErrVolumeInUse is ignored because having this
-			// volume being referenced by othe container is
-			// not an error, but an implementation detail.
-			// This prevents docker from logging "ERROR: Volume in use"
-			// where there is another container using the volume.
-			if err != nil && err != store.ErrVolumeInUse {
-				rmErrors = append(rmErrors, err.Error())
-			}
-		}
+	path, err := v.Mount()
+	if err != nil {
+		return err
 	}
-	if len(rmErrors) > 0 {
-		return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n"))
+
+	if err := copyExistingContents(rootfs, path); err != nil {
+		return err
 	}
-	return nil
+
+	return v.Unmount()
 }
 
 func (container *Container) shmPath() (string, error) {
diff --git a/daemon/container_windows.go b/daemon/container_windows.go
index f0d0c06ea1..178521087a 100644
--- a/daemon/container_windows.go
+++ b/daemon/container_windows.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/daemon/execdriver"
 	derr "github.com/docker/docker/errors"
+	"github.com/docker/docker/volume"
 	"github.com/docker/libnetwork"
 )
 
@@ -169,18 +170,11 @@ func (container *Container) updateNetwork() error {
 func (container *Container) releaseNetwork() {
 }
 
-func (container *Container) unmountVolumes(forceSyscall bool) error {
-	return nil
-}
-
-// prepareMountPoints is a no-op on Windows
-func (container *Container) prepareMountPoints() error {
-	return nil
-}
-
-// removeMountPoints is a no-op on Windows.
-func (container *Container) removeMountPoints(_ bool) error {
-	return nil
+// appendNetworkMounts appends any network mounts to the array of mount points passed in.
+// Windows does not support network mounts (not to be confused with SMB network mounts), so
+// this is a no-op.
+func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
+	return volumeMounts, nil
 }
 
 func (container *Container) setupIpcDirs() error {
diff --git a/daemon/create.go b/daemon/create.go
index 83a1a8b7e4..61c60cfc93 100644
--- a/daemon/create.go
+++ b/daemon/create.go
@@ -15,26 +15,34 @@ import (
 	"github.com/opencontainers/runc/libcontainer/label"
 )
 
+// ContainerCreateConfig is the parameter set to ContainerCreate()
+type ContainerCreateConfig struct {
+	Name            string
+	Config          *runconfig.Config
+	HostConfig      *runconfig.HostConfig
+	AdjustCPUShares bool
+}
+
 // ContainerCreate takes configs and creates a container.
-func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hostConfig *runconfig.HostConfig, adjustCPUShares bool) (types.ContainerCreateResponse, error) {
-	if config == nil {
+func (daemon *Daemon) ContainerCreate(params *ContainerCreateConfig) (types.ContainerCreateResponse, error) {
+	if params.Config == nil {
 		return types.ContainerCreateResponse{}, derr.ErrorCodeEmptyConfig
 	}
 
-	warnings, err := daemon.verifyContainerSettings(hostConfig, config)
+	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config)
 	if err != nil {
 		return types.ContainerCreateResponse{"", warnings}, err
 	}
 
-	daemon.adaptContainerSettings(hostConfig, adjustCPUShares)
+	daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares)
 
-	container, err := daemon.Create(config, hostConfig, name)
+	container, err := daemon.create(params)
 	if err != nil {
-		if daemon.Graph().IsNotExist(err, config.Image) {
-			if strings.Contains(config.Image, "@") {
-				return types.ContainerCreateResponse{"", warnings}, derr.ErrorCodeNoSuchImageHash.WithArgs(config.Image)
+		if daemon.Graph().IsNotExist(err, params.Config.Image) {
+			if strings.Contains(params.Config.Image, "@") {
+				return types.ContainerCreateResponse{"", warnings}, derr.ErrorCodeNoSuchImageHash.WithArgs(params.Config.Image)
 			}
-			img, tag := parsers.ParseRepositoryTag(config.Image)
+			img, tag := parsers.ParseRepositoryTag(params.Config.Image)
 			if tag == "" {
 				tag = tags.DefaultTag
 			}
@@ -47,7 +55,7 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos
 }
 
 // Create creates a new container from the given configuration with a given name.
-func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.HostConfig, name string) (retC *Container, retErr error) {
+func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, retErr error) {
 	var (
 		container *Container
 		img       *image.Image
@@ -55,8 +63,8 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 		err       error
 	)
 
-	if config.Image != "" {
-		img, err = daemon.repositories.LookupImage(config.Image)
+	if params.Config.Image != "" {
+		img, err = daemon.repositories.LookupImage(params.Config.Image)
 		if err != nil {
 			return nil, err
 		}
@@ -66,20 +74,20 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 		imgID = img.ID
 	}
 
-	if err := daemon.mergeAndVerifyConfig(config, img); err != nil {
+	if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil {
 		return nil, err
 	}
 
-	if hostConfig == nil {
-		hostConfig = &runconfig.HostConfig{}
+	if params.HostConfig == nil {
+		params.HostConfig = &runconfig.HostConfig{}
 	}
-	if hostConfig.SecurityOpt == nil {
-		hostConfig.SecurityOpt, err = daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode)
+	if params.HostConfig.SecurityOpt == nil {
+		params.HostConfig.SecurityOpt, err = daemon.generateSecurityOpt(params.HostConfig.IpcMode, params.HostConfig.PidMode)
 		if err != nil {
 			return nil, err
 		}
 	}
-	if container, err = daemon.newContainer(name, config, imgID); err != nil {
+	if container, err = daemon.newContainer(params.Name, params.Config, imgID); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -96,7 +104,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 	if err := daemon.createRootfs(container); err != nil {
 		return nil, err
 	}
-	if err := daemon.setHostConfig(container, hostConfig); err != nil {
+	if err := daemon.setHostConfig(container, params.HostConfig); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -111,7 +119,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 	}
 	defer container.Unmount()
 
-	if err := createContainerPlatformSpecificSettings(container, config, hostConfig, img); err != nil {
+	if err := createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig, img); err != nil {
 		return nil, err
 	}
 
diff --git a/daemon/create_unix.go b/daemon/create_unix.go
index 66138db967..85fb1cdbee 100644
--- a/daemon/create_unix.go
+++ b/daemon/create_unix.go
@@ -16,9 +16,11 @@ import (
 
 // createContainerPlatformSpecificSettings performs platform specific container create functionality
 func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
+	var name, destination string
+
 	for spec := range config.Volumes {
-		name := stringid.GenerateNonCryptoID()
-		destination := filepath.Clean(spec)
+		name = stringid.GenerateNonCryptoID()
+		destination = filepath.Clean(spec)
 
 		// Skip volumes for which we already have something mounted on that
 		// destination because of a --volume-from.
diff --git a/daemon/create_windows.go b/daemon/create_windows.go
index 21aac13d31..1ea465f71c 100644
--- a/daemon/create_windows.go
+++ b/daemon/create_windows.go
@@ -1,11 +1,83 @@
 package daemon
 
 import (
+	"fmt"
+
 	"github.com/docker/docker/image"
+	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
+	"github.com/docker/docker/volume"
 )
 
 // createContainerPlatformSpecificSettings performs platform specific container create functionality
 func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
+	for spec := range config.Volumes {
+
+		mp, err := volume.ParseMountSpec(spec, hostConfig.VolumeDriver)
+		if err != nil {
+			return fmt.Errorf("Unrecognised volume spec: %v", err)
+		}
+
+		// If the mountpoint doesn't have a name, generate one.
+		if len(mp.Name) == 0 {
+			mp.Name = stringid.GenerateNonCryptoID()
+		}
+
+		// Skip volumes for which we already have something mounted on that
+		// destination because of a --volume-from.
+		if container.isDestinationMounted(mp.Destination) {
+			continue
+		}
+
+		volumeDriver := hostConfig.VolumeDriver
+		if mp.Destination != "" && img != nil {
+			if _, ok := img.ContainerConfig.Volumes[mp.Destination]; ok {
+				// check for whether bind is not specified and then set to local
+				if _, ok := container.MountPoints[mp.Destination]; !ok {
+					volumeDriver = volume.DefaultDriverName
+				}
+			}
+		}
+
+		// Create the volume in the volume driver. If it doesn't exist,
+		// a new one will be created.
+		v, err := container.daemon.createVolume(mp.Name, volumeDriver, nil)
+		if err != nil {
+			return err
+		}
+
+		// FIXME Windows: This code block is present in the Linux version and
+		// allows the contents to be copied to the container FS prior to it
+		// being started. However, the function utilises the FollowSymLinkInScope
+		// path which does not cope with Windows volume-style file paths. There
+		// is a seperate effort to resolve this (@swernli), so this processing
+		// is deferred for now. A case where this would be useful is when
+		// a dockerfile includes a VOLUME statement, but something is created
+		// in that directory during the dockerfile processing. What this means
+		// on Windows for TP4 is that in that scenario, the contents will not
+		// copied, but that's (somewhat) OK as HCS will bomb out soon after
+		// at it doesn't support mapped directories which have contents in the
+		// destination path anyway.
+		//
+		// Example for repro later:
+		//   FROM windowsservercore
+		//   RUN mkdir c:\myvol
+		//   RUN copy c:\windows\system32\ntdll.dll c:\myvol
+		//   VOLUME "c:\myvol"
+		//
+		// Then
+		//   docker build -t vol .
+		//   docker run -it --rm vol cmd  <-- This is where HCS will error out.
+		//
+		//	// never attempt to copy existing content in a container FS to a shared volume
+		//	if v.DriverName() == volume.DefaultDriverName {
+		//		if err := container.copyImagePathContent(v, mp.Destination); err != nil {
+		//			return err
+		//		}
+		//	}
+
+		// Add it to container.MountPoints
+		container.addMountPointWithVolume(mp.Destination, v, mp.RW)
+	}
 	return nil
 }
diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go
index 38de83cef8..4291edee45 100644
--- a/daemon/daemon_unix.go
+++ b/daemon/daemon_unix.go
@@ -22,6 +22,7 @@ import (
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
+	"github.com/docker/docker/volume"
 	"github.com/docker/libnetwork"
 	nwconfig "github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/drivers/bridge"
@@ -603,10 +604,10 @@ func (daemon *Daemon) newBaseContainer(id string) Container {
 			State:        NewState(),
 			execCommands: newExecStore(),
 			root:         daemon.containerRoot(id),
+			MountPoints:  make(map[string]*volume.MountPoint),
 		},
-		MountPoints: make(map[string]*mountPoint),
-		Volumes:     make(map[string]string),
-		VolumesRW:   make(map[string]bool),
+		Volumes:   make(map[string]string),
+		VolumesRW: make(map[string]bool),
 	}
 }
 
diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go
index 6786401631..fab97face2 100644
--- a/daemon/daemonbuilder/builder.go
+++ b/daemon/daemonbuilder/builder.go
@@ -83,7 +83,12 @@ func (d Docker) Container(id string) (*daemon.Container, error) {
 
 // Create creates a new Docker container and returns potential warnings
 func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*daemon.Container, []string, error) {
-	ccr, err := d.Daemon.ContainerCreate("", cfg, hostCfg, true)
+	ccr, err := d.Daemon.ContainerCreate(&daemon.ContainerCreateConfig{
+		Name:            "",
+		Config:          cfg,
+		HostConfig:      hostCfg,
+		AdjustCPUShares: true,
+	})
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go
index e88ea0bde7..462666b392 100644
--- a/daemon/execdriver/driver.go
+++ b/daemon/execdriver/driver.go
@@ -165,16 +165,8 @@ type ResourceStats struct {
 	SystemUsage uint64    `json:"system_usage"`
 }
 
-// Mount contains information for a mount operation.
-type Mount struct {
-	Source      string `json:"source"`
-	Destination string `json:"destination"`
-	Writable    bool   `json:"writable"`
-	Private     bool   `json:"private"`
-	Slave       bool   `json:"slave"`
-}
-
 // User contains the uid and gid representing a Unix user
+// TODO Windows: Factor out User
 type User struct {
 	UID int `json:"root_uid"`
 	GID int `json:"root_gid"`
diff --git a/daemon/execdriver/driver_unix.go b/daemon/execdriver/driver_unix.go
index 3baa3e0965..2ebf95fc84 100644
--- a/daemon/execdriver/driver_unix.go
+++ b/daemon/execdriver/driver_unix.go
@@ -18,6 +18,15 @@ import (
 	"github.com/opencontainers/runc/libcontainer/configs"
 )
 
+// Mount contains information for a mount operation.
+type Mount struct {
+	Source      string `json:"source"`
+	Destination string `json:"destination"`
+	Writable    bool   `json:"writable"`
+	Private     bool   `json:"private"`
+	Slave       bool   `json:"slave"`
+}
+
 // Network settings of the container
 type Network struct {
 	Mtu            int    `json:"mtu"`
diff --git a/daemon/execdriver/driver_windows.go b/daemon/execdriver/driver_windows.go
index 08568bc242..b422bcd79a 100644
--- a/daemon/execdriver/driver_windows.go
+++ b/daemon/execdriver/driver_windows.go
@@ -2,6 +2,13 @@ package execdriver
 
 import "github.com/docker/docker/pkg/nat"
 
+// Mount contains information for a mount operation.
+type Mount struct {
+	Source      string `json:"source"`
+	Destination string `json:"destination"`
+	Writable    bool   `json:"writable"`
+}
+
 // Network settings of the container
 type Network struct {
 	Interface   *NetworkInterface `json:"interface"`
diff --git a/daemon/execdriver/windows/run.go b/daemon/execdriver/windows/run.go
index 98d8d1c921..3b86109b2c 100644
--- a/daemon/execdriver/windows/run.go
+++ b/daemon/execdriver/windows/run.go
@@ -2,8 +2,6 @@
 
 package windows
 
-// Note this is alpha code for the bring up of containers on Windows.
-
 import (
 	"encoding/json"
 	"errors"
@@ -60,18 +58,25 @@ type device struct {
 	Settings   interface{}
 }
 
+type mappedDir struct {
+	HostPath      string
+	ContainerPath string
+	ReadOnly      bool
+}
+
 type containerInit struct {
-	SystemType              string   // HCS requires this to be hard-coded to "Container"
-	Name                    string   // Name of the container. We use the docker ID.
-	Owner                   string   // The management platform that created this container
-	IsDummy                 bool     // Used for development purposes.
-	VolumePath              string   // Windows volume path for scratch space
-	Devices                 []device // Devices used by the container
-	IgnoreFlushesDuringBoot bool     // Optimisation hint for container startup in Windows
-	LayerFolderPath         string   // Where the layer folders are located
-	Layers                  []layer  // List of storage layers
-	ProcessorWeight         int64    // CPU Shares 1..9 on Windows; or 0 is platform default.
-	HostName                string   // Hostname
+	SystemType              string      // HCS requires this to be hard-coded to "Container"
+	Name                    string      // Name of the container. We use the docker ID.
+	Owner                   string      // The management platform that created this container
+	IsDummy                 bool        // Used for development purposes.
+	VolumePath              string      // Windows volume path for scratch space
+	Devices                 []device    // Devices used by the container
+	IgnoreFlushesDuringBoot bool        // Optimisation hint for container startup in Windows
+	LayerFolderPath         string      // Where the layer folders are located
+	Layers                  []layer     // List of storage layers
+	ProcessorWeight         int64       // CPU Shares 1..9 on Windows; or 0 is platform default.
+	HostName                string      // Hostname
+	MappedDirectories       []mappedDir // List of mapped directories (volumes/mounts)
 }
 
 // defaultOwner is a tag passed to HCS to allow it to differentiate between
@@ -105,18 +110,28 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
 		HostName:                c.Hostname,
 	}
 
-	for i := 0; i < len(c.LayerPaths); i++ {
-		_, filename := filepath.Split(c.LayerPaths[i])
+	for _, layerPath := range c.LayerPaths {
+		_, filename := filepath.Split(layerPath)
 		g, err := hcsshim.NameToGuid(filename)
 		if err != nil {
 			return execdriver.ExitStatus{ExitCode: -1}, err
 		}
 		cu.Layers = append(cu.Layers, layer{
 			ID:   g.ToString(),
-			Path: c.LayerPaths[i],
+			Path: layerPath,
 		})
 	}
 
+	// Add the mounts (volumes, bind mounts etc) to the structure
+	mds := make([]mappedDir, len(c.Mounts))
+	for i, mount := range c.Mounts {
+		mds[i] = mappedDir{
+			HostPath:      mount.Source,
+			ContainerPath: mount.Destination,
+			ReadOnly:      !mount.Writable}
+	}
+	cu.MappedDirectories = mds
+
 	// TODO Windows. At some point, when there is CLI on docker run to
 	// enable the IP Address of the container to be passed into docker run,
 	// the IP Address needs to be wired through to HCS in the JSON. It
diff --git a/daemon/inspect_windows.go b/daemon/inspect_windows.go
index 654d78c35e..26b386160d 100644
--- a/daemon/inspect_windows.go
+++ b/daemon/inspect_windows.go
@@ -8,7 +8,17 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
 }
 
 func addMountPoints(container *Container) []types.MountPoint {
-	return nil
+	mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
+	for _, m := range container.MountPoints {
+		mountPoints = append(mountPoints, types.MountPoint{
+			Name:        m.Name,
+			Source:      m.Path(),
+			Destination: m.Destination,
+			Driver:      m.Driver,
+			RW:          m.RW,
+		})
+	}
+	return mountPoints
 }
 
 // ContainerInspectPre120 get containers for pre 1.20 APIs.
diff --git a/daemon/volumes.go b/daemon/volumes.go
index 54aca3522b..cca889830f 100644
--- a/daemon/volumes.go
+++ b/daemon/volumes.go
@@ -2,18 +2,16 @@ package daemon
 
 import (
 	"errors"
-	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/daemon/execdriver"
 	derr "github.com/docker/docker/errors"
-	"github.com/docker/docker/pkg/chrootarchive"
-	"github.com/docker/docker/pkg/system"
+	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
+	"github.com/opencontainers/runc/libcontainer/label"
 )
 
 var (
@@ -22,82 +20,7 @@ var (
 	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
 )
 
-// mountPoint is the intersection point between a volume and a container. It
-// specifies which volume is to be used and where inside a container it should
-// be mounted.
-type mountPoint struct {
-	Name        string
-	Destination string
-	Driver      string
-	RW          bool
-	Volume      volume.Volume `json:"-"`
-	Source      string
-	Mode        string `json:"Relabel"` // Originally field was `Relabel`"
-}
-
-// Setup sets up a mount point by either mounting the volume if it is
-// configured, or creating the source directory if supplied.
-func (m *mountPoint) Setup() (string, error) {
-	if m.Volume != nil {
-		return m.Volume.Mount()
-	}
-
-	if len(m.Source) > 0 {
-		if _, err := os.Stat(m.Source); err != nil {
-			if !os.IsNotExist(err) {
-				return "", err
-			}
-			logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source)
-			if err := system.MkdirAll(m.Source, 0755); err != nil {
-				return "", err
-			}
-		}
-		return m.Source, nil
-	}
-
-	return "", derr.ErrorCodeMountSetup
-}
-
-// hasResource checks whether the given absolute path for a container is in
-// this mount point. If the relative path starts with `../` then the resource
-// is outside of this mount point, but we can't simply check for this prefix
-// because it misses `..` which is also outside of the mount, so check both.
-func (m *mountPoint) hasResource(absolutePath string) bool {
-	relPath, err := filepath.Rel(m.Destination, absolutePath)
-
-	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
-}
-
-// Path returns the path of a volume in a mount point.
-func (m *mountPoint) Path() string {
-	if m.Volume != nil {
-		return m.Volume.Path()
-	}
-
-	return m.Source
-}
-
-// copyExistingContents copies from the source to the destination and
-// ensures the ownership is appropriately set.
-func copyExistingContents(source, destination string) error {
-	volList, err := ioutil.ReadDir(source)
-	if err != nil {
-		return err
-	}
-	if len(volList) > 0 {
-		srcList, err := ioutil.ReadDir(destination)
-		if err != nil {
-			return err
-		}
-		if len(srcList) == 0 {
-			// If the source volume is empty copy files from the root into the volume
-			if err := chrootarchive.CopyWithTar(source, destination); err != nil {
-				return err
-			}
-		}
-	}
-	return copyOwnership(source, destination)
-}
+type mounts []execdriver.Mount
 
 // volumeToAPIType converts a volume.Volume to the type used by the remote API
 func volumeToAPIType(v volume.Volume) *types.Volume {
@@ -107,3 +30,126 @@ func volumeToAPIType(v volume.Volume) *types.Volume {
 		Mountpoint: v.Path(),
 	}
 }
+
+// createVolume creates a volume.
+func (daemon *Daemon) createVolume(name, driverName string, opts map[string]string) (volume.Volume, error) {
+	v, err := daemon.volumes.Create(name, driverName, opts)
+	if err != nil {
+		return nil, err
+	}
+	daemon.volumes.Increment(v)
+	return v, nil
+}
+
+// Len returns the number of mounts. Used in sorting.
+func (m mounts) Len() int {
+	return len(m)
+}
+
+// Less returns true if the number of parts (a/b/c would be 3 parts) in the
+// mount indexed by parameter 1 is less than that of the mount indexed by
+// parameter 2. Used in sorting.
+func (m mounts) Less(i, j int) bool {
+	return m.parts(i) < m.parts(j)
+}
+
+// Swap swaps two items in an array of mounts. Used in sorting
+func (m mounts) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
+// parts returns the number of parts in the destination of a mount. Used in sorting.
+func (m mounts) parts(i int) int {
+	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
+}
+
+// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
+// It follows the next sequence to decide what to mount in each final destination:
+//
+// 1. Select the previously configured mount points for the containers, if any.
+// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
+// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
+func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
+	binds := map[string]bool{}
+	mountPoints := map[string]*volume.MountPoint{}
+
+	// 1. Read already configured mount points.
+	for name, point := range container.MountPoints {
+		mountPoints[name] = point
+	}
+
+	// 2. Read volumes from other containers.
+	for _, v := range hostConfig.VolumesFrom {
+		containerID, mode, err := volume.ParseVolumesFrom(v)
+		if err != nil {
+			return err
+		}
+
+		c, err := daemon.Get(containerID)
+		if err != nil {
+			return err
+		}
+
+		for _, m := range c.MountPoints {
+			cp := &volume.MountPoint{
+				Name:        m.Name,
+				Source:      m.Source,
+				RW:          m.RW && volume.ReadWrite(mode),
+				Driver:      m.Driver,
+				Destination: m.Destination,
+			}
+
+			if len(cp.Source) == 0 {
+				v, err := daemon.createVolume(cp.Name, cp.Driver, nil)
+				if err != nil {
+					return err
+				}
+				cp.Volume = v
+			}
+
+			mountPoints[cp.Destination] = cp
+		}
+	}
+
+	// 3. Read bind mounts
+	for _, b := range hostConfig.Binds {
+		// #10618
+		bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver)
+		if err != nil {
+			return err
+		}
+
+		if binds[bind.Destination] {
+			return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination)
+		}
+
+		if len(bind.Name) > 0 && len(bind.Driver) > 0 {
+			// create the volume
+			v, err := daemon.createVolume(bind.Name, bind.Driver, nil)
+			if err != nil {
+				return err
+			}
+			bind.Volume = v
+			bind.Source = v.Path()
+			// bind.Name is an already existing volume, we need to use that here
+			bind.Driver = v.DriverName()
+			bind = setBindModeIfNull(bind)
+		}
+		shared := label.IsShared(bind.Mode)
+		if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil {
+			return err
+		}
+		binds[bind.Destination] = true
+		mountPoints[bind.Destination] = bind
+	}
+
+	bcVolumes, bcVolumesRW := configureBackCompatStructures(daemon, container, mountPoints)
+
+	container.Lock()
+	container.MountPoints = mountPoints
+	setBackCompatStructures(container, bcVolumes, bcVolumesRW)
+
+	container.Unlock()
+
+	return nil
+}
diff --git a/daemon/volumes_linux_unit_test.go b/daemon/volumes_linux_unit_test.go
deleted file mode 100644
index 6f4ab882dc..0000000000
--- a/daemon/volumes_linux_unit_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// +build experimental
-
-package daemon
-
-import "testing"
-
-func TestParseBindMount(t *testing.T) {
-	cases := []struct {
-		bind      string
-		driver    string
-		expDest   string
-		expSource string
-		expName   string
-		expDriver string
-		expRW     bool
-		fail      bool
-	}{
-		{"/tmp:/tmp", "", "/tmp", "/tmp", "", "", true, false},
-		{"/tmp:/tmp:ro", "", "/tmp", "/tmp", "", "", false, false},
-		{"/tmp:/tmp:rw", "", "/tmp", "/tmp", "", "", true, false},
-		{"/tmp:/tmp:foo", "", "/tmp", "/tmp", "", "", false, true},
-		{"name:/tmp", "", "/tmp", "", "name", "local", true, false},
-		{"name:/tmp", "external", "/tmp", "", "name", "external", true, false},
-		{"name:/tmp:ro", "local", "/tmp", "", "name", "local", false, false},
-		{"local/name:/tmp:rw", "", "/tmp", "", "local/name", "local", true, false},
-		{"/tmp:tmp", "", "", "", "", "", true, true},
-	}
-
-	for _, c := range cases {
-		m, err := parseBindMount(c.bind, c.driver)
-		if c.fail {
-			if err == nil {
-				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
-			}
-			continue
-		}
-
-		if m.Destination != c.expDest {
-			t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind)
-		}
-
-		if m.Source != c.expSource {
-			t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind)
-		}
-
-		if m.Name != c.expName {
-			t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind)
-		}
-
-		if m.Driver != c.expDriver {
-			t.Fatalf("Expected driver %s, was %s, for spec %s\n", c.expDriver, m.Driver, c.bind)
-		}
-
-		if m.RW != c.expRW {
-			t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind)
-		}
-	}
-}
diff --git a/daemon/volumes_unit_test.go b/daemon/volumes_unit_test.go
index 9420989b1c..dadf24e144 100644
--- a/daemon/volumes_unit_test.go
+++ b/daemon/volumes_unit_test.go
@@ -1,6 +1,9 @@
 package daemon
 
-import "testing"
+import (
+	"github.com/docker/docker/volume"
+	"testing"
+)
 
 func TestParseVolumesFrom(t *testing.T) {
 	cases := []struct {
@@ -17,7 +20,7 @@ func TestParseVolumesFrom(t *testing.T) {
 	}
 
 	for _, c := range cases {
-		id, mode, err := parseVolumesFrom(c.spec)
+		id, mode, err := volume.ParseVolumesFrom(c.spec)
 		if c.fail {
 			if err == nil {
 				t.Fatalf("Expected error, was nil, for spec %s\n", c.spec)
diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go
index 3b32c65f9a..b2a3c27b67 100644
--- a/daemon/volumes_unix.go
+++ b/daemon/volumes_unix.go
@@ -11,15 +11,35 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/execdriver"
-	derr "github.com/docker/docker/errors"
+	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/system"
-	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
 	volumedrivers "github.com/docker/docker/volume/drivers"
 	"github.com/docker/docker/volume/local"
-	"github.com/opencontainers/runc/libcontainer/label"
 )
 
+// copyExistingContents copies from the source to the destination and
+// ensures the ownership is appropriately set.
+func copyExistingContents(source, destination string) error {
+	volList, err := ioutil.ReadDir(source)
+	if err != nil {
+		return err
+	}
+	if len(volList) > 0 {
+		srcList, err := ioutil.ReadDir(destination)
+		if err != nil {
+			return err
+		}
+		if len(srcList) == 0 {
+			// If the source volume is empty copy files from the root into the volume
+			if err := chrootarchive.CopyWithTar(source, destination); err != nil {
+				return err
+			}
+		}
+	}
+	return copyOwnership(source, destination)
+}
+
 // copyOwnership copies the permissions and uid:gid of the source file
 // to the destination file
 func copyOwnership(source, destination string) error {
@@ -68,53 +88,6 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
 	return append(mounts, netMounts...), nil
 }
 
-// parseBindMount validates the configuration of mount information in runconfig is valid.
-func parseBindMount(spec, volumeDriver string) (*mountPoint, error) {
-	bind := &mountPoint{
-		RW: true,
-	}
-	arr := strings.Split(spec, ":")
-
-	switch len(arr) {
-	case 2:
-		bind.Destination = arr[1]
-	case 3:
-		bind.Destination = arr[1]
-		mode := arr[2]
-		if !volume.ValidMountMode(mode) {
-			return nil, derr.ErrorCodeVolumeInvalidMode.WithArgs(mode)
-		}
-		bind.RW = volume.ReadWrite(mode)
-		// Mode field is used by SELinux to decide whether to apply label
-		bind.Mode = mode
-	default:
-		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
-	}
-
-	//validate the volumes destination path
-	if !filepath.IsAbs(bind.Destination) {
-		return nil, derr.ErrorCodeVolumeAbs.WithArgs(bind.Destination)
-	}
-
-	name, source, err := parseVolumeSource(arr[0])
-	if err != nil {
-		return nil, err
-	}
-
-	if len(source) == 0 {
-		bind.Driver = volumeDriver
-		if len(bind.Driver) == 0 {
-			bind.Driver = volume.DefaultDriverName
-		}
-	} else {
-		bind.Source = filepath.Clean(source)
-	}
-
-	bind.Name = name
-	bind.Destination = filepath.Clean(bind.Destination)
-	return bind, nil
-}
-
 // sortMounts sorts an array of mounts in lexicographic order. This ensure that
 // when mounting, the mounts don't shadow other mounts. For example, if mounting
 // /etc and /etc/resolv.conf, /etc/resolv.conf must not be mounted first.
@@ -123,30 +96,6 @@ func sortMounts(m []execdriver.Mount) []execdriver.Mount {
 	return m
 }
 
-type mounts []execdriver.Mount
-
-// Len returns the number of mounts
-func (m mounts) Len() int {
-	return len(m)
-}
-
-// Less returns true if the number of parts (a/b/c would be 3 parts) in the
-// mount indexed by parameter 1 is less than that of the mount indexed by
-// parameter 2.
-func (m mounts) Less(i, j int) bool {
-	return m.parts(i) < m.parts(j)
-}
-
-// Swap swaps two items in an array of mounts.
-func (m mounts) Swap(i, j int) {
-	m[i], m[j] = m[j], m[i]
-}
-
-// parts returns the number of parts in the destination of a mount.
-func (m mounts) parts(i int) int {
-	return len(strings.Split(filepath.Clean(m[i].Destination), string(os.PathSeparator)))
-}
-
 // migrateVolume links the contents of a volume created pre Docker 1.7
 // into the location expected by the local driver.
 // It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
@@ -211,12 +160,7 @@ func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
 				}
 				container.addLocalMountPoint(id, destination, rw)
 			} else { // Bind mount
-				id, source, err := parseVolumeSource(hostPath)
-				// We should not find an error here coming
-				// from the old configuration, but who knows.
-				if err != nil {
-					return err
-				}
+				id, source := volume.ParseVolumeSource(hostPath)
 				container.addBindMountPoint(id, source, destination, rw)
 			}
 		}
@@ -270,109 +214,19 @@ func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
 	return nil
 }
 
-// parseVolumesFrom ensure that the supplied volumes-from is valid.
-func parseVolumesFrom(spec string) (string, string, error) {
-	if len(spec) == 0 {
-		return "", "", derr.ErrorCodeVolumeFromBlank.WithArgs(spec)
+// setBindModeIfNull is platform specific processing to ensure the
+// shared mode is set to 'z' if it is null. This is called in the case
+// of processing a named volume and not a typical bind.
+func setBindModeIfNull(bind *volume.MountPoint) *volume.MountPoint {
+	if bind.Mode == "" {
+		bind.Mode = "z"
 	}
-
-	specParts := strings.SplitN(spec, ":", 2)
-	id := specParts[0]
-	mode := "rw"
-
-	if len(specParts) == 2 {
-		mode = specParts[1]
-		if !volume.ValidMountMode(mode) {
-			return "", "", derr.ErrorCodeVolumeMode.WithArgs(mode)
-		}
-	}
-	return id, mode, nil
+	return bind
 }
 
-// registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
-// It follows the next sequence to decide what to mount in each final destination:
-//
-// 1. Select the previously configured mount points for the containers, if any.
-// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
-// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
-func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
-	binds := map[string]bool{}
-	mountPoints := map[string]*mountPoint{}
-
-	// 1. Read already configured mount points.
-	for name, point := range container.MountPoints {
-		mountPoints[name] = point
-	}
-
-	// 2. Read volumes from other containers.
-	for _, v := range hostConfig.VolumesFrom {
-		containerID, mode, err := parseVolumesFrom(v)
-		if err != nil {
-			return err
-		}
-
-		c, err := daemon.Get(containerID)
-		if err != nil {
-			return err
-		}
-
-		for _, m := range c.MountPoints {
-			cp := &mountPoint{
-				Name:        m.Name,
-				Source:      m.Source,
-				RW:          m.RW && volume.ReadWrite(mode),
-				Driver:      m.Driver,
-				Destination: m.Destination,
-			}
-
-			if len(cp.Source) == 0 {
-				v, err := daemon.createVolume(cp.Name, cp.Driver, nil)
-				if err != nil {
-					return err
-				}
-				cp.Volume = v
-			}
-
-			mountPoints[cp.Destination] = cp
-		}
-	}
-
-	// 3. Read bind mounts
-	for _, b := range hostConfig.Binds {
-		// #10618
-		bind, err := parseBindMount(b, hostConfig.VolumeDriver)
-		if err != nil {
-			return err
-		}
-
-		if binds[bind.Destination] {
-			return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination)
-		}
-
-		if len(bind.Name) > 0 && len(bind.Driver) > 0 {
-			// create the volume
-			v, err := daemon.createVolume(bind.Name, bind.Driver, nil)
-			if err != nil {
-				return err
-			}
-			bind.Volume = v
-			bind.Source = v.Path()
-			// bind.Name is an already existing volume, we need to use that here
-			bind.Driver = v.DriverName()
-			// Since this is just a named volume and not a typical bind, set to shared mode `z`
-			if bind.Mode == "" {
-				bind.Mode = "z"
-			}
-		}
-
-		shared := label.IsShared(bind.Mode)
-		if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil {
-			return err
-		}
-		binds[bind.Destination] = true
-		mountPoints[bind.Destination] = bind
-	}
-
+// configureBackCompatStructures is platform specific processing for
+// registering mount points to populate old structures.
+func configureBackCompatStructures(daemon *Daemon, container *Container, mountPoints map[string]*volume.MountPoint) (map[string]string, map[string]bool) {
 	// Keep backwards compatible structures
 	bcVolumes := map[string]string{}
 	bcVolumesRW := map[string]bool{}
@@ -387,38 +241,12 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
 			}
 		}
 	}
+	return bcVolumes, bcVolumesRW
+}
 
-	container.Lock()
-	container.MountPoints = mountPoints
+// setBackCompatStructures is a platform specific helper function to set
+// backwards compatible structures in the container when registering volumes.
+func setBackCompatStructures(container *Container, bcVolumes map[string]string, bcVolumesRW map[string]bool) {
 	container.Volumes = bcVolumes
 	container.VolumesRW = bcVolumesRW
-	container.Unlock()
-
-	return nil
-}
-
-// createVolume creates a volume.
-func (daemon *Daemon) createVolume(name, driverName string, opts map[string]string) (volume.Volume, error) {
-	v, err := daemon.volumes.Create(name, driverName, opts)
-	if err != nil {
-		return nil, err
-	}
-	daemon.volumes.Increment(v)
-	return v, nil
-}
-
-// parseVolumeSource parses the origin sources that's mounted into the container.
-func parseVolumeSource(spec string) (string, string, error) {
-	if !filepath.IsAbs(spec) {
-		return spec, "", nil
-	}
-
-	return "", spec, nil
-}
-
-// BackwardsCompatible decides whether this mount point can be
-// used in old versions of Docker or not.
-// Only bind mounts and local volumes can be used in old versions of Docker.
-func (m *mountPoint) BackwardsCompatible() bool {
-	return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
 }
diff --git a/daemon/volumes_windows.go b/daemon/volumes_windows.go
index df122ecbac..5c6182da53 100644
--- a/daemon/volumes_windows.go
+++ b/daemon/volumes_windows.go
@@ -4,22 +4,35 @@ package daemon
 
 import (
 	"github.com/docker/docker/daemon/execdriver"
-	"github.com/docker/docker/runconfig"
+	derr "github.com/docker/docker/errors"
+	"github.com/docker/docker/volume"
+	"sort"
 )
 
-// copyOwnership copies the permissions and group of a source file to the
-// destination file. This is a no-op on Windows.
-func copyOwnership(source, destination string) error {
-	return nil
-}
-
-// setupMounts configures the mount points for a container.
-// setupMounts on Linux iterates through each of the mount points for a
-// container and calls Setup() on each. It also looks to see if is a network
-// mount such as /etc/resolv.conf, and if it is not, appends it to the array
-// of mounts. As Windows does not support mount points, this is a no-op.
+// setupMounts configures the mount points for a container by appending each
+// of the configured mounts on the container to the execdriver mount structure
+// which will ultimately be passed into the exec driver during container creation.
+// It also ensures each of the mounts are lexographically sorted.
 func (container *Container) setupMounts() ([]execdriver.Mount, error) {
-	return nil, nil
+	var mnts []execdriver.Mount
+	for _, mount := range container.MountPoints { // type is volume.MountPoint
+		// If there is no source, take it from the volume path
+		s := mount.Source
+		if s == "" && mount.Volume != nil {
+			s = mount.Volume.Path()
+		}
+		if s == "" {
+			return nil, derr.ErrorCodeVolumeNoSourceForMount.WithArgs(mount.Name, mount.Driver, mount.Destination)
+		}
+		mnts = append(mnts, execdriver.Mount{
+			Source:      s,
+			Destination: mount.Destination,
+			Writable:    mount.RW,
+		})
+	}
+
+	sort.Sort(mounts(mnts))
+	return mnts, nil
 }
 
 // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
@@ -28,9 +41,20 @@ func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
 	return nil
 }
 
-// registerMountPoints initializes the container mount points with the
-// configured volumes and bind mounts. Windows does not support volumes or
-// mount points.
-func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
-	return nil
+// setBindModeIfNull is platform specific processing which is a no-op on
+// Windows.
+func setBindModeIfNull(bind *volume.MountPoint) *volume.MountPoint {
+	return bind
+}
+
+// configureBackCompatStructures is platform specific processing for
+// registering mount points to populate old structures. This is a no-op on Windows.
+func configureBackCompatStructures(*Daemon, *Container, map[string]*volume.MountPoint) (map[string]string, map[string]bool) {
+	return nil, nil
+}
+
+// setBackCompatStructures is a platform specific helper function to set
+// backwards compatible structures in the container when registering volumes.
+// This is a no-op on Windows.
+func setBackCompatStructures(*Container, map[string]string, map[string]bool) {
 }
diff --git a/errors/daemon.go b/errors/daemon.go
index ea31580094..4673ad306d 100644
--- a/errors/daemon.go
+++ b/errors/daemon.go
@@ -359,12 +359,12 @@ var (
 		HTTPStatusCode: http.StatusInternalServerError,
 	})
 
-	// ErrorCodeVolumeInvalidMode is generated when we the mode of a volume
+	// ErrorCodeVolumeInvalidMode is generated when we the mode of a volume/bind
 	// mount is invalid.
 	ErrorCodeVolumeInvalidMode = errcode.Register(errGroup, errcode.ErrorDescriptor{
 		Value:          "VOLUMEINVALIDMODE",
-		Message:        "invalid mode for volumes-from: %s",
-		Description:    "An invalid 'mode' was specified in the mount request",
+		Message:        "invalid mode: %s",
+		Description:    "An invalid 'mode' was specified",
 		HTTPStatusCode: http.StatusInternalServerError,
 	})
 
@@ -393,6 +393,41 @@ var (
 		HTTPStatusCode: http.StatusBadRequest,
 	})
 
+	// ErrorCodeVolumeSlash is generated when destination path to a volume is /
+	ErrorCodeVolumeSlash = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMESLASH",
+		Message:        "Invalid specification: destination can't be '/' in '%s'",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
+	// ErrorCodeVolumeDestIsC is generated the destination is c: (Windows specific)
+	ErrorCodeVolumeDestIsC = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMEDESTISC",
+		Message:        "Destination drive letter in '%s' cannot be c:",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
+	// ErrorCodeVolumeDestIsCRoot is generated the destination path is c:\ (Windows specific)
+	ErrorCodeVolumeDestIsCRoot = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMEDESTISCROOT",
+		Message:        `Destination path in '%s' cannot be c:\`,
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
+	// ErrorCodeVolumeSourceNotFound is generated the source directory could not be found (Windows specific)
+	ErrorCodeVolumeSourceNotFound = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMESOURCENOTFOUND",
+		Message:        "Source directory '%s' could not be found: %v",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
+	// ErrorCodeVolumeSourceNotDirectory is generated the source is not a directory (Windows specific)
+	ErrorCodeVolumeSourceNotDirectory = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMESOURCENOTDIRECTORY",
+		Message:        "Source '%s' is not a directory",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
 	// ErrorCodeVolumeFromBlank is generated when path to a volume is blank.
 	ErrorCodeVolumeFromBlank = errcode.Register(errGroup, errcode.ErrorDescriptor{
 		Value:          "VOLUMEFROMBLANK",
@@ -401,15 +436,6 @@ var (
 		HTTPStatusCode: http.StatusInternalServerError,
 	})
 
-	// ErrorCodeVolumeMode is generated when 'mode' for a volume
-	// isn't a valid.
-	ErrorCodeVolumeMode = errcode.Register(errGroup, errcode.ErrorDescriptor{
-		Value:          "VOLUMEMODE",
-		Message:        "invalid mode for volumes-from: %s",
-		Description:    "An invalid 'mode' path was specified in the mount request",
-		HTTPStatusCode: http.StatusInternalServerError,
-	})
-
 	// ErrorCodeVolumeDup is generated when we try to mount two volumes
 	// to the same path.
 	ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
@@ -419,6 +445,22 @@ var (
 		HTTPStatusCode: http.StatusInternalServerError,
 	})
 
+	// ErrorCodeVolumeNoSourceForMount is generated when no source directory
+	// for a volume mount was found. (Windows specific)
+	ErrorCodeVolumeNoSourceForMount = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMENOSOURCEFORMOUNT",
+		Message:        "No source for mount name %q driver %q destination %s",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
+	// ErrorCodeVolumeNameReservedWord is generated when the name in a volume
+	// uses a reserved word for filenames. (Windows specific)
+	ErrorCodeVolumeNameReservedWord = errcode.Register(errGroup, errcode.ErrorDescriptor{
+		Value:          "VOLUMENAMERESERVEDWORD",
+		Message:        "Volume name %q cannot be a reserved word for Windows filenames",
+		HTTPStatusCode: http.StatusInternalServerError,
+	})
+
 	// ErrorCodeCantUnpause is generated when there's an error while trying
 	// to unpause a container.
 	ErrorCodeCantUnpause = errcode.Register(errGroup, errcode.ErrorDescriptor{
diff --git a/image/fixtures/pre1.9/expected_config b/image/fixtures/pre1.9/expected_config
index 83fc30487a..121efe1fe6 100644
--- a/image/fixtures/pre1.9/expected_config
+++ b/image/fixtures/pre1.9/expected_config
@@ -1 +1,2 @@
 {"architecture":"amd64","config":{"Hostname":"03797203757d","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/go/bin:/usr/src/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOLANG_VERSION=1.4.1","GOPATH=/go"],"Cmd":null,"Image":"ec3025ca8cc9bcab039e193e20ec647c2da3c53a74020f2ba611601f9b2c6c02","Volumes":null,"WorkingDir":"/go","Entrypoint":["/go/bin/dnsdock"],"OnBuild":[],"Labels":{}},"container":"d91be3479d5b1e84b0c00d18eea9dc777ca0ad166d51174b24283e2e6f104253","container_config":{"Hostname":"03797203757d","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/go/bin:/usr/src/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOLANG_VERSION=1.4.1","GOPATH=/go"],"Cmd":["/bin/sh","-c","#(nop) ENTRYPOINT [\"/go/bin/dnsdock\"]"],"Image":"ec3025ca8cc9bcab039e193e20ec647c2da3c53a74020f2ba611601f9b2c6c02","Volumes":null,"WorkingDir":"/go","Entrypoint":["/go/bin/dnsdock"],"OnBuild":[],"Labels":{}},"created":"2015-08-19T16:49:11.368300679Z","docker_version":"1.6.2","layer_id":"sha256:31176893850e05d308cdbfef88877e460d50c8063883fb13eb5753097da6422a","os":"linux","parent_id":"sha256:ec3025ca8cc9bcab039e193e20ec647c2da3c53a74020f2ba611601f9b2c6c02"}
+
diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go
index 80d4ad26fd..e167f00416 100644
--- a/integration-cli/docker_cli_run_test.go
+++ b/integration-cli/docker_cli_run_test.go
@@ -293,8 +293,8 @@ func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *check.C) {
 	dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "true")
 	dockerCmd(c, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file")
 
-	if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", "/test/file"); err == nil || !strings.Contains(out, "invalid mode for volumes-from: bar") {
-		c.Fatalf("running --volumes-from foo:bar should have failed with invalid mount mode: %q", out)
+	if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", "/test/file"); err == nil || !strings.Contains(out, "invalid mode: bar") {
+		c.Fatalf("running --volumes-from foo:bar should have failed with invalid mode: %q", out)
 	}
 
 	dockerCmd(c, "run", "--volumes-from", "parent", "busybox", "touch", "/test/file")
diff --git a/opts/opts.go b/opts/opts.go
index 3d8e4b4816..46adef1d85 100644
--- a/opts/opts.go
+++ b/opts/opts.go
@@ -9,7 +9,6 @@ import (
 	"strings"
 
 	"github.com/docker/docker/pkg/parsers"
-	"github.com/docker/docker/volume"
 )
 
 var (
@@ -214,14 +213,6 @@ func ValidateDevice(val string) (string, error) {
 	return validatePath(val, ValidDeviceMode)
 }
 
-// ValidatePath validates a path for volumes
-// It will make sure 'val' is in the form:
-//    [host-dir:]container-path[:rw|ro]
-// It also validates the mount mode.
-func ValidatePath(val string) (string, error) {
-	return validatePath(val, volume.ValidMountMode)
-}
-
 func validatePath(val string, validator func(string) bool) (string, error) {
 	var containerPath string
 	var mode string
diff --git a/opts/opts_test.go b/opts/opts_test.go
index baf5f53362..e02d3f8ea6 100644
--- a/opts/opts_test.go
+++ b/opts/opts_test.go
@@ -274,58 +274,6 @@ func TestValidateLink(t *testing.T) {
 	}
 }
 
-func TestValidatePath(t *testing.T) {
-	valid := []string{
-		"/home",
-		"/home:/home",
-		"/home:/something/else",
-		"/with space",
-		"/home:/with space",
-		"relative:/absolute-path",
-		"hostPath:/containerPath:ro",
-		"/hostPath:/containerPath:rw",
-		"/rw:/ro",
-		"/path:rw",
-		"/path:ro",
-		"/rw:rw",
-	}
-	invalid := map[string]string{
-		"":                "bad format for path: ",
-		"./":              "./ is not an absolute path",
-		"../":             "../ is not an absolute path",
-		"/:../":           "../ is not an absolute path",
-		"/:path":          "path is not an absolute path",
-		":":               "bad format for path: :",
-		"/tmp:":           " is not an absolute path",
-		":test":           "bad format for path: :test",
-		":/test":          "bad format for path: :/test",
-		"tmp:":            " is not an absolute path",
-		":test:":          "bad format for path: :test:",
-		"::":              "bad format for path: ::",
-		":::":             "bad format for path: :::",
-		"/tmp:::":         "bad format for path: /tmp:::",
-		":/tmp::":         "bad format for path: :/tmp::",
-		"path:ro":         "path is not an absolute path",
-		"/path:/path:sw":  "bad mode specified: sw",
-		"/path:/path:rwz": "bad mode specified: rwz",
-	}
-
-	for _, path := range valid {
-		if _, err := ValidatePath(path); err != nil {
-			t.Fatalf("ValidatePath(`%q`) should succeed: error %q", path, err)
-		}
-	}
-
-	for path, expectedError := range invalid {
-		if _, err := ValidatePath(path); err == nil {
-			t.Fatalf("ValidatePath(`%q`) should have failed validation", path)
-		} else {
-			if err.Error() != expectedError {
-				t.Fatalf("ValidatePath(`%q`) error should contain %q, got %q", path, expectedError, err.Error())
-			}
-		}
-	}
-}
 func TestValidateDevice(t *testing.T) {
 	valid := []string{
 		"/home",
diff --git a/pkg/system/syscall_unix.go b/pkg/system/syscall_unix.go
new file mode 100644
index 0000000000..e1b90b38e6
--- /dev/null
+++ b/pkg/system/syscall_unix.go
@@ -0,0 +1,11 @@
+// +build linux freebsd
+
+package system
+
+import "syscall"
+
+// UnmountWithSyscall is a platform-specific helper function to call
+// the unmount syscall.
+func UnmountWithSyscall(dest string) {
+	syscall.Unmount(dest, 0)
+}
diff --git a/pkg/system/syscall_windows.go b/pkg/system/syscall_windows.go
new file mode 100644
index 0000000000..26bd80bd5d
--- /dev/null
+++ b/pkg/system/syscall_windows.go
@@ -0,0 +1,6 @@
+package system
+
+// UnmountWithSyscall is a platform-specific helper function to call
+// the unmount syscall. Not supported on Windows
+func UnmountWithSyscall(dest string) {
+}
diff --git a/runconfig/config.go b/runconfig/config.go
index 08f146d1b9..ced845f20e 100644
--- a/runconfig/config.go
+++ b/runconfig/config.go
@@ -2,10 +2,12 @@ package runconfig
 
 import (
 	"encoding/json"
+	"fmt"
 	"io"
 
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/stringutils"
+	"github.com/docker/docker/volume"
 )
 
 // Config contains the configuration data about a container.
@@ -44,15 +46,29 @@ type Config struct {
 // Be aware this function is not checking whether the resulted structs are nil,
 // it's your business to do so
 func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
-	decoder := json.NewDecoder(src)
-
 	var w ContainerConfigWrapper
+
+	decoder := json.NewDecoder(src)
 	if err := decoder.Decode(&w); err != nil {
 		return nil, nil, err
 	}
 
 	hc := w.getHostConfig()
 
+	// Perform platform-specific processing of Volumes and Binds.
+	if w.Config != nil && hc != nil {
+
+		// Initialise the volumes map if currently nil
+		if w.Config.Volumes == nil {
+			w.Config.Volumes = make(map[string]struct{})
+		}
+
+		// Now validate all the volumes and binds
+		if err := validateVolumesAndBindSettings(w.Config, hc); err != nil {
+			return nil, nil, err
+		}
+	}
+
 	// Certain parameters need daemon-side validation that cannot be done
 	// on the client, as only the daemon knows what is valid for the platform.
 	if err := ValidateNetMode(w.Config, hc); err != nil {
@@ -61,3 +77,22 @@ func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
 
 	return w.Config, hc, nil
 }
+
+// validateVolumesAndBindSettings validates each of the volumes and bind settings
+// passed by the caller to ensure they are valid.
+func validateVolumesAndBindSettings(c *Config, hc *HostConfig) error {
+
+	// Ensure all volumes and binds are valid.
+	for spec := range c.Volumes {
+		if _, err := volume.ParseMountSpec(spec, hc.VolumeDriver); err != nil {
+			return fmt.Errorf("Invalid volume spec %q: %v", spec, err)
+		}
+	}
+	for _, spec := range hc.Binds {
+		if _, err := volume.ParseMountSpec(spec, hc.VolumeDriver); err != nil {
+			return fmt.Errorf("Invalid bind mount spec %q: %v", spec, err)
+		}
+	}
+
+	return nil
+}
diff --git a/runconfig/config_test.go b/runconfig/config_test.go
index 66def67446..b4890aeb0c 100644
--- a/runconfig/config_test.go
+++ b/runconfig/config_test.go
@@ -4,19 +4,36 @@ import (
 	"bytes"
 	"fmt"
 	"io/ioutil"
+	"runtime"
 	"testing"
 
 	"github.com/docker/docker/pkg/stringutils"
 )
 
+type f struct {
+	file       string
+	entrypoint *stringutils.StrSlice
+}
+
 func TestDecodeContainerConfig(t *testing.T) {
-	fixtures := []struct {
-		file       string
-		entrypoint *stringutils.StrSlice
-	}{
-		{"fixtures/container_config_1_14.json", stringutils.NewStrSlice()},
-		{"fixtures/container_config_1_17.json", stringutils.NewStrSlice("bash")},
-		{"fixtures/container_config_1_19.json", stringutils.NewStrSlice("bash")},
+
+	var (
+		fixtures []f
+		image    string
+	)
+
+	if runtime.GOOS != "windows" {
+		image = "ubuntu"
+		fixtures = []f{
+			{"fixtures/unix/container_config_1_14.json", stringutils.NewStrSlice()},
+			{"fixtures/unix/container_config_1_17.json", stringutils.NewStrSlice("bash")},
+			{"fixtures/unix/container_config_1_19.json", stringutils.NewStrSlice("bash")},
+		}
+	} else {
+		image = "windows"
+		fixtures = []f{
+			{"fixtures/windows/container_config_1_19.json", stringutils.NewStrSlice("cmd")},
+		}
 	}
 
 	for _, f := range fixtures {
@@ -30,15 +47,15 @@ func TestDecodeContainerConfig(t *testing.T) {
 			t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
 		}
 
-		if c.Image != "ubuntu" {
-			t.Fatalf("Expected ubuntu image, found %s\n", c.Image)
+		if c.Image != image {
+			t.Fatalf("Expected %s image, found %s\n", image, c.Image)
 		}
 
 		if c.Entrypoint.Len() != f.entrypoint.Len() {
 			t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint)
 		}
 
-		if h.Memory != 1000 {
+		if h != nil && h.Memory != 1000 {
 			t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory)
 		}
 	}
diff --git a/runconfig/fixtures/container_config_1_14.json b/runconfig/fixtures/unix/container_config_1_14.json
similarity index 100%
rename from runconfig/fixtures/container_config_1_14.json
rename to runconfig/fixtures/unix/container_config_1_14.json
diff --git a/runconfig/fixtures/container_config_1_17.json b/runconfig/fixtures/unix/container_config_1_17.json
similarity index 100%
rename from runconfig/fixtures/container_config_1_17.json
rename to runconfig/fixtures/unix/container_config_1_17.json
diff --git a/runconfig/fixtures/container_config_1_19.json b/runconfig/fixtures/unix/container_config_1_19.json
similarity index 100%
rename from runconfig/fixtures/container_config_1_19.json
rename to runconfig/fixtures/unix/container_config_1_19.json
diff --git a/runconfig/fixtures/container_hostconfig_1_14.json b/runconfig/fixtures/unix/container_hostconfig_1_14.json
similarity index 100%
rename from runconfig/fixtures/container_hostconfig_1_14.json
rename to runconfig/fixtures/unix/container_hostconfig_1_14.json
diff --git a/runconfig/fixtures/container_hostconfig_1_19.json b/runconfig/fixtures/unix/container_hostconfig_1_19.json
similarity index 100%
rename from runconfig/fixtures/container_hostconfig_1_19.json
rename to runconfig/fixtures/unix/container_hostconfig_1_19.json
diff --git a/runconfig/fixtures/windows/container_config_1_19.json b/runconfig/fixtures/windows/container_config_1_19.json
new file mode 100644
index 0000000000..724320c760
--- /dev/null
+++ b/runconfig/fixtures/windows/container_config_1_19.json
@@ -0,0 +1,58 @@
+{
+     "Hostname": "",
+     "Domainname": "",
+     "User": "",
+     "AttachStdin": false,
+     "AttachStdout": true,
+     "AttachStderr": true,
+     "Tty": false,
+     "OpenStdin": false,
+     "StdinOnce": false,
+     "Env": null,
+     "Cmd": [
+             "date"
+     ],
+     "Entrypoint": "cmd",
+     "Image": "windows",
+     "Labels": {
+             "com.example.vendor": "Acme",
+             "com.example.license": "GPL",
+             "com.example.version": "1.0"
+     },
+     "Volumes": {
+             "c:/windows": {}
+     },
+     "WorkingDir": "",
+     "NetworkDisabled": false,
+     "MacAddress": "12:34:56:78:9a:bc",
+     "ExposedPorts": {
+             "22/tcp": {}
+     },
+     "HostConfig": {
+       "Binds": ["c:/windows:d:/tmp"],
+       "Links": ["redis3:redis"],
+       "LxcConf": {"lxc.utsname":"docker"},
+       "Memory": 1000,
+       "MemorySwap": 0,
+       "CpuShares": 512,
+       "CpusetCpus": "0,1",
+       "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
+       "PublishAllPorts": false,
+       "Privileged": false,
+       "ReadonlyRootfs": false,
+       "Dns": ["8.8.8.8"],
+       "DnsSearch": [""],
+       "DnsOptions": [""],
+       "ExtraHosts": null,
+       "VolumesFrom": ["parent", "other:ro"],
+       "CapAdd": ["NET_ADMIN"],
+       "CapDrop": ["MKNOD"],
+       "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
+       "NetworkMode": "default",
+       "Devices": [],
+       "Ulimits": [{}],
+       "LogConfig": { "Type": "json-file", "Config": {} },
+       "SecurityOpt": [""],
+       "CgroupParent": ""
+    }
+}
diff --git a/runconfig/hostconfig_test.go b/runconfig/hostconfig_test.go
index 806f4a8a54..3e2eddd183 100644
--- a/runconfig/hostconfig_test.go
+++ b/runconfig/hostconfig_test.go
@@ -234,8 +234,8 @@ func TestDecodeHostConfig(t *testing.T) {
 	fixtures := []struct {
 		file string
 	}{
-		{"fixtures/container_hostconfig_1_14.json"},
-		{"fixtures/container_hostconfig_1_19.json"},
+		{"fixtures/unix/container_hostconfig_1_14.json"},
+		{"fixtures/unix/container_hostconfig_1_19.json"},
 	}
 
 	for _, f := range fixtures {
diff --git a/runconfig/parse.go b/runconfig/parse.go
index 7297597d57..4bc2fee6aa 100644
--- a/runconfig/parse.go
+++ b/runconfig/parse.go
@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/units"
+	"github.com/docker/docker/volume"
 )
 
 var (
@@ -46,7 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 	var (
 		// FIXME: use utils.ListOpts for attach and volumes?
 		flAttach  = opts.NewListOpts(opts.ValidateAttach)
-		flVolumes = opts.NewListOpts(opts.ValidatePath)
+		flVolumes = opts.NewListOpts(nil)
 		flLinks   = opts.NewListOpts(opts.ValidateLink)
 		flEnv     = opts.NewListOpts(opts.ValidateEnv)
 		flLabels  = opts.NewListOpts(opts.ValidateEnv)
@@ -201,16 +202,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 	var binds []string
 	// add any bind targets to the list of container volumes
 	for bind := range flVolumes.GetMap() {
-		if arr := strings.Split(bind, ":"); len(arr) > 1 {
-			if arr[1] == "/" {
-				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: destination can't be '/'")
-			}
+		if arr := volume.SplitN(bind, 2); len(arr) > 1 {
 			// after creating the bind mount we want to delete it from the flVolumes values because
 			// we do not want bind mounts being committed to image configs
 			binds = append(binds, bind)
 			flVolumes.Delete(bind)
-		} else if bind == "/" {
-			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
 		}
 	}
 
diff --git a/runconfig/parse_test.go b/runconfig/parse_test.go
index 4cae1c7c69..e4420cf0c0 100644
--- a/runconfig/parse_test.go
+++ b/runconfig/parse_test.go
@@ -1,8 +1,12 @@
 package runconfig
 
 import (
+	"bytes"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"os"
+	"runtime"
 	"strings"
 	"testing"
 
@@ -31,17 +35,6 @@ func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
 	return config, hostConfig
 }
 
-// check if (a == c && b == d) || (a == d && b == c)
-// because maps are randomized
-func compareRandomizedStrings(a, b, c, d string) error {
-	if a == c && b == d {
-		return nil
-	}
-	if a == d && b == c {
-		return nil
-	}
-	return fmt.Errorf("strings don't match")
-}
 func TestParseRunLinks(t *testing.T) {
 	if _, hostConfig := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
 		t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
@@ -98,81 +91,257 @@ func TestParseRunAttach(t *testing.T) {
 }
 
 func TestParseRunVolumes(t *testing.T) {
-	if config, hostConfig := mustParse(t, "-v /tmp"); hostConfig.Binds != nil {
-		t.Fatalf("Error parsing volume flags, `-v /tmp` should not mount-bind anything. Received %v", hostConfig.Binds)
-	} else if _, exists := config.Volumes["/tmp"]; !exists {
-		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
+
+	// A single volume
+	arr, tryit := setupPlatformVolume([]string{`/tmp`}, []string{`c:\tmp`})
+	if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil {
+		t.Fatalf("Error parsing volume flags, %q should not mount-bind anything. Received %v", tryit, hostConfig.Binds)
+	} else if _, exists := config.Volumes[arr[0]]; !exists {
+		t.Fatalf("Error parsing volume flags, %q is missing from volumes. Received %v", tryit, config.Volumes)
 	}
 
-	if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil {
-		t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds)
-	} else if _, exists := config.Volumes["/tmp"]; !exists {
-		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
-	} else if _, exists := config.Volumes["/var"]; !exists {
-		t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
+	// Two volumes
+	arr, tryit = setupPlatformVolume([]string{`/tmp`, `/var`}, []string{`c:\tmp`, `c:\var`})
+	if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil {
+		t.Fatalf("Error parsing volume flags, %q should not mount-bind anything. Received %v", tryit, hostConfig.Binds)
+	} else if _, exists := config.Volumes[arr[0]]; !exists {
+		t.Fatalf("Error parsing volume flags, %s is missing from volumes. Received %v", arr[0], config.Volumes)
+	} else if _, exists := config.Volumes[arr[1]]; !exists {
+		t.Fatalf("Error parsing volume flags, %s is missing from volumes. Received %v", arr[1], config.Volumes)
 	}
 
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containerTmp. Received %v", hostConfig.Binds)
+	// A single bind-mount
+	arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`}, []string{os.Getenv("TEMP") + `:c:\containerTmp`})
+	if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || hostConfig.Binds[0] != arr[0] {
+		t.Fatalf("Error parsing volume flags, %q should mount-bind the path before the colon into the path after the colon. Received %v %v", arr[0], hostConfig.Binds, config.Volumes)
 	}
 
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp", "/hostVar:/containerVar") != nil {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
+	// Two bind-mounts.
+	arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`, `/hostVar:/containerVar`}, []string{os.Getenv("ProgramData") + `:c:\ContainerPD`, os.Getenv("TEMP") + `:c:\containerTmp`})
+	if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
+		t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
 	}
 
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro", "/hostVar:/containerVar:rw") != nil {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
+	// Two bind-mounts, first read-only, second read-write.
+	// TODO Windows: The Windows version uses read-write as that's the only mode it supports. Can change this post TP4
+	arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp:ro`, `/hostVar:/containerVar:rw`}, []string{os.Getenv("TEMP") + `:c:\containerTmp:rw`, os.Getenv("ProgramData") + `:c:\ContainerPD:rw`})
+	if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
+		t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
 	}
 
-	if _, hostConfig := mustParse(t, "-v /containerTmp:ro -v /containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/containerTmp:ro", "/containerVar:rw") != nil {
-		t.Fatalf("Error parsing volume flags, `-v /containerTmp:ro -v /containerVar:rw` should mount-bind /containerTmp into /ro and /containerVar into /rw. Received %v", hostConfig.Binds)
+	// Similar to previous test but with alternate modes which are only supported by Linux
+	if runtime.GOOS != "windows" {
+		arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp:ro,Z`, `/hostVar:/containerVar:rw,Z`}, []string{})
+		if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
+			t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
+		}
+
+		arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp:Z`, `/hostVar:/containerVar:z`}, []string{})
+		if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
+			t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
+		}
 	}
 
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro,Z -v /hostVar:/containerVar:rw,Z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro,Z", "/hostVar:/containerVar:rw,Z") != nil {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro,Z -v /hostVar:/containerVar:rw,Z` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
+	// One bind mount and one volume
+	arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`, `/containerVar`}, []string{os.Getenv("TEMP") + `:c:\containerTmp`, `c:\containerTmp`})
+	if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] {
+		t.Fatalf("Error parsing volume flags, %s and %s should only one and only one bind mount %s. Received %s", arr[0], arr[1], arr[0], hostConfig.Binds)
+	} else if _, exists := config.Volumes[arr[1]]; !exists {
+		t.Fatalf("Error parsing volume flags %s and %s. %s is missing from volumes. Received %v", arr[0], arr[1], arr[1], config.Volumes)
 	}
 
-	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:Z", "/hostVar:/containerVar:z") != nil {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
+	// Root to non-c: drive letter (Windows specific)
+	if runtime.GOOS == "windows" {
+		arr, tryit = setupPlatformVolume([]string{}, []string{os.Getenv("SystemDrive") + `\:d:`})
+		if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] || len(config.Volumes) != 0 {
+			t.Fatalf("Error parsing %s. Should have a single bind mount and no volumes", arr[0])
+		}
 	}
 
-	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
-		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containerTmp. Received %v", hostConfig.Binds)
-	} else if _, exists := config.Volumes["/containerVar"]; !exists {
-		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
+}
+
+// This tests the cases for binds which are generated through
+// DecodeContainerConfig rather than Parse()
+func TestDecodeContainerConfigVolumes(t *testing.T) {
+
+	// Root to root
+	bindsOrVols, _ := setupPlatformVolume([]string{`/:/`}, []string{os.Getenv("SystemDrive") + `\:c:\`})
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
+	}
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("volume %v should have failed", bindsOrVols)
 	}
 
-	if config, hostConfig := mustParse(t, ""); hostConfig.Binds != nil {
-		t.Fatalf("Error parsing volume flags, without volume, nothing should be mount-binded. Received %v", hostConfig.Binds)
-	} else if len(config.Volumes) != 0 {
-		t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
+	// No destination path
+	bindsOrVols, _ = setupPlatformVolume([]string{`/tmp:`}, []string{os.Getenv("TEMP") + `\:`})
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
+	}
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
 
-	if _, _, err := parse(t, "-v /"); err == nil {
-		t.Fatalf("Expected error, but got none")
+	//	// No destination path or mode
+	bindsOrVols, _ = setupPlatformVolume([]string{`/tmp::`}, []string{os.Getenv("TEMP") + `\::`})
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
+	}
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
 
-	if _, _, err := parse(t, "-v /:/"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
+	// A whole lot of nothing
+	bindsOrVols = []string{`:`}
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v` should fail but didn't")
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v /tmp:"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v /tmp:` should fail but didn't")
+
+	// A whole lot of nothing with no mode
+	bindsOrVols = []string{`::`}
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v /tmp::"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v /tmp::` should fail but didn't")
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v :"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v :` should fail but didn't")
+
+	// Too much including an invalid mode
+	wTmp := os.Getenv("TEMP")
+	bindsOrVols, _ = setupPlatformVolume([]string{`/tmp:/tmp:/tmp:/tmp`}, []string{wTmp + ":" + wTmp + ":" + wTmp + ":" + wTmp})
+	if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v ::"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v ::` should fail but didn't")
+	if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+		t.Fatalf("binds %v should have failed", bindsOrVols)
 	}
-	if _, _, err := parse(t, "-v /tmp:/tmp:/tmp:/tmp"); err == nil {
-		t.Fatalf("Error parsing volume flags, `-v /tmp:/tmp:/tmp:/tmp` should fail but didn't")
+
+	// Windows specific error tests
+	if runtime.GOOS == "windows" {
+		// Volume which does not include a drive letter
+		bindsOrVols = []string{`\tmp`}
+		if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+		if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+
+		// Root to C-Drive
+		bindsOrVols = []string{os.Getenv("SystemDrive") + `\:c:`}
+		if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+		if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+
+		// Container path that does not include a drive letter
+		bindsOrVols = []string{`c:\windows:\somewhere`}
+		if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+		if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
 	}
+
+	// Linux-specific error tests
+	if runtime.GOOS != "windows" {
+		// Just root
+		bindsOrVols = []string{`/`}
+		if _, _, err := callDecodeContainerConfig(nil, bindsOrVols); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+		if _, _, err := callDecodeContainerConfig(bindsOrVols, nil); err == nil {
+			t.Fatalf("binds %v should have failed", bindsOrVols)
+		}
+
+		// A single volume that looks like a bind mount passed in Volumes.
+		// This should be handled as a bind mount, not a volume.
+		vols := []string{`/foo:/bar`}
+		if config, hostConfig, err := callDecodeContainerConfig(vols, nil); err != nil {
+			t.Fatal("Volume /foo:/bar should have succeeded as a volume name")
+		} else if hostConfig.Binds != nil {
+			t.Fatalf("Error parsing volume flags, /foo:/bar should not mount-bind anything. Received %v", hostConfig.Binds)
+		} else if _, exists := config.Volumes[vols[0]]; !exists {
+			t.Fatalf("Error parsing volume flags, /foo:/bar is missing from volumes. Received %v", config.Volumes)
+		}
+
+	}
+}
+
+// callDecodeContainerConfig is a utility function used by TestDecodeContainerConfigVolumes
+// to call DecodeContainerConfig. It effectively does what a client would
+// do when calling the daemon by constructing a JSON stream of a
+// ContainerConfigWrapper which is populated by the set of volume specs
+// passed into it. It returns a config and a hostconfig which can be
+// validated to ensure DecodeContainerConfig has manipulated the structures
+// correctly.
+func callDecodeContainerConfig(volumes []string, binds []string) (*Config, *HostConfig, error) {
+	var (
+		b   []byte
+		err error
+		c   *Config
+		h   *HostConfig
+	)
+	w := ContainerConfigWrapper{
+		Config: &Config{
+			Volumes: map[string]struct{}{},
+		},
+		HostConfig: &HostConfig{
+			NetworkMode: "none",
+			Binds:       binds,
+		},
+	}
+	for _, v := range volumes {
+		w.Config.Volumes[v] = struct{}{}
+	}
+	if b, err = json.Marshal(w); err != nil {
+		return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
+	}
+	c, h, err = DecodeContainerConfig(bytes.NewReader(b))
+	if err != nil {
+		return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
+	}
+	if c == nil || h == nil {
+		return nil, nil, fmt.Errorf("Empty config or hostconfig")
+	}
+
+	return c, h, err
+}
+
+// check if (a == c && b == d) || (a == d && b == c)
+// because maps are randomized
+func compareRandomizedStrings(a, b, c, d string) error {
+	if a == c && b == d {
+		return nil
+	}
+	if a == d && b == c {
+		return nil
+	}
+	return fmt.Errorf("strings don't match")
+}
+
+// setupPlatformVolume takes two arrays of volume specs - a Unix style
+// spec and a Windows style spec. Depending on the platform being unit tested,
+// it returns one of them, along with a volume string that would be passed
+// on the docker CLI (eg -v /bar -v /foo).
+func setupPlatformVolume(u []string, w []string) ([]string, string) {
+	var a []string
+	if runtime.GOOS == "windows" {
+		a = w
+	} else {
+		a = u
+	}
+	s := ""
+	for _, v := range a {
+		s = s + "-v " + v + " "
+	}
+	return a, s
 }
 
 func TestParseLxcConfOpt(t *testing.T) {
@@ -438,9 +607,13 @@ func TestParseLoggingOpts(t *testing.T) {
 }
 
 func TestParseEnvfileVariables(t *testing.T) {
+	e := "open nonexistent: no such file or directory"
+	if runtime.GOOS == "windows" {
+		e = "open nonexistent: The system cannot find the file specified."
+	}
 	// env ko
-	if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
-		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
+	if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
+		t.Fatalf("Expected an error with message '%s', got %v", e, err)
 	}
 	// env ok
 	config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
@@ -460,9 +633,13 @@ func TestParseEnvfileVariables(t *testing.T) {
 }
 
 func TestParseLabelfileVariables(t *testing.T) {
+	e := "open nonexistent: no such file or directory"
+	if runtime.GOOS == "windows" {
+		e = "open nonexistent: The system cannot find the file specified."
+	}
 	// label ko
-	if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
-		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
+	if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
+		t.Fatalf("Expected an error with message '%s', got %v", e, err)
 	}
 	// label ok
 	config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
diff --git a/volume/drivers/extpoint_test.go b/volume/drivers/extpoint_test.go
index f9824e5fed..8ab60c95e3 100644
--- a/volume/drivers/extpoint_test.go
+++ b/volume/drivers/extpoint_test.go
@@ -11,7 +11,6 @@ func TestGetDriver(t *testing.T) {
 	if err == nil {
 		t.Fatal("Expected error, was nil")
 	}
-
 	Register(volumetestutils.FakeDriver{}, "fake")
 	d, err := GetDriver("fake")
 	if err != nil {
diff --git a/volume/store/store.go b/volume/store/store.go
index 46c44333cd..22c213fb48 100644
--- a/volume/store/store.go
+++ b/volume/store/store.go
@@ -14,6 +14,8 @@ var (
 	ErrVolumeInUse = errors.New("volume is in use")
 	// ErrNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store
 	ErrNoSuchVolume = errors.New("no such volume")
+	// ErrInvalidName is a typed error returned when creating a volume with a name that is not valid on the platform
+	ErrInvalidName = errors.New("volume name is not valid on this platform")
 )
 
 // New initializes a VolumeStore to keep
@@ -39,13 +41,14 @@ type volumeCounter struct {
 // AddAll adds a list of volumes to the store
 func (s *VolumeStore) AddAll(vols []volume.Volume) {
 	for _, v := range vols {
-		s.vols[v.Name()] = &volumeCounter{v, 0}
+		s.vols[normaliseVolumeName(v.Name())] = &volumeCounter{v, 0}
 	}
 }
 
 // Create tries to find an existing volume with the given name or create a new one from the passed in driver
 func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) {
 	s.mu.Lock()
+	name = normaliseVolumeName(name)
 	if vc, exists := s.vols[name]; exists {
 		v := vc.Volume
 		s.mu.Unlock()
@@ -59,13 +62,22 @@ func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (v
 		return nil, err
 	}
 
+	// Validate the name in a platform-specific manner
+	valid, err := volume.IsVolumeNameValid(name)
+	if err != nil {
+		return nil, err
+	}
+	if !valid {
+		return nil, ErrInvalidName
+	}
+
 	v, err := vd.Create(name, opts)
 	if err != nil {
 		return nil, err
 	}
 
 	s.mu.Lock()
-	s.vols[v.Name()] = &volumeCounter{v, 0}
+	s.vols[normaliseVolumeName(v.Name())] = &volumeCounter{v, 0}
 	s.mu.Unlock()
 
 	return v, nil
@@ -73,6 +85,7 @@ func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (v
 
 // Get looks if a volume with the given name exists and returns it if so
 func (s *VolumeStore) Get(name string) (volume.Volume, error) {
+	name = normaliseVolumeName(name)
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	vc, exists := s.vols[name]
@@ -86,7 +99,7 @@ func (s *VolumeStore) Get(name string) (volume.Volume, error) {
 func (s *VolumeStore) Remove(v volume.Volume) error {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	name := v.Name()
+	name := normaliseVolumeName(v.Name())
 	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
 	vc, exists := s.vols[name]
 	if !exists {
@@ -112,11 +125,12 @@ func (s *VolumeStore) Remove(v volume.Volume) error {
 func (s *VolumeStore) Increment(v volume.Volume) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())
+	name := normaliseVolumeName(v.Name())
+	logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), name)
 
-	vc, exists := s.vols[v.Name()]
+	vc, exists := s.vols[name]
 	if !exists {
-		s.vols[v.Name()] = &volumeCounter{v, 1}
+		s.vols[name] = &volumeCounter{v, 1}
 		return
 	}
 	vc.count++
@@ -126,9 +140,10 @@ func (s *VolumeStore) Increment(v volume.Volume) {
 func (s *VolumeStore) Decrement(v volume.Volume) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), v.Name())
+	name := normaliseVolumeName(v.Name())
+	logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), name)
 
-	vc, exists := s.vols[v.Name()]
+	vc, exists := s.vols[name]
 	if !exists {
 		return
 	}
@@ -142,7 +157,7 @@ func (s *VolumeStore) Decrement(v volume.Volume) {
 func (s *VolumeStore) Count(v volume.Volume) uint {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	vc, exists := s.vols[v.Name()]
+	vc, exists := s.vols[normaliseVolumeName(v.Name())]
 	if !exists {
 		return 0
 	}
diff --git a/volume/store/store_unix.go b/volume/store/store_unix.go
new file mode 100644
index 0000000000..319c541d60
--- /dev/null
+++ b/volume/store/store_unix.go
@@ -0,0 +1,9 @@
+// +build linux freebsd
+
+package store
+
+// normaliseVolumeName is a platform specific function to normalise the name
+// of a volume. This is a no-op on Unix-like platforms
+func normaliseVolumeName(name string) string {
+	return name
+}
diff --git a/volume/store/store_windows.go b/volume/store/store_windows.go
new file mode 100644
index 0000000000..a42c1f8413
--- /dev/null
+++ b/volume/store/store_windows.go
@@ -0,0 +1,12 @@
+package store
+
+import "strings"
+
+// normaliseVolumeName is a platform specific function to normalise the name
+// of a volume. On Windows, as NTFS is case insensitive, under
+// c:\ProgramData\Docker\Volumes\, the folders John and john would be synonymous.
+// Hence we can't allow the volume "John" and "john" to be created as seperate
+// volumes.
+func normaliseVolumeName(name string) string {
+	return strings.ToLower(name)
+}
diff --git a/volume/volume.go b/volume/volume.go
index 647469e50f..98f90f06d4 100644
--- a/volume/volume.go
+++ b/volume/volume.go
@@ -1,5 +1,15 @@
 package volume
 
+import (
+	"os"
+	"runtime"
+	"strings"
+
+	"github.com/Sirupsen/logrus"
+	derr "github.com/docker/docker/errors"
+	"github.com/docker/docker/pkg/system"
+)
+
 // DefaultDriverName is the driver name used for the driver
 // implemented in the local package.
 const DefaultDriverName string = "local"
@@ -29,33 +39,134 @@ type Volume interface {
 	Unmount() error
 }
 
-// read-write modes
-var rwModes = map[string]bool{
-	"rw":   true,
-	"rw,Z": true,
-	"rw,z": true,
-	"z,rw": true,
-	"Z,rw": true,
-	"Z":    true,
-	"z":    true,
+// MountPoint is the intersection point between a volume and a container. It
+// specifies which volume is to be used and where inside a container it should
+// be mounted.
+type MountPoint struct {
+	Source      string // Container host directory
+	Destination string // Inside the container
+	RW          bool   // True if writable
+	Name        string // Name set by user
+	Driver      string // Volume driver to use
+	Volume      Volume `json:"-"`
+
+	// Note Mode is not used on Windows
+	Mode string `json:"Relabel"` // Originally field was `Relabel`"
 }
 
-// read-only modes
-var roModes = map[string]bool{
-	"ro":   true,
-	"ro,Z": true,
-	"ro,z": true,
-	"z,ro": true,
-	"Z,ro": true,
+// Setup sets up a mount point by either mounting the volume if it is
+// configured, or creating the source directory if supplied.
+func (m *MountPoint) Setup() (string, error) {
+	if m.Volume != nil {
+		return m.Volume.Mount()
+	}
+	if len(m.Source) > 0 {
+		if _, err := os.Stat(m.Source); err != nil {
+			if !os.IsNotExist(err) {
+				return "", err
+			}
+			if runtime.GOOS != "windows" { // Windows does not have deprecation issues here
+				logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source)
+				if err := system.MkdirAll(m.Source, 0755); err != nil {
+					return "", err
+				}
+			}
+		}
+		return m.Source, nil
+	}
+	return "", derr.ErrorCodeMountSetup
+}
+
+// Path returns the path of a volume in a mount point.
+func (m *MountPoint) Path() string {
+	if m.Volume != nil {
+		return m.Volume.Path()
+	}
+	return m.Source
 }
 
 // ValidMountMode will make sure the mount mode is valid.
 // returns if it's a valid mount mode or not.
 func ValidMountMode(mode string) bool {
-	return roModes[mode] || rwModes[mode]
+	return roModes[strings.ToLower(mode)] || rwModes[strings.ToLower(mode)]
 }
 
 // ReadWrite tells you if a mode string is a valid read-write mode or not.
 func ReadWrite(mode string) bool {
-	return rwModes[mode]
+	return rwModes[strings.ToLower(mode)]
+}
+
+// ParseVolumesFrom ensure that the supplied volumes-from is valid.
+func ParseVolumesFrom(spec string) (string, string, error) {
+	if len(spec) == 0 {
+		return "", "", derr.ErrorCodeVolumeFromBlank.WithArgs(spec)
+	}
+
+	specParts := strings.SplitN(spec, ":", 2)
+	id := specParts[0]
+	mode := "rw"
+
+	if len(specParts) == 2 {
+		mode = specParts[1]
+		if !ValidMountMode(mode) {
+			return "", "", derr.ErrorCodeVolumeInvalidMode.WithArgs(mode)
+		}
+	}
+	return id, mode, nil
+}
+
+// SplitN splits raw into a maximum of n parts, separated by a separator colon.
+// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped).
+// This allows to correctly split strings such as `C:\foo:D:\:rw`.
+func SplitN(raw string, n int) []string {
+	var array []string
+	if len(raw) == 0 || raw[0] == ':' {
+		// invalid
+		return nil
+	}
+	// numberOfParts counts the number of parts separated by a separator colon
+	numberOfParts := 0
+	// left represents the left-most cursor in raw, updated at every `:` character considered as a separator.
+	left := 0
+	// right represents the right-most cursor in raw incremented with the loop. Note this
+	// starts at index 1 as index 0 is already handle above as a special case.
+	for right := 1; right < len(raw); right++ {
+		// stop parsing if reached maximum number of parts
+		if n >= 0 && numberOfParts >= n {
+			break
+		}
+		if raw[right] != ':' {
+			continue
+		}
+		potentialDriveLetter := raw[right-1]
+		if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
+			if right > 1 {
+				beforePotentialDriveLetter := raw[right-2]
+				if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' {
+					// e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`.
+					array = append(array, raw[left:right])
+					left = right + 1
+					numberOfParts++
+				}
+				// else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing.
+			}
+			// if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing.
+		} else {
+			// if `:` is not preceded by a potential drive letter, then consider it as a delimiter.
+			array = append(array, raw[left:right])
+			left = right + 1
+			numberOfParts++
+		}
+	}
+	// need to take care of the last part
+	if left < len(raw) {
+		if n >= 0 && numberOfParts >= n {
+			// if the maximum number of parts is reached, just append the rest to the last part
+			// left-1 is at the last `:` that needs to be included since not considered a separator.
+			array[n-1] += raw[left-1:]
+		} else {
+			array = append(array, raw[left:])
+		}
+	}
+	return array
 }
diff --git a/volume/volume_test.go b/volume/volume_test.go
new file mode 100644
index 0000000000..5ce3d9fa96
--- /dev/null
+++ b/volume/volume_test.go
@@ -0,0 +1,261 @@
+package volume
+
+import (
+	"runtime"
+	"strings"
+	"testing"
+)
+
+func TestParseMountSpec(t *testing.T) {
+	var (
+		valid   []string
+		invalid map[string]string
+	)
+
+	if runtime.GOOS == "windows" {
+		valid = []string{
+			`d:\`,
+			`d:`,
+			`d:\path`,
+			`d:\path with space`,
+			// TODO Windows post TP4 - readonly support `d:\pathandmode:ro`,
+			`c:\:d:\`,
+			`c:\windows\:d:`,
+			`c:\windows:d:\s p a c e`,
+			`c:\windows:d:\s p a c e:RW`,
+			`c:\program files:d:\s p a c e i n h o s t d i r`,
+			`0123456789name:d:`,
+			`MiXeDcAsEnAmE:d:`,
+			`name:D:`,
+			`name:D::rW`,
+			`name:D::RW`,
+			// TODO Windows post TP4 - readonly support `name:D::RO`,
+			`c:/:d:/forward/slashes/are/good/too`,
+			// TODO Windows post TP4 - readonly support `c:/:d:/including with/spaces:ro`,
+			`c:\Windows`,             // With capital
+			`c:\Program Files (x86)`, // With capitals and brackets
+		}
+		invalid = map[string]string{
+			``:                                 "Invalid volume specification: ",
+			`.`:                                "Invalid volume specification: ",
+			`..\`:                              "Invalid volume specification: ",
+			`c:\:..\`:                          "Invalid volume specification: ",
+			`c:\:d:\:xyzzy`:                    "Invalid volume specification: ",
+			`c:`:                               "cannot be c:",
+			`c:\`:                              `cannot be c:\`,
+			`c:\notexist:d:`:                   `The system cannot find the file specified`,
+			`c:\windows\system32\ntdll.dll:d:`: `Source 'c:\windows\system32\ntdll.dll' is not a directory`,
+			`name<:d:`:                         `Invalid volume specification`,
+			`name>:d:`:                         `Invalid volume specification`,
+			`name::d:`:                         `Invalid volume specification`,
+			`name":d:`:                         `Invalid volume specification`,
+			`name\:d:`:                         `Invalid volume specification`,
+			`name*:d:`:                         `Invalid volume specification`,
+			`name|:d:`:                         `Invalid volume specification`,
+			`name?:d:`:                         `Invalid volume specification`,
+			`name/:d:`:                         `Invalid volume specification`,
+			`d:\pathandmode:rw`:                `Invalid volume specification`,
+			`con:d:`:                           `cannot be a reserved word for Windows filenames`,
+			`PRN:d:`:                           `cannot be a reserved word for Windows filenames`,
+			`aUx:d:`:                           `cannot be a reserved word for Windows filenames`,
+			`nul:d:`:                           `cannot be a reserved word for Windows filenames`,
+			`com1:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com2:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com3:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com4:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com5:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com6:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com7:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com8:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`com9:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt1:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt2:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt3:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt4:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt5:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt6:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt7:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt8:d:`:                          `cannot be a reserved word for Windows filenames`,
+			`lpt9:d:`:                          `cannot be a reserved word for Windows filenames`,
+		}
+
+	} else {
+		valid = []string{
+			"/home",
+			"/home:/home",
+			"/home:/something/else",
+			"/with space",
+			"/home:/with space",
+			"relative:/absolute-path",
+			"hostPath:/containerPath:ro",
+			"/hostPath:/containerPath:rw",
+			"/rw:/ro",
+		}
+		invalid = map[string]string{
+			"":                "Invalid volume specification",
+			"./":              "Invalid volume destination",
+			"../":             "Invalid volume destination",
+			"/:../":           "Invalid volume destination",
+			"/:path":          "Invalid volume destination",
+			":":               "Invalid volume specification",
+			"/tmp:":           "Invalid volume destination",
+			":test":           "Invalid volume specification",
+			":/test":          "Invalid volume specification",
+			"tmp:":            "Invalid volume destination",
+			":test:":          "Invalid volume specification",
+			"::":              "Invalid volume specification",
+			":::":             "Invalid volume specification",
+			"/tmp:::":         "Invalid volume specification",
+			":/tmp::":         "Invalid volume specification",
+			"/path:rw":        "Invalid volume specification",
+			"/path:ro":        "Invalid volume specification",
+			"/rw:rw":          "Invalid volume specification",
+			"path:ro":         "Invalid volume specification",
+			"/path:/path:sw":  "invalid mode: sw",
+			"/path:/path:rwz": "invalid mode: rwz",
+		}
+	}
+
+	for _, path := range valid {
+		if _, err := ParseMountSpec(path, "local"); err != nil {
+			t.Fatalf("ParseMountSpec(`%q`) should succeed: error %q", path, err)
+		}
+	}
+
+	for path, expectedError := range invalid {
+		if _, err := ParseMountSpec(path, "local"); err == nil {
+			t.Fatalf("ParseMountSpec(`%q`) should have failed validation. Err %v", path, err)
+		} else {
+			if !strings.Contains(err.Error(), expectedError) {
+				t.Fatalf("ParseMountSpec(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
+			}
+		}
+	}
+}
+
+func TestSplitN(t *testing.T) {
+	for _, x := range []struct {
+		input    string
+		n        int
+		expected []string
+	}{
+		{`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
+		{`:C:\foo:d:`, -1, nil},
+		{`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
+		{`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
+		{`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
+
+		{`d:\`, -1, []string{`d:\`}},
+		{`d:`, -1, []string{`d:`}},
+		{`d:\path`, -1, []string{`d:\path`}},
+		{`d:\path with space`, -1, []string{`d:\path with space`}},
+		{`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
+		{`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
+		{`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
+		{`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
+		{`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
+		{`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
+		{`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
+		{`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
+		{`name:D:`, -1, []string{`name`, `D:`}},
+		{`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
+		{`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
+		{`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
+		{`c:\Windows`, -1, []string{`c:\Windows`}},
+		{`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
+
+		{``, -1, nil},
+		{`.`, -1, []string{`.`}},
+		{`..\`, -1, []string{`..\`}},
+		{`c:\:..\`, -1, []string{`c:\`, `..\`}},
+		{`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
+	} {
+		res := SplitN(x.input, x.n)
+		if len(res) < len(x.expected) {
+			t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
+		}
+		for i, e := range res {
+			if e != x.expected[i] {
+				t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
+			}
+		}
+	}
+}
+
+// testParseMountSpec is a structure used by TestParseMountSpecSplit for
+// specifying test cases for the ParseMountSpec() function.
+type testParseMountSpec struct {
+	bind      string
+	driver    string
+	expDest   string
+	expSource string
+	expName   string
+	expDriver string
+	expRW     bool
+	fail      bool
+}
+
+func TestParseMountSpecSplit(t *testing.T) {
+	var cases []testParseMountSpec
+	if runtime.GOOS == "windows" {
+		cases = []testParseMountSpec{
+			{`c:\:d:`, "local", `d:`, `c:\`, ``, "", true, false},
+			{`c:\:d:\`, "local", `d:\`, `c:\`, ``, "", true, false},
+			// TODO Windows post TP4 - Add readonly support {`c:\:d:\:ro`, "local", `d:\`, `c:\`, ``, "", false, false},
+			{`c:\:d:\:rw`, "local", `d:\`, `c:\`, ``, "", true, false},
+			{`c:\:d:\:foo`, "local", `d:\`, `c:\`, ``, "", false, true},
+			{`name:d::rw`, "local", `d:`, ``, `name`, "local", true, false},
+			{`name:d:`, "local", `d:`, ``, `name`, "local", true, false},
+			// TODO Windows post TP4 - Add readonly support {`name:d::ro`, "local", `d:`, ``, `name`, "local", false, false},
+			{`name:c:`, "", ``, ``, ``, "", true, true},
+			{`driver/name:c:`, "", ``, ``, ``, "", true, true},
+		}
+	} else {
+		cases = []testParseMountSpec{
+			{"/tmp:/tmp1", "", "/tmp1", "/tmp", "", "", true, false},
+			{"/tmp:/tmp2:ro", "", "/tmp2", "/tmp", "", "", false, false},
+			{"/tmp:/tmp3:rw", "", "/tmp3", "/tmp", "", "", true, false},
+			{"/tmp:/tmp4:foo", "", "", "", "", "", false, true},
+			{"name:/named1", "", "/named1", "", "name", "local", true, false},
+			{"name:/named2", "external", "/named2", "", "name", "external", true, false},
+			{"name:/named3:ro", "local", "/named3", "", "name", "local", false, false},
+			{"local/name:/tmp:rw", "", "/tmp", "", "local/name", "local", true, false},
+			{"/tmp:tmp", "", "", "", "", "", true, true},
+		}
+	}
+
+	for _, c := range cases {
+		m, err := ParseMountSpec(c.bind, c.driver)
+		if c.fail {
+			if err == nil {
+				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
+			}
+			continue
+		}
+
+		if m == nil || err != nil {
+			t.Fatalf("ParseMountSpec failed for spec %s driver %s error %v\n", c.bind, c.driver, err.Error())
+			continue
+		}
+
+		if m.Destination != c.expDest {
+			t.Fatalf("Expected destination %s, was %s, for spec %s\n", c.expDest, m.Destination, c.bind)
+		}
+
+		if m.Source != c.expSource {
+			t.Fatalf("Expected source %s, was %s, for spec %s\n", c.expSource, m.Source, c.bind)
+		}
+
+		if m.Name != c.expName {
+			t.Fatalf("Expected name %s, was %s for spec %s\n", c.expName, m.Name, c.bind)
+		}
+
+		if m.Driver != c.expDriver {
+			t.Fatalf("Expected driver %s, was %s, for spec %s\n", c.expDriver, m.Driver, c.bind)
+		}
+
+		if m.RW != c.expRW {
+			t.Fatalf("Expected RW %v, was %v for spec %s\n", c.expRW, m.RW, c.bind)
+		}
+	}
+}
diff --git a/volume/volume_unix.go b/volume/volume_unix.go
new file mode 100644
index 0000000000..8c98a3d95a
--- /dev/null
+++ b/volume/volume_unix.go
@@ -0,0 +1,132 @@
+// +build linux freebsd darwin
+
+package volume
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	derr "github.com/docker/docker/errors"
+)
+
+// read-write modes
+var rwModes = map[string]bool{
+	"rw":   true,
+	"rw,Z": true,
+	"rw,z": true,
+	"z,rw": true,
+	"Z,rw": true,
+	"Z":    true,
+	"z":    true,
+}
+
+// read-only modes
+var roModes = map[string]bool{
+	"ro":   true,
+	"ro,Z": true,
+	"ro,z": true,
+	"z,ro": true,
+	"Z,ro": true,
+}
+
+// BackwardsCompatible decides whether this mount point can be
+// used in old versions of Docker or not.
+// Only bind mounts and local volumes can be used in old versions of Docker.
+func (m *MountPoint) BackwardsCompatible() bool {
+	return len(m.Source) > 0 || m.Driver == DefaultDriverName
+}
+
+// HasResource checks whether the given absolute path for a container is in
+// this mount point. If the relative path starts with `../` then the resource
+// is outside of this mount point, but we can't simply check for this prefix
+// because it misses `..` which is also outside of the mount, so check both.
+func (m *MountPoint) HasResource(absolutePath string) bool {
+	relPath, err := filepath.Rel(m.Destination, absolutePath)
+	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
+}
+
+// ParseMountSpec validates the configuration of mount information is valid.
+func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
+	spec = filepath.ToSlash(spec)
+
+	mp := &MountPoint{
+		RW: true,
+	}
+	if strings.Count(spec, ":") > 2 {
+		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+	}
+
+	arr := strings.SplitN(spec, ":", 3)
+	if arr[0] == "" {
+		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+	}
+
+	switch len(arr) {
+	case 1:
+		// Just a destination path in the container
+		mp.Destination = filepath.Clean(arr[0])
+	case 2:
+		if isValid := ValidMountMode(arr[1]); isValid {
+			// Destination + Mode is not a valid volume - volumes
+			// cannot include a mode. eg /foo:rw
+			return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+		}
+		// Host Source Path or Name + Destination
+		mp.Source = arr[0]
+		mp.Destination = arr[1]
+	case 3:
+		// HostSourcePath+DestinationPath+Mode
+		mp.Source = arr[0]
+		mp.Destination = arr[1]
+		mp.Mode = arr[2] // Mode field is used by SELinux to decide whether to apply label
+		if !ValidMountMode(mp.Mode) {
+			return nil, derr.ErrorCodeVolumeInvalidMode.WithArgs(mp.Mode)
+		}
+		mp.RW = ReadWrite(mp.Mode)
+	default:
+		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+	}
+
+	//validate the volumes destination path
+	mp.Destination = filepath.Clean(mp.Destination)
+	if !filepath.IsAbs(mp.Destination) {
+		return nil, derr.ErrorCodeVolumeAbs.WithArgs(mp.Destination)
+	}
+
+	// Destination cannot be "/"
+	if mp.Destination == "/" {
+		return nil, derr.ErrorCodeVolumeSlash.WithArgs(spec)
+	}
+
+	name, source := ParseVolumeSource(mp.Source)
+	if len(source) == 0 {
+		mp.Source = "" // Clear it out as we previously assumed it was not a name
+		mp.Driver = volumeDriver
+		if len(mp.Driver) == 0 {
+			mp.Driver = DefaultDriverName
+		}
+	} else {
+		mp.Source = filepath.Clean(source)
+	}
+
+	mp.Name = name
+
+	return mp, nil
+}
+
+// ParseVolumeSource parses the origin sources that's mounted into the container.
+// It returns a name and a source. It looks to see if the spec passed in
+// is an absolute file. If it is, it assumes the spec is a source. If not,
+// it assumes the spec is a name.
+func ParseVolumeSource(spec string) (string, string) {
+	if !filepath.IsAbs(spec) {
+		return spec, ""
+	}
+	return "", spec
+}
+
+// IsVolumeNameValid checks a volume name in a platform specific manner.
+func IsVolumeNameValid(name string) (bool, error) {
+	return true, nil
+}
diff --git a/volume/volume_windows.go b/volume/volume_windows.go
new file mode 100644
index 0000000000..b6b3bbb0fe
--- /dev/null
+++ b/volume/volume_windows.go
@@ -0,0 +1,181 @@
+package volume
+
+import (
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+
+	"github.com/Sirupsen/logrus"
+	derr "github.com/docker/docker/errors"
+)
+
+// read-write modes
+var rwModes = map[string]bool{
+	"rw": true,
+}
+
+// read-only modes
+var roModes = map[string]bool{
+	"ro": true,
+}
+
+const (
+	// Spec should be in the format [source:]destination[:mode]
+	//
+	// Examples: c:\foo bar:d:rw
+	//           c:\foo:d:\bar
+	//           myname:d:
+	//           d:\
+	//
+	// Explanation of this regex! Thanks @thaJeztah on IRC and gist for help. See
+	// https://gist.github.com/thaJeztah/6185659e4978789fb2b2. A good place to
+	// test is https://regex-golang.appspot.com/assets/html/index.html
+	//
+	// Useful link for referencing named capturing groups:
+	// http://stackoverflow.com/questions/20750843/using-named-matches-from-go-regex
+	//
+	// There are three match groups: source, destination and mode.
+	//
+
+	// RXHostDir is the first option of a source
+	RXHostDir = `[a-z]:\\(?:[^\\/:*?"<>|\r\n]+\\?)*`
+	// RXName is the second option of a source
+	RXName = `[^\\/:*?"<>|\r\n]+`
+	// RXReservedNames are reserved names not possible on Windows
+	RXReservedNames = `(con)|(prn)|(nul)|(aux)|(com[1-9])|(lpt[1-9])`
+
+	// RXSource is the combined possiblities for a source
+	RXSource = `((?P<source>((` + RXHostDir + `)|(` + RXName + `))):)?`
+
+	// Source. Can be either a host directory, a name, or omitted:
+	//  HostDir:
+	//    -  Essentially using the folder solution from
+	//       https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch08s18.html
+	//       but adding case insensitivity.
+	//    -  Must be an absolute path such as c:\path
+	//    -  Can include spaces such as `c:\program files`
+	//    -  And then followed by a colon which is not in the capture group
+	//    -  And can be optional
+	//  Name:
+	//    -  Must not contain invalid NTFS filename characters (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx)
+	//    -  And then followed by a colon which is not in the capture group
+	//    -  And can be optional
+
+	// RXDestination is the regex expression for the mount destination
+	RXDestination = `(?P<destination>([a-z]):((?:\\[^\\/:*?"<>\r\n]+)*\\?))`
+	// Destination (aka container path):
+	//    -  Variation on hostdir but can be a drive followed by colon as well
+	//    -  If a path, must be absolute. Can include spaces
+	//    -  Drive cannot be c: (explicitly checked in code, not RegEx)
+	//
+
+	// RXMode is the regex expression for the mode of the mount
+	RXMode = `(:(?P<mode>(?i)rw))?`
+	// Temporarily for TP4, disabling the use of ro as it's not supported yet
+	// in the platform. TODO Windows: `(:(?P<mode>(?i)ro|rw))?`
+	// mode (optional)
+	//    -  Hopefully self explanatory in comparison to above.
+	//    -  Colon is not in the capture group
+	//
+)
+
+// ParseMountSpec validates the configuration of mount information is valid.
+func ParseMountSpec(spec string, volumeDriver string) (*MountPoint, error) {
+	var specExp = regexp.MustCompile(`^` + RXSource + RXDestination + RXMode + `$`)
+
+	// Ensure in platform semantics for matching. The CLI will send in Unix semantics.
+	match := specExp.FindStringSubmatch(filepath.FromSlash(strings.ToLower(spec)))
+
+	// Must have something back
+	if len(match) == 0 {
+		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+	}
+
+	// Pull out the sub expressions from the named capture groups
+	matchgroups := make(map[string]string)
+	for i, name := range specExp.SubexpNames() {
+		matchgroups[name] = strings.ToLower(match[i])
+	}
+
+	mp := &MountPoint{
+		Source:      matchgroups["source"],
+		Destination: matchgroups["destination"],
+		RW:          true,
+	}
+	if strings.ToLower(matchgroups["mode"]) == "ro" {
+		mp.RW = false
+	}
+
+	// Volumes cannot include an explicitly supplied mode eg c:\path:rw
+	if mp.Source == "" && mp.Destination != "" && matchgroups["mode"] != "" {
+		return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
+	}
+
+	// Note: No need to check if destination is absolute as it must be by
+	// definition of matching the regex.
+
+	if filepath.VolumeName(mp.Destination) == mp.Destination {
+		// Ensure the destination path, if a drive letter, is not the c drive
+		if strings.ToLower(mp.Destination) == "c:" {
+			return nil, derr.ErrorCodeVolumeDestIsC.WithArgs(spec)
+		}
+	} else {
+		// So we know the destination is a path, not drive letter. Clean it up.
+		mp.Destination = filepath.Clean(mp.Destination)
+		// Ensure the destination path, if a path, is not the c root directory
+		if strings.ToLower(mp.Destination) == `c:\` {
+			return nil, derr.ErrorCodeVolumeDestIsCRoot.WithArgs(spec)
+		}
+	}
+
+	// See if the source is a name instead of a host directory
+	if len(mp.Source) > 0 {
+		validName, err := IsVolumeNameValid(mp.Source)
+		if err != nil {
+			return nil, err
+		}
+		if validName {
+			// OK, so the source is a name.
+			mp.Name = mp.Source
+			mp.Source = ""
+
+			// Set the driver accordingly
+			mp.Driver = volumeDriver
+			if len(mp.Driver) == 0 {
+				mp.Driver = DefaultDriverName
+			}
+		} else {
+			// OK, so the source must be a host directory. Make sure it's clean.
+			mp.Source = filepath.Clean(mp.Source)
+		}
+	}
+
+	// Ensure the host path source, if supplied, exists and is a directory
+	if len(mp.Source) > 0 {
+		var fi os.FileInfo
+		var err error
+		if fi, err = os.Stat(mp.Source); err != nil {
+			return nil, derr.ErrorCodeVolumeSourceNotFound.WithArgs(mp.Source, err)
+		}
+		if !fi.IsDir() {
+			return nil, derr.ErrorCodeVolumeSourceNotDirectory.WithArgs(mp.Source)
+		}
+	}
+
+	logrus.Debugf("MP: Source '%s', Dest '%s', RW %t, Name '%s', Driver '%s'", mp.Source, mp.Destination, mp.RW, mp.Name, mp.Driver)
+	return mp, nil
+}
+
+// IsVolumeNameValid checks a volume name in a platform specific manner.
+func IsVolumeNameValid(name string) (bool, error) {
+	nameExp := regexp.MustCompile(`^` + RXName + `$`)
+	if !nameExp.MatchString(name) {
+		return false, nil
+	}
+	nameExp = regexp.MustCompile(`^` + RXReservedNames + `$`)
+	if nameExp.MatchString(name) {
+		return false, derr.ErrorCodeVolumeNameReservedWord.WithArgs(name)
+	}
+	return true, nil
+}