Browse Source

Merge branch 'master' into b

Nirmal Mehta 9 years ago
parent
commit
59bb86a964
100 changed files with 2144 additions and 676 deletions
  1. 1 1
      Dockerfile.ppc64le
  2. 6 0
      MAINTAINERS
  3. 9 7
      api/client/attach.go
  4. 17 15
      api/client/build.go
  5. 13 11
      api/client/cp.go
  6. 10 10
      api/client/create.go
  7. 8 6
      api/client/exec.go
  8. 0 1
      api/client/export.go
  9. 12 10
      api/client/inspect.go
  10. 4 2
      api/client/login.go
  11. 3 1
      api/client/logout.go
  12. 4 2
      api/client/logs.go
  13. 6 2
      api/client/network.go
  14. 3 1
      api/client/pause.go
  15. 7 5
      api/client/pull.go
  16. 9 6
      api/client/push.go
  17. 5 3
      api/client/rm.go
  18. 3 1
      api/client/rmi.go
  19. 14 13
      api/client/run.go
  20. 28 4
      api/client/search.go
  21. 13 12
      api/client/start.go
  22. 8 6
      api/client/stats.go
  23. 2 2
      api/client/stats_helpers.go
  24. 3 1
      api/client/stop.go
  25. 9 9
      api/client/trust.go
  26. 3 1
      api/client/unpause.go
  27. 4 1
      api/client/update.go
  28. 17 17
      api/client/utils.go
  29. 6 2
      api/client/volume.go
  30. 3 1
      api/client/wait.go
  31. 1 1
      api/server/router/image/backend.go
  32. 1 1
      api/server/router/image/image_routes.go
  33. 1 1
      builder/dockerfile/dispatchers.go
  34. 95 0
      builder/dockerfile/evaluator_test.go
  35. 22 12
      builder/dockerfile/internals.go
  36. 55 0
      builder/dockerfile/internals_test.go
  37. 26 29
      cli/cli.go
  38. 0 5
      cliconfig/config.go
  39. 3 2
      cmd/dockerd/daemon.go
  40. 74 0
      cmd/dockerd/daemon_solaris.go
  41. 16 1
      cmd/dockerd/daemon_unix.go
  42. 5 0
      cmd/dockerd/daemon_windows.go
  43. 116 0
      cmd/dockerd/hack/malformed_host_override.go
  44. 115 0
      cmd/dockerd/hack/malformed_host_override_test.go
  45. 95 0
      container/container_solaris.go
  46. 7 0
      container/state_solaris.go
  47. 24 4
      contrib/completion/bash/docker
  48. 62 3
      contrib/completion/zsh/_docker
  49. 1 1
      daemon/config.go
  50. 39 0
      daemon/config_solaris.go
  51. 1 1
      daemon/config_unix.go
  52. 50 0
      daemon/container_operations_solaris.go
  53. 4 0
      daemon/create.go
  54. 106 286
      daemon/daemon.go
  55. 159 0
      daemon/daemon_solaris.go
  56. 3 1
      daemon/daemon_unix.go
  57. 1 1
      daemon/daemon_unsupported.go
  58. 10 0
      daemon/daemon_windows.go
  59. 1 1
      daemon/debugtrap_unsupported.go
  60. 14 0
      daemon/events.go
  61. 5 0
      daemon/events/filter.go
  62. 11 0
      daemon/exec_solaris.go
  63. 8 0
      daemon/graphdriver/aufs/aufs.go
  64. 47 12
      daemon/graphdriver/counter.go
  65. 10 10
      daemon/graphdriver/devmapper/driver.go
  66. 6 0
      daemon/graphdriver/driver.go
  67. 32 0
      daemon/graphdriver/driver_linux.go
  68. 65 0
      daemon/graphdriver/driver_solaris.go
  69. 1 1
      daemon/graphdriver/driver_unsupported.go
  70. 0 2
      daemon/graphdriver/driver_windows.go
  71. 28 78
      daemon/graphdriver/overlay/overlay.go
  72. 1 1
      daemon/graphdriver/register/register_zfs.go
  73. 34 44
      daemon/graphdriver/windows/windows.go
  74. 9 9
      daemon/graphdriver/zfs/zfs.go
  75. 59 0
      daemon/graphdriver/zfs/zfs_solaris.go
  76. 1 1
      daemon/graphdriver/zfs/zfs_unsupported.go
  77. 124 0
      daemon/image.go
  78. 25 0
      daemon/image_exporter.go
  79. 82 0
      daemon/image_history.go
  80. 81 0
      daemon/image_inspect.go
  81. 40 0
      daemon/inspect_solaris.go
  82. 1 1
      daemon/inspect_unix.go
  83. 1 1
      daemon/list_unix.go
  84. 18 0
      daemon/monitor_solaris.go
  85. 12 0
      daemon/oci_solaris.go
  86. 1 1
      daemon/seccomp_linux.go
  87. 34 0
      daemon/stats_collector_solaris.go
  88. 1 1
      daemon/stats_collector_unix.go
  89. 11 0
      daemon/update_solaris.go
  90. 0 4
      distribution/xfer/download_test.go
  91. 2 3
      docs/Makefile
  92. 7 0
      docs/breaking_changes.md
  93. 9 0
      docs/deprecated.md
  94. 3 0
      docs/reference/api/docker_remote_api.md
  95. 10 1
      docs/reference/api/docker_remote_api_v1.24.md
  96. 9 0
      docs/reference/builder.md
  97. 5 1
      docs/reference/commandline/build.md
  98. 1 0
      docs/reference/commandline/commit.md
  99. 11 1
      docs/reference/commandline/cp.md
  100. 2 1
      docs/reference/commandline/create.md

+ 1 - 1
Dockerfile.ppc64le

@@ -168,7 +168,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
 
 VOLUME /var/lib/docker
 WORKDIR /go/src/github.com/docker/docker
-ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
+ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
 
 # Let us use a .bashrc file
 RUN ln -sfv $PWD/.bashrc ~/.bashrc

+ 6 - 0
MAINTAINERS

@@ -35,6 +35,7 @@
 			"estesp",
 			"icecrime",
 			"jhowardmsft",
+			"justincormack",
 			"lk4d4",
 			"mavenugo",
 			"mhbauer",
@@ -204,6 +205,11 @@
 	Email = "jess@linux.com"
 	GitHub = "jfrazelle"
 
+	[people.justincormack]
+	Name = "Justin Cormack"
+	Email = "justin.cormack@docker.com"
+	GitHub = "justincormack"
+
 	[people.lk4d4]
 	Name = "Alexander Morozov"
 	Email = "lk4d4@docker.com"

+ 9 - 7
api/client/attach.go

@@ -27,7 +27,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
-	c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
+	ctx := context.Background()
+
+	c, err := cli.client.ContainerInspect(ctx, cmd.Arg(0))
 	if err != nil {
 		return err
 	}
@@ -64,11 +66,11 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 	}
 
 	if *proxy && !c.Config.Tty {
-		sigc := cli.forwardAllSignals(container)
+		sigc := cli.forwardAllSignals(ctx, container)
 		defer signal.StopCatch(sigc)
 	}
 
-	resp, errAttach := cli.client.ContainerAttach(context.Background(), container, options)
+	resp, errAttach := cli.client.ContainerAttach(ctx, container, options)
 	if errAttach != nil && errAttach != httputil.ErrPersistEOF {
 		// ContainerAttach returns an ErrPersistEOF (connection closed)
 		// means server met an error and put it in Hijacked connection
@@ -83,15 +85,15 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
 		// resize it, then go back to normal. Without this, every attach after the first will
 		// require the user to manually resize or hit enter.
-		cli.resizeTtyTo(cmd.Arg(0), height+1, width+1, false)
+		cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false)
 
 		// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
 		// to the actual size.
-		if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
+		if err := cli.monitorTtySize(ctx, cmd.Arg(0), false); err != nil {
 			logrus.Debugf("Error monitoring TTY size: %s", err)
 		}
 	}
-	if err := cli.holdHijackedConnection(context.Background(), c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
+	if err := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
 		return err
 	}
 
@@ -99,7 +101,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return errAttach
 	}
 
-	_, status, err := getExitCode(cli, container)
+	_, status, err := cli.getExitCode(ctx, container)
 	if err != nil {
 		return err
 	}

+ 17 - 15
api/client/build.go

@@ -32,7 +32,7 @@ import (
 	"github.com/docker/go-units"
 )
 
-type translatorFunc func(reference.NamedTagged) (reference.Canonical, error)
+type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
 
 // CmdBuild builds a new image from the source code at a given path.
 //
@@ -77,8 +77,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	cmd.ParseFlags(args, true)
 
 	var (
-		ctx io.ReadCloser
-		err error
+		buildCtx io.ReadCloser
+		err      error
 	)
 
 	specifiedContext := cmd.Arg(0)
@@ -100,11 +100,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 
 	switch {
 	case specifiedContext == "-":
-		ctx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
+		buildCtx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
 	case urlutil.IsGitURL(specifiedContext):
 		tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, *dockerfileName)
 	case urlutil.IsURL(specifiedContext):
-		ctx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
+		buildCtx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
 	default:
 		contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, *dockerfileName)
 	}
@@ -121,7 +121,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		contextDir = tempDir
 	}
 
-	if ctx == nil {
+	if buildCtx == nil {
 		// And canonicalize dockerfile name to a platform-independent one
 		relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
 		if err != nil {
@@ -159,7 +159,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 			includes = append(includes, ".dockerignore", relDockerfile)
 		}
 
-		ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
+		buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
 			Compression:     archive.Uncompressed,
 			ExcludePatterns: excludes,
 			IncludeFiles:    includes,
@@ -169,17 +169,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		}
 	}
 
+	ctx := context.Background()
+
 	var resolvedTags []*resolvedTag
 	if isTrusted() {
 		// Wrap the tar archive to replace the Dockerfile entry with the rewritten
 		// Dockerfile which uses trusted pulls.
-		ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
+		buildCtx = replaceDockerfileTarWrapper(ctx, buildCtx, relDockerfile, cli.trustedReference, &resolvedTags)
 	}
 
 	// Setup an upload progress bar
 	progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
 
-	var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
+	var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
 
 	var memory int64
 	if *flMemoryString != "" {
@@ -235,7 +237,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		Labels:         runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
 	}
 
-	response, err := cli.client.ImageBuild(context.Background(), body, options)
+	response, err := cli.client.ImageBuild(ctx, body, options)
 	if err != nil {
 		return err
 	}
@@ -271,7 +273,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		// Since the build was successful, now we must tag any of the resolved
 		// images from the above Dockerfile rewrite.
 		for _, resolved := range resolvedTags {
-			if err := cli.tagTrusted(resolved.digestRef, resolved.tagRef); err != nil {
+			if err := cli.tagTrusted(ctx, resolved.digestRef, resolved.tagRef); err != nil {
 				return err
 			}
 		}
@@ -303,7 +305,7 @@ type resolvedTag struct {
 // "FROM <image>" instructions to a digest reference. `translator` is a
 // function that takes a repository name and tag reference and returns a
 // trusted digest reference.
-func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
+func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
 	scanner := bufio.NewScanner(dockerfile)
 	buf := bytes.NewBuffer(nil)
 
@@ -320,7 +322,7 @@ func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (new
 			}
 			ref = reference.WithDefaultTag(ref)
 			if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
-				trustedRef, err := translator(ref)
+				trustedRef, err := translator(ctx, ref)
 				if err != nil {
 					return nil, nil, err
 				}
@@ -346,7 +348,7 @@ func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (new
 // replaces the entry with the given Dockerfile name with the contents of the
 // new Dockerfile. Returns a new tar archive stream with the replaced
 // Dockerfile.
-func replaceDockerfileTarWrapper(inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
+func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
 	pipeReader, pipeWriter := io.Pipe()
 	go func() {
 		tarReader := tar.NewReader(inputTarStream)
@@ -373,7 +375,7 @@ func replaceDockerfileTarWrapper(inputTarStream io.ReadCloser, dockerfileName st
 				// generated from a directory on the local filesystem, the
 				// Dockerfile will only appear once in the archive.
 				var newDockerfile []byte
-				newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(content, translator)
+				newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(ctx, content, translator)
 				if err != nil {
 					pipeWriter.CloseWithError(err)
 					return

+ 13 - 11
api/client/cp.go

@@ -81,11 +81,13 @@ func (cli *DockerCli) CmdCp(args ...string) error {
 		followLink: *followLink,
 	}
 
+	ctx := context.Background()
+
 	switch direction {
 	case fromContainer:
-		return cli.copyFromContainer(srcContainer, srcPath, dstPath, cpParam)
+		return cli.copyFromContainer(ctx, srcContainer, srcPath, dstPath, cpParam)
 	case toContainer:
-		return cli.copyToContainer(srcPath, dstContainer, dstPath, cpParam)
+		return cli.copyToContainer(ctx, srcPath, dstContainer, dstPath, cpParam)
 	case acrossContainers:
 		// Copying between containers isn't supported.
 		return fmt.Errorf("copying between containers is not supported")
@@ -126,8 +128,8 @@ func splitCpArg(arg string) (container, path string) {
 	return parts[0], parts[1]
 }
 
-func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
-	return cli.client.ContainerStatPath(context.Background(), containerName, path)
+func (cli *DockerCli) statContainerPath(ctx context.Context, containerName, path string) (types.ContainerPathStat, error) {
+	return cli.client.ContainerStatPath(ctx, containerName, path)
 }
 
 func resolveLocalPath(localPath string) (absPath string, err error) {
@@ -138,7 +140,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
 	return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
 }
 
-func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
+func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
 	if dstPath != "-" {
 		// Get an absolute destination path.
 		dstPath, err = resolveLocalPath(dstPath)
@@ -150,7 +152,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
 	// if client requests to follow symbol link, then must decide target file to be copied
 	var rebaseName string
 	if cpParam.followLink {
-		srcStat, err := cli.statContainerPath(srcContainer, srcPath)
+		srcStat, err := cli.statContainerPath(ctx, srcContainer, srcPath)
 
 		// If the destination is a symbolic link, we should follow it.
 		if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
@@ -167,7 +169,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
 
 	}
 
-	content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
+	content, stat, err := cli.client.CopyFromContainer(ctx, srcContainer, srcPath)
 	if err != nil {
 		return err
 	}
@@ -199,7 +201,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
 	return archive.CopyTo(preArchive, srcInfo, dstPath)
 }
 
-func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
+func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
 	if srcPath != "-" {
 		// Get an absolute source path.
 		srcPath, err = resolveLocalPath(srcPath)
@@ -215,7 +217,7 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
 
 	// Prepare destination copy info by stat-ing the container path.
 	dstInfo := archive.CopyInfo{Path: dstPath}
-	dstStat, err := cli.statContainerPath(dstContainer, dstPath)
+	dstStat, err := cli.statContainerPath(ctx, dstContainer, dstPath)
 
 	// If the destination is a symbolic link, we should evaluate it.
 	if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
@@ -227,7 +229,7 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
 		}
 
 		dstInfo.Path = linkTarget
-		dstStat, err = cli.statContainerPath(dstContainer, linkTarget)
+		dstStat, err = cli.statContainerPath(ctx, dstContainer, linkTarget)
 	}
 
 	// Ignore any error and assume that the parent directory of the destination
@@ -291,5 +293,5 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
 		AllowOverwriteDirWithFile: false,
 	}
 
-	return cli.client.CopyToContainer(context.Background(), dstContainer, resolvedDstPath, content, options)
+	return cli.client.CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
 }

+ 10 - 10
api/client/create.go

@@ -19,7 +19,7 @@ import (
 	networktypes "github.com/docker/engine-api/types/network"
 )
 
-func (cli *DockerCli) pullImage(image string, out io.Writer) error {
+func (cli *DockerCli) pullImage(ctx context.Context, image string, out io.Writer) error {
 	ref, err := reference.ParseNamed(image)
 	if err != nil {
 		return err
@@ -31,7 +31,7 @@ func (cli *DockerCli) pullImage(image string, out io.Writer) error {
 		return err
 	}
 
-	authConfig := cli.resolveAuthConfig(repoInfo.Index)
+	authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
 	encodedAuth, err := encodeAuthToBase64(authConfig)
 	if err != nil {
 		return err
@@ -41,7 +41,7 @@ func (cli *DockerCli) pullImage(image string, out io.Writer) error {
 		RegistryAuth: encodedAuth,
 	}
 
-	responseBody, err := cli.client.ImageCreate(context.Background(), image, options)
+	responseBody, err := cli.client.ImageCreate(ctx, image, options)
 	if err != nil {
 		return err
 	}
@@ -69,7 +69,7 @@ func newCIDFile(path string) (*cidFile, error) {
 	return &cidFile{path: path, file: f}, nil
 }
 
-func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
+func (cli *DockerCli) createContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
 	var containerIDFile *cidFile
 	if cidfile != "" {
 		var err error
@@ -89,7 +89,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
 
 		if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
 			var err error
-			trustedRef, err = cli.trustedReference(ref)
+			trustedRef, err = cli.trustedReference(ctx, ref)
 			if err != nil {
 				return nil, err
 			}
@@ -98,7 +98,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
 	}
 
 	//create the container
-	response, err := cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
+	response, err := cli.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
 
 	//if image not found try to pull it
 	if err != nil {
@@ -106,17 +106,17 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
 			fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
 
 			// we don't want to write to stdout anything apart from container.ID
-			if err = cli.pullImage(config.Image, cli.err); err != nil {
+			if err = cli.pullImage(ctx, config.Image, cli.err); err != nil {
 				return nil, err
 			}
 			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
-				if err := cli.tagTrusted(trustedRef, ref); err != nil {
+				if err := cli.tagTrusted(ctx, trustedRef, ref); err != nil {
 					return nil, err
 				}
 			}
 			// Retry
 			var retryErr error
-			response, retryErr = cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
+			response, retryErr = cli.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
 			if retryErr != nil {
 				return nil, retryErr
 			}
@@ -158,7 +158,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
 		cmd.Usage()
 		return nil
 	}
-	response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
+	response, err := cli.createContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
 	if err != nil {
 		return err
 	}

+ 8 - 6
api/client/exec.go

@@ -34,7 +34,9 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 	// Send client escape keys
 	execConfig.DetachKeys = cli.configFile.DetachKeys
 
-	response, err := cli.client.ContainerExecCreate(context.Background(), container, *execConfig)
+	ctx := context.Background()
+
+	response, err := cli.client.ContainerExecCreate(ctx, container, *execConfig)
 	if err != nil {
 		return err
 	}
@@ -56,7 +58,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 			Tty:    execConfig.Tty,
 		}
 
-		if err := cli.client.ContainerExecStart(context.Background(), execID, execStartCheck); err != nil {
+		if err := cli.client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
 			return err
 		}
 		// For now don't print this - wait for when we support exec wait()
@@ -85,17 +87,17 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 		}
 	}
 
-	resp, err := cli.client.ContainerExecAttach(context.Background(), execID, *execConfig)
+	resp, err := cli.client.ContainerExecAttach(ctx, execID, *execConfig)
 	if err != nil {
 		return err
 	}
 	defer resp.Close()
 	errCh = promise.Go(func() error {
-		return cli.holdHijackedConnection(context.Background(), execConfig.Tty, in, out, stderr, resp)
+		return cli.holdHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
 	})
 
 	if execConfig.Tty && cli.isTerminalIn {
-		if err := cli.monitorTtySize(execID, true); err != nil {
+		if err := cli.monitorTtySize(ctx, execID, true); err != nil {
 			fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
 		}
 	}
@@ -106,7 +108,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 	}
 
 	var status int
-	if _, status, err = getExecExitCode(cli, execID); err != nil {
+	if _, status, err = cli.getExecExitCode(ctx, execID); err != nil {
 		return err
 	}
 

+ 0 - 1
api/client/export.go

@@ -38,5 +38,4 @@ func (cli *DockerCli) CmdExport(args ...string) error {
 	}
 
 	return copyToFile(*outfile, responseBody)
-
 }

+ 12 - 10
api/client/inspect.go

@@ -28,38 +28,40 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 		return fmt.Errorf("%q is not a valid value for --type", *inspectType)
 	}
 
+	ctx := context.Background()
+
 	var elementSearcher inspectSearcher
 	switch *inspectType {
 	case "container":
-		elementSearcher = cli.inspectContainers(*size)
+		elementSearcher = cli.inspectContainers(ctx, *size)
 	case "image":
-		elementSearcher = cli.inspectImages(*size)
+		elementSearcher = cli.inspectImages(ctx, *size)
 	default:
-		elementSearcher = cli.inspectAll(*size)
+		elementSearcher = cli.inspectAll(ctx, *size)
 	}
 
 	return cli.inspectElements(*tmplStr, cmd.Args(), elementSearcher)
 }
 
-func (cli *DockerCli) inspectContainers(getSize bool) inspectSearcher {
+func (cli *DockerCli) inspectContainers(ctx context.Context, getSize bool) inspectSearcher {
 	return func(ref string) (interface{}, []byte, error) {
-		return cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
+		return cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
 	}
 }
 
-func (cli *DockerCli) inspectImages(getSize bool) inspectSearcher {
+func (cli *DockerCli) inspectImages(ctx context.Context, getSize bool) inspectSearcher {
 	return func(ref string) (interface{}, []byte, error) {
-		return cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
+		return cli.client.ImageInspectWithRaw(ctx, ref, getSize)
 	}
 }
 
-func (cli *DockerCli) inspectAll(getSize bool) inspectSearcher {
+func (cli *DockerCli) inspectAll(ctx context.Context, getSize bool) inspectSearcher {
 	return func(ref string) (interface{}, []byte, error) {
-		c, rawContainer, err := cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
+		c, rawContainer, err := cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
 		if err != nil {
 			// Search for image with that id if a container doesn't exist.
 			if client.IsErrContainerNotFound(err) {
-				i, rawImage, err := cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
+				i, rawImage, err := cli.client.ImageInspectWithRaw(ctx, ref, getSize)
 				if err != nil {
 					if client.IsErrImageNotFound(err) {
 						return nil, nil, fmt.Errorf("Error: No such image or container: %s", ref)

+ 4 - 2
api/client/login.go

@@ -40,12 +40,14 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		cli.in = os.Stdin
 	}
 
+	ctx := context.Background()
+
 	var serverAddress string
 	var isDefaultRegistry bool
 	if len(cmd.Args()) > 0 {
 		serverAddress = cmd.Arg(0)
 	} else {
-		serverAddress = cli.electAuthServer()
+		serverAddress = cli.electAuthServer(ctx)
 		isDefaultRegistry = true
 	}
 
@@ -54,7 +56,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		return err
 	}
 
-	response, err := cli.client.RegistryLogin(context.Background(), authConfig)
+	response, err := cli.client.RegistryLogin(ctx, authConfig)
 	if err != nil {
 		return err
 	}

+ 3 - 1
api/client/logout.go

@@ -3,6 +3,8 @@ package client
 import (
 	"fmt"
 
+	"golang.org/x/net/context"
+
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
@@ -22,7 +24,7 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
 	if len(cmd.Args()) > 0 {
 		serverAddress = cmd.Arg(0)
 	} else {
-		serverAddress = cli.electAuthServer()
+		serverAddress = cli.electAuthServer(context.Background())
 	}
 
 	// check if we're logged in based on the records in the config file

+ 4 - 2
api/client/logs.go

@@ -33,7 +33,9 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 
 	name := cmd.Arg(0)
 
-	c, err := cli.client.ContainerInspect(context.Background(), name)
+	ctx := context.Background()
+
+	c, err := cli.client.ContainerInspect(ctx, name)
 	if err != nil {
 		return err
 	}
@@ -51,7 +53,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 		Tail:       *tail,
 		Details:    *details,
 	}
-	responseBody, err := cli.client.ContainerLogs(context.Background(), name, options)
+	responseBody, err := cli.client.ContainerLogs(ctx, name, options)
 	if err != nil {
 		return err
 	}

+ 6 - 2
api/client/network.go

@@ -104,9 +104,11 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
 		return err
 	}
 
+	ctx := context.Background()
+
 	status := 0
 	for _, net := range cmd.Args() {
-		if err := cli.client.NetworkRemove(context.Background(), net); err != nil {
+		if err := cli.client.NetworkRemove(ctx, net); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			status = 1
 			continue
@@ -239,8 +241,10 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
 		return err
 	}
 
+	ctx := context.Background()
+
 	inspectSearcher := func(name string) (interface{}, []byte, error) {
-		i, err := cli.client.NetworkInspect(context.Background(), name)
+		i, err := cli.client.NetworkInspect(ctx, name)
 		return i, nil, err
 	}
 

+ 3 - 1
api/client/pause.go

@@ -19,9 +19,11 @@ func (cli *DockerCli) CmdPause(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, name := range cmd.Args() {
-		if err := cli.client.ContainerPause(context.Background(), name); err != nil {
+		if err := cli.client.ContainerPause(ctx, name); err != nil {
 			errs = append(errs, err.Error())
 		} else {
 			fmt.Fprintf(cli.out, "%s\n", name)

+ 7 - 5
api/client/pull.go

@@ -55,18 +55,20 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 		return err
 	}
 
-	authConfig := cli.resolveAuthConfig(repoInfo.Index)
+	ctx := context.Background()
+
+	authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
 	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "pull")
 
 	if isTrusted() && !registryRef.HasDigest() {
 		// Check if tag is digest
-		return cli.trustedPull(repoInfo, registryRef, authConfig, requestPrivilege)
+		return cli.trustedPull(ctx, repoInfo, registryRef, authConfig, requestPrivilege)
 	}
 
-	return cli.imagePullPrivileged(authConfig, distributionRef.String(), requestPrivilege, *allTags)
+	return cli.imagePullPrivileged(ctx, authConfig, distributionRef.String(), requestPrivilege, *allTags)
 }
 
-func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
+func (cli *DockerCli) imagePullPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
 
 	encodedAuth, err := encodeAuthToBase64(authConfig)
 	if err != nil {
@@ -78,7 +80,7 @@ func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, ref strin
 		All:           all,
 	}
 
-	responseBody, err := cli.client.ImagePull(context.Background(), ref, options)
+	responseBody, err := cli.client.ImagePull(ctx, ref, options)
 	if err != nil {
 		return err
 	}

+ 9 - 6
api/client/push.go

@@ -33,15 +33,18 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 	if err != nil {
 		return err
 	}
-	// Resolve the Auth config relevant for this server
-	authConfig := cli.resolveAuthConfig(repoInfo.Index)
 
+	ctx := context.Background()
+
+	// Resolve the Auth config relevant for this server
+	authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
 	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
+
 	if isTrusted() {
-		return cli.trustedPush(repoInfo, ref, authConfig, requestPrivilege)
+		return cli.trustedPush(ctx, repoInfo, ref, authConfig, requestPrivilege)
 	}
 
-	responseBody, err := cli.imagePushPrivileged(authConfig, ref.String(), requestPrivilege)
+	responseBody, err := cli.imagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
 	if err != nil {
 		return err
 	}
@@ -51,7 +54,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 	return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil)
 }
 
-func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
+func (cli *DockerCli) imagePushPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
 	encodedAuth, err := encodeAuthToBase64(authConfig)
 	if err != nil {
 		return nil, err
@@ -61,5 +64,5 @@ func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, ref strin
 		PrivilegeFunc: requestPrivilege,
 	}
 
-	return cli.client.ImagePush(context.Background(), ref, options)
+	return cli.client.ImagePush(ctx, ref, options)
 }

+ 5 - 3
api/client/rm.go

@@ -23,6 +23,8 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, name := range cmd.Args() {
 		if name == "" {
@@ -30,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 		}
 		name = strings.Trim(name, "/")
 
-		if err := cli.removeContainer(name, *v, *link, *force); err != nil {
+		if err := cli.removeContainer(ctx, name, *v, *link, *force); err != nil {
 			errs = append(errs, err.Error())
 		} else {
 			fmt.Fprintf(cli.out, "%s\n", name)
@@ -42,13 +44,13 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	return nil
 }
 
-func (cli *DockerCli) removeContainer(container string, removeVolumes, removeLinks, force bool) error {
+func (cli *DockerCli) removeContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
 	options := types.ContainerRemoveOptions{
 		RemoveVolumes: removeVolumes,
 		RemoveLinks:   removeLinks,
 		Force:         force,
 	}
-	if err := cli.client.ContainerRemove(context.Background(), container, options); err != nil {
+	if err := cli.client.ContainerRemove(ctx, container, options); err != nil {
 		return err
 	}
 	return nil

+ 3 - 1
api/client/rmi.go

@@ -31,6 +31,8 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 		v.Set("noprune", "1")
 	}
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, image := range cmd.Args() {
 		options := types.ImageRemoveOptions{
@@ -38,7 +40,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 			PruneChildren: !*noprune,
 		}
 
-		dels, err := cli.client.ImageRemove(context.Background(), image, options)
+		dels, err := cli.client.ImageRemove(ctx, image, options)
 		if err != nil {
 			errs = append(errs, err.Error())
 		} else {

+ 14 - 13
api/client/run.go

@@ -147,20 +147,20 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
 	}
 
-	createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
+	ctx, cancelFun := context.WithCancel(context.Background())
+
+	createResponse, err := cli.createContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
 	if err != nil {
 		cmd.ReportError(err.Error(), true)
 		return runStartContainerErr(err)
 	}
 	if sigProxy {
-		sigc := cli.forwardAllSignals(createResponse.ID)
+		sigc := cli.forwardAllSignals(ctx, createResponse.ID)
 		defer signal.StopCatch(sigc)
 	}
 	var (
 		waitDisplayID chan struct{}
 		errCh         chan error
-		cancelFun     context.CancelFunc
-		ctx           context.Context
 	)
 	if !config.AttachStdout && !config.AttachStderr {
 		// Make this asynchronous to allow the client to write to stdin before having to read the ID
@@ -205,7 +205,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 			DetachKeys: cli.configFile.DetachKeys,
 		}
 
-		resp, errAttach := cli.client.ContainerAttach(context.Background(), createResponse.ID, options)
+		resp, errAttach := cli.client.ContainerAttach(ctx, createResponse.ID, options)
 		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
 			// ContainerAttach returns an ErrPersistEOF (connection closed)
 			// means server met an error and put it in Hijacked connection
@@ -214,7 +214,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 		defer resp.Close()
 
-		ctx, cancelFun = context.WithCancel(context.Background())
 		errCh = promise.Go(func() error {
 			errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp)
 			if errHijack == nil {
@@ -226,14 +225,16 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 
 	if *flAutoRemove {
 		defer func() {
-			if err := cli.removeContainer(createResponse.ID, true, false, true); err != nil {
+			// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
+			// and thus the container would not be removed.
+			if err := cli.removeContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
 				fmt.Fprintf(cli.err, "%v\n", err)
 			}
 		}()
 	}
 
 	//start the container
-	if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil {
+	if err := cli.client.ContainerStart(ctx, createResponse.ID); err != nil {
 		// If we have holdHijackedConnection, we should notify
 		// holdHijackedConnection we are going to exit and wait
 		// to avoid the terminal are not restored.
@@ -247,7 +248,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	}
 
 	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
-		if err := cli.monitorTtySize(createResponse.ID, false); err != nil {
+		if err := cli.monitorTtySize(ctx, createResponse.ID, false); err != nil {
 			fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
 		}
 	}
@@ -272,23 +273,23 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	if *flAutoRemove {
 		// Autoremove: wait for the container to finish, retrieve
 		// the exit code and remove the container
-		if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
+		if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
 			return runStartContainerErr(err)
 		}
-		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
+		if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
 			return err
 		}
 	} else {
 		// No Autoremove: Simply retrieve the exit code
 		if !config.Tty {
 			// In non-TTY mode, we can't detach, so we must wait for container exit
-			if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
+			if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
 				return err
 			}
 		} else {
 			// In TTY mode, there is a race: if the process dies too slowly, the state could
 			// be updated after the getExitCode call and result in the wrong exit code being reported
-			if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
+			if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
 				return err
 			}
 		}

+ 28 - 4
api/client/search.go

@@ -10,10 +10,12 @@ import (
 	"golang.org/x/net/context"
 
 	Cli "github.com/docker/docker/cli"
+	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/registry"
 	"github.com/docker/engine-api/types"
+	"github.com/docker/engine-api/types/filters"
 	registrytypes "github.com/docker/engine-api/types/registry"
 )
 
@@ -21,14 +23,32 @@ import (
 //
 // Usage: docker search [OPTIONS] TERM
 func (cli *DockerCli) CmdSearch(args ...string) error {
+	var (
+		err error
+
+		filterArgs = filters.NewArgs()
+
+		flFilter = opts.NewListOpts(nil)
+	)
+
 	cmd := Cli.Subcmd("search", []string{"TERM"}, Cli.DockerCommands["search"].Description, true)
 	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output")
-	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
-	stars := cmd.Uint([]string{"s", "-stars"}, 0, "Only displays with at least x stars")
+	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
+
+	// Deprecated since Docker 1.12 in favor of "--filter"
+	automated := cmd.Bool([]string{"#-automated"}, false, "Only show automated builds - DEPRECATED")
+	stars := cmd.Uint([]string{"s", "#-stars"}, 0, "Only displays with at least x stars - DEPRECATED")
+
 	cmd.Require(flag.Exact, 1)
 
 	cmd.ParseFlags(args, true)
 
+	for _, f := range flFilter.GetAll() {
+		if filterArgs, err = filters.ParseFlag(f, filterArgs); err != nil {
+			return err
+		}
+	}
+
 	name := cmd.Arg(0)
 	v := url.Values{}
 	v.Set("term", name)
@@ -38,7 +58,9 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 		return err
 	}
 
-	authConfig := cli.resolveAuthConfig(indexInfo)
+	ctx := context.Background()
+
+	authConfig := cli.resolveAuthConfig(ctx, indexInfo)
 	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(indexInfo, "search")
 
 	encodedAuth, err := encodeAuthToBase64(authConfig)
@@ -49,9 +71,10 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 	options := types.ImageSearchOptions{
 		RegistryAuth:  encodedAuth,
 		PrivilegeFunc: requestPrivilege,
+		Filters:       filterArgs,
 	}
 
-	unorderedResults, err := cli.client.ImageSearch(context.Background(), name, options)
+	unorderedResults, err := cli.client.ImageSearch(ctx, name, options)
 	if err != nil {
 		return err
 	}
@@ -62,6 +85,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
 	for _, res := range results {
+		// --automated and -s, --stars are deprecated since Docker 1.12
 		if (*automated && !res.IsAutomated) || (int(*stars) > res.StarCount) {
 			continue
 		}

+ 13 - 12
api/client/start.go

@@ -17,7 +17,7 @@ import (
 	"github.com/docker/engine-api/types"
 )
 
-func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
+func (cli *DockerCli) forwardAllSignals(ctx context.Context, cid string) chan os.Signal {
 	sigc := make(chan os.Signal, 128)
 	signal.CatchAll(sigc)
 	go func() {
@@ -37,7 +37,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
 				continue
 			}
 
-			if err := cli.client.ContainerKill(context.Background(), cid, sig); err != nil {
+			if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
 				logrus.Debugf("Error sending signal: %s", err)
 			}
 		}
@@ -57,6 +57,8 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx, cancelFun := context.WithCancel(context.Background())
+
 	if *attach || *openStdin {
 		// We're going to attach to a container.
 		// 1. Ensure we only have one container.
@@ -66,13 +68,13 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 
 		// 2. Attach to the container.
 		container := cmd.Arg(0)
-		c, err := cli.client.ContainerInspect(context.Background(), container)
+		c, err := cli.client.ContainerInspect(ctx, container)
 		if err != nil {
 			return err
 		}
 
 		if !c.Config.Tty {
-			sigc := cli.forwardAllSignals(container)
+			sigc := cli.forwardAllSignals(ctx, container)
 			defer signal.StopCatch(sigc)
 		}
 
@@ -94,7 +96,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			in = cli.in
 		}
 
-		resp, errAttach := cli.client.ContainerAttach(context.Background(), container, options)
+		resp, errAttach := cli.client.ContainerAttach(ctx, container, options)
 		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
 			// ContainerAttach return an ErrPersistEOF (connection closed)
 			// means server met an error and put it in Hijacked connection
@@ -102,7 +104,6 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			return errAttach
 		}
 		defer resp.Close()
-		ctx, cancelFun := context.WithCancel(context.Background())
 		cErr := promise.Go(func() error {
 			errHijack := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp)
 			if errHijack == nil {
@@ -112,7 +113,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 		})
 
 		// 3. Start the container.
-		if err := cli.client.ContainerStart(context.Background(), container); err != nil {
+		if err := cli.client.ContainerStart(ctx, container); err != nil {
 			cancelFun()
 			<-cErr
 			return err
@@ -120,14 +121,14 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 
 		// 4. Wait for attachment to break.
 		if c.Config.Tty && cli.isTerminalOut {
-			if err := cli.monitorTtySize(container, false); err != nil {
+			if err := cli.monitorTtySize(ctx, container, false); err != nil {
 				fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
 			}
 		}
 		if attchErr := <-cErr; attchErr != nil {
 			return attchErr
 		}
-		_, status, err := getExitCode(cli, container)
+		_, status, err := cli.getExitCode(ctx, container)
 		if err != nil {
 			return err
 		}
@@ -137,16 +138,16 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 	} else {
 		// We're not going to attach to anything.
 		// Start as many containers as we want.
-		return cli.startContainersWithoutAttachments(cmd.Args())
+		return cli.startContainersWithoutAttachments(ctx, cmd.Args())
 	}
 
 	return nil
 }
 
-func (cli *DockerCli) startContainersWithoutAttachments(containers []string) error {
+func (cli *DockerCli) startContainersWithoutAttachments(ctx context.Context, containers []string) error {
 	var failedContainers []string
 	for _, container := range containers {
-		if err := cli.client.ContainerStart(context.Background(), container); err != nil {
+		if err := cli.client.ContainerStart(ctx, container); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			failedContainers = append(failedContainers, container)
 		} else {

+ 8 - 6
api/client/stats.go

@@ -33,6 +33,8 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 	showAll := len(names) == 0
 	closeChan := make(chan error)
 
+	ctx := context.Background()
+
 	// monitorContainerEvents watches for container creation and removal (only
 	// used when calling `docker stats` without arguments).
 	monitorContainerEvents := func(started chan<- struct{}, c chan events.Message) {
@@ -41,7 +43,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 		options := types.EventsOptions{
 			Filters: f,
 		}
-		resBody, err := cli.client.Events(context.Background(), options)
+		resBody, err := cli.client.Events(ctx, options)
 		// Whether we successfully subscribed to events or not, we can now
 		// unblock the main goroutine.
 		close(started)
@@ -71,7 +73,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 		options := types.ContainerListOptions{
 			All: *all,
 		}
-		cs, err := cli.client.ContainerList(context.Background(), options)
+		cs, err := cli.client.ContainerList(ctx, options)
 		if err != nil {
 			closeChan <- err
 		}
@@ -79,7 +81,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 			s := &containerStats{Name: container.ID[:12]}
 			if cStats.add(s) {
 				waitFirst.Add(1)
-				go s.Collect(cli.client, !*noStream, waitFirst)
+				go s.Collect(ctx, cli.client, !*noStream, waitFirst)
 			}
 		}
 	}
@@ -96,7 +98,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 				s := &containerStats{Name: e.ID[:12]}
 				if cStats.add(s) {
 					waitFirst.Add(1)
-					go s.Collect(cli.client, !*noStream, waitFirst)
+					go s.Collect(ctx, cli.client, !*noStream, waitFirst)
 				}
 			}
 		})
@@ -105,7 +107,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 			s := &containerStats{Name: e.ID[:12]}
 			if cStats.add(s) {
 				waitFirst.Add(1)
-				go s.Collect(cli.client, !*noStream, waitFirst)
+				go s.Collect(ctx, cli.client, !*noStream, waitFirst)
 			}
 		})
 
@@ -131,7 +133,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 			s := &containerStats{Name: name}
 			if cStats.add(s) {
 				waitFirst.Add(1)
-				go s.Collect(cli.client, !*noStream, waitFirst)
+				go s.Collect(ctx, cli.client, !*noStream, waitFirst)
 			}
 		}
 

+ 2 - 2
api/client/stats_helpers.go

@@ -63,7 +63,7 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
 	return -1, false
 }
 
-func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
+func (s *containerStats) Collect(ctx context.Context, cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
 	logrus.Debugf("collecting stats for %s", s.Name)
 	var (
 		getFirst       bool
@@ -80,7 +80,7 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
 		}
 	}()
 
-	responseBody, err := cli.ContainerStats(context.Background(), s.Name, streamStats)
+	responseBody, err := cli.ContainerStats(ctx, s.Name, streamStats)
 	if err != nil {
 		s.mu.Lock()
 		s.err = err

+ 3 - 1
api/client/stop.go

@@ -22,9 +22,11 @@ func (cli *DockerCli) CmdStop(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, name := range cmd.Args() {
-		if err := cli.client.ContainerStop(context.Background(), name, *nSeconds); err != nil {
+		if err := cli.client.ContainerStop(ctx, name, *nSeconds); err != nil {
 			errs = append(errs, err.Error())
 		} else {
 			fmt.Fprintf(cli.out, "%s\n", name)

+ 9 - 9
api/client/trust.go

@@ -229,14 +229,14 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
 	}
 }
 
-func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Canonical, error) {
+func (cli *DockerCli) trustedReference(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
 	if err != nil {
 		return nil, err
 	}
 
 	// Resolve the Auth config relevant for this server
-	authConfig := cli.resolveAuthConfig(repoInfo.Index)
+	authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
 
 	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
 	if err != nil {
@@ -262,14 +262,14 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can
 	return reference.WithDigest(ref, r.digest)
 }
 
-func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.NamedTagged) error {
+func (cli *DockerCli) tagTrusted(ctx context.Context, trustedRef reference.Canonical, ref reference.NamedTagged) error {
 	fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
 
 	options := types.ImageTagOptions{
 		Force: true,
 	}
 
-	return cli.client.ImageTag(context.Background(), trustedRef.String(), ref.String(), options)
+	return cli.client.ImageTag(ctx, trustedRef.String(), ref.String(), options)
 }
 
 func notaryError(repoName string, err error) error {
@@ -302,7 +302,7 @@ func notaryError(repoName string, err error) error {
 	return err
 }
 
-func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
+func (cli *DockerCli) trustedPull(ctx context.Context, repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
 	var refs []target
 
 	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
@@ -364,7 +364,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 		if err != nil {
 			return err
 		}
-		if err := cli.imagePullPrivileged(authConfig, ref.String(), requestPrivilege, false); err != nil {
+		if err := cli.imagePullPrivileged(ctx, authConfig, ref.String(), requestPrivilege, false); err != nil {
 			return err
 		}
 
@@ -378,7 +378,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 			if err != nil {
 				return err
 			}
-			if err := cli.tagTrusted(trustedRef, tagged); err != nil {
+			if err := cli.tagTrusted(ctx, trustedRef, tagged); err != nil {
 				return err
 			}
 		}
@@ -386,8 +386,8 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 	return nil
 }
 
-func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
-	responseBody, err := cli.imagePushPrivileged(authConfig, ref.String(), requestPrivilege)
+func (cli *DockerCli) trustedPush(ctx context.Context, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
+	responseBody, err := cli.imagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
 	if err != nil {
 		return err
 	}

+ 3 - 1
api/client/unpause.go

@@ -19,9 +19,11 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, name := range cmd.Args() {
-		if err := cli.client.ContainerUnpause(context.Background(), name); err != nil {
+		if err := cli.client.ContainerUnpause(ctx, name); err != nil {
 			errs = append(errs, err.Error())
 		} else {
 			fmt.Fprintf(cli.out, "%s\n", name)

+ 4 - 1
api/client/update.go

@@ -99,10 +99,13 @@ func (cli *DockerCli) CmdUpdate(args ...string) error {
 		RestartPolicy: restartPolicy,
 	}
 
+	ctx := context.Background()
+
 	names := cmd.Args()
 	var errs []string
+
 	for _, name := range names {
-		if err := cli.client.ContainerUpdate(context.Background(), name, updateConfig); err != nil {
+		if err := cli.client.ContainerUpdate(ctx, name, updateConfig); err != nil {
 			errs = append(errs, err.Error())
 		} else {
 			fmt.Fprintf(cli.out, "%s\n", name)

+ 17 - 17
api/client/utils.go

@@ -23,13 +23,13 @@ import (
 	registrytypes "github.com/docker/engine-api/types/registry"
 )
 
-func (cli *DockerCli) electAuthServer() string {
+func (cli *DockerCli) electAuthServer(ctx context.Context) string {
 	// The daemon `/info` endpoint informs us of the default registry being
 	// used. This is essential in cross-platforms environment, where for
 	// example a Linux client might be interacting with a Windows daemon, hence
 	// the default registry URL might be Windows specific.
 	serverAddress := registry.IndexServer
-	if info, err := cli.client.Info(context.Background()); err != nil {
+	if info, err := cli.client.Info(ctx); err != nil {
 		fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
 	} else {
 		serverAddress = info.IndexServerAddress
@@ -58,12 +58,12 @@ func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.
 	}
 }
 
-func (cli *DockerCli) resizeTty(id string, isExec bool) {
+func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
 	height, width := cli.getTtySize()
-	cli.resizeTtyTo(id, height, width, isExec)
+	cli.resizeTtyTo(ctx, id, height, width, isExec)
 }
 
-func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
+func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
 	if height == 0 && width == 0 {
 		return
 	}
@@ -75,9 +75,9 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
 
 	var err error
 	if isExec {
-		err = cli.client.ContainerExecResize(context.Background(), id, options)
+		err = cli.client.ContainerExecResize(ctx, id, options)
 	} else {
-		err = cli.client.ContainerResize(context.Background(), id, options)
+		err = cli.client.ContainerResize(ctx, id, options)
 	}
 
 	if err != nil {
@@ -87,8 +87,8 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
 
 // getExitCode perform an inspect on the container. It returns
 // the running state and the exit code.
-func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
-	c, err := cli.client.ContainerInspect(context.Background(), containerID)
+func (cli *DockerCli) getExitCode(ctx context.Context, containerID string) (bool, int, error) {
+	c, err := cli.client.ContainerInspect(ctx, containerID)
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		if err != client.ErrConnectionFailed {
@@ -102,8 +102,8 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
 
 // getExecExitCode perform an inspect on the exec command. It returns
 // the running state and the exit code.
-func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
-	resp, err := cli.client.ContainerExecInspect(context.Background(), execID)
+func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
+	resp, err := cli.client.ContainerExecInspect(ctx, execID)
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		if err != client.ErrConnectionFailed {
@@ -115,8 +115,8 @@ func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
 	return resp.Running, resp.ExitCode, nil
 }
 
-func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
-	cli.resizeTty(id, isExec)
+func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool) error {
+	cli.resizeTty(ctx, id, isExec)
 
 	if runtime.GOOS == "windows" {
 		go func() {
@@ -126,7 +126,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
 				h, w := cli.getTtySize()
 
 				if prevW != w || prevH != h {
-					cli.resizeTty(id, isExec)
+					cli.resizeTty(ctx, id, isExec)
 				}
 				prevH = h
 				prevW = w
@@ -137,7 +137,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
 		gosignal.Notify(sigchan, signal.SIGWINCH)
 		go func() {
 			for range sigchan {
-				cli.resizeTty(id, isExec)
+				cli.resizeTty(ctx, id, isExec)
 			}
 		}()
 	}
@@ -185,10 +185,10 @@ func copyToFile(outfile string, r io.Reader) error {
 // resolveAuthConfig is like registry.ResolveAuthConfig, but if using the
 // default index, it uses the default index name for the daemon's platform,
 // not the client's platform.
-func (cli *DockerCli) resolveAuthConfig(index *registrytypes.IndexInfo) types.AuthConfig {
+func (cli *DockerCli) resolveAuthConfig(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
 	configKey := index.Name
 	if index.Official {
-		configKey = cli.electAuthServer()
+		configKey = cli.electAuthServer(ctx)
 	}
 
 	a, _ := getCredentials(cli.configFile, configKey)

+ 6 - 2
api/client/volume.go

@@ -110,8 +110,10 @@ func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
 		return nil
 	}
 
+	ctx := context.Background()
+
 	inspectSearcher := func(name string) (interface{}, []byte, error) {
-		i, err := cli.client.VolumeInspect(context.Background(), name)
+		i, err := cli.client.VolumeInspect(ctx, name)
 		return i, nil, err
 	}
 
@@ -161,8 +163,10 @@ func (cli *DockerCli) CmdVolumeRm(args ...string) error {
 
 	var status = 0
 
+	ctx := context.Background()
+
 	for _, name := range cmd.Args() {
-		if err := cli.client.VolumeRemove(context.Background(), name); err != nil {
+		if err := cli.client.VolumeRemove(ctx, name); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			status = 1
 			continue

+ 3 - 1
api/client/wait.go

@@ -21,9 +21,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
 
 	cmd.ParseFlags(args, true)
 
+	ctx := context.Background()
+
 	var errs []string
 	for _, name := range cmd.Args() {
-		status, err := cli.client.ContainerWait(context.Background(), name)
+		status, err := cli.client.ContainerWait(ctx, name)
 		if err != nil {
 			errs = append(errs, err.Error())
 		} else {

+ 1 - 1
api/server/router/image/backend.go

@@ -39,5 +39,5 @@ type importExportBackend interface {
 type registryBackend interface {
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
 	PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
-	SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
+	SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
 }

+ 1 - 1
api/server/router/image/image_routes.go

@@ -301,7 +301,7 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
 			headers[k] = v
 		}
 	}
-	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("term"), config, headers)
+	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), config, headers)
 	if err != nil {
 		return err
 	}

+ 1 - 1
builder/dockerfile/dispatchers.go

@@ -266,7 +266,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
 		return err
 	}
 
-	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
+	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", b.runConfig.WorkingDir))
 }
 
 // RUN some command yo

+ 95 - 0
builder/dockerfile/evaluator_test.go

@@ -3,6 +3,7 @@ package dockerfile
 import (
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -16,6 +17,7 @@ import (
 
 type dispatchTestCase struct {
 	name, dockerfile, expectedError string
+	files                           map[string]string
 }
 
 func init() {
@@ -34,21 +36,97 @@ func initDispatchTestCases() []dispatchTestCase {
 			name:          "ONBUILD forbidden FROM",
 			dockerfile:    "ONBUILD FROM scratch",
 			expectedError: "FROM isn't allowed as an ONBUILD trigger",
+			files:         nil,
 		},
 		{
 			name:          "ONBUILD forbidden MAINTAINER",
 			dockerfile:    "ONBUILD MAINTAINER docker.io",
 			expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
+			files:         nil,
 		},
 		{
 			name:          "ARG two arguments",
 			dockerfile:    "ARG foo bar",
 			expectedError: "ARG requires exactly one argument definition",
+			files:         nil,
 		},
 		{
 			name:          "MAINTAINER unknown flag",
 			dockerfile:    "MAINTAINER --boo joe@example.com",
 			expectedError: "Unknown flag: boo",
+			files:         nil,
+		},
+		{
+			name:          "ADD multiple files to file",
+			dockerfile:    "ADD file1.txt file2.txt test",
+			expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "JSON ADD multiple files to file",
+			dockerfile:    `ADD ["file1.txt", "file2.txt", "test"]`,
+			expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "Wildcard ADD multiple files to file",
+			dockerfile:    "ADD file*.txt test",
+			expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "Wildcard JSON ADD multiple files to file",
+			dockerfile:    `ADD ["file*.txt", "test"]`,
+			expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "COPY multiple files to file",
+			dockerfile:    "COPY file1.txt file2.txt test",
+			expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "JSON COPY multiple files to file",
+			dockerfile:    `COPY ["file1.txt", "file2.txt", "test"]`,
+			expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
+		},
+		{
+			name:          "ADD multiple files to file with whitespace",
+			dockerfile:    `ADD [ "test file1.txt", "test file2.txt", "test" ]`,
+			expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
+		},
+		{
+			name:          "COPY multiple files to file with whitespace",
+			dockerfile:    `COPY [ "test file1.txt", "test file2.txt", "test" ]`,
+			expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
+			files:         map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
+		},
+		{
+			name:          "COPY wildcard no files",
+			dockerfile:    `COPY file*.txt /tmp/`,
+			expectedError: "No source files were specified",
+			files:         nil,
+		},
+		{
+			name:          "COPY url",
+			dockerfile:    `COPY https://index.docker.io/robots.txt /`,
+			expectedError: "Source can't be a URL for COPY",
+			files:         nil,
+		},
+		{
+			name:          "Chaining ONBUILD",
+			dockerfile:    `ONBUILD ONBUILD RUN touch foobar`,
+			expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
+			files:         nil,
+		},
+		{
+			name:          "Invalid instruction",
+			dockerfile:    `foo bar`,
+			expectedError: "Unknown instruction: FOO",
+			files:         nil,
 		}}
 
 	return dispatchTestCases
@@ -66,6 +144,10 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
 	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
 	defer cleanup()
 
+	for filename, content := range testCase.files {
+		createTestTempFile(t, contextDir, filename, content, 0777)
+	}
+
 	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
 
 	if err != nil {
@@ -132,3 +214,16 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
 		}
 	}
 }
+
+// createTestTempFile creates a temporary file within dir with specific contents and permissions.
+// When an error occurs, it terminates the test
+func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
+	filePath := filepath.Join(dir, filename)
+	err := ioutil.WriteFile(filePath, []byte(contents), perm)
+
+	if err != nil {
+		t.Fatalf("Error when creating %s file: %s", filename, err)
+	}
+
+	return filePath
+}

+ 22 - 12
builder/dockerfile/internals.go

@@ -618,6 +618,27 @@ func (b *Builder) readDockerfile() error {
 		}
 	}
 
+	err := b.parseDockerfile()
+
+	if err != nil {
+		return err
+	}
+
+	// After the Dockerfile has been parsed, we need to check the .dockerignore
+	// file for either "Dockerfile" or ".dockerignore", and if either are
+	// present then erase them from the build context. These files should never
+	// have been sent from the client but we did send them to make sure that
+	// we had the Dockerfile to actually parse, and then we also need the
+	// .dockerignore file to know whether either file should be removed.
+	// Note that this assumes the Dockerfile has been read into memory and
+	// is now safe to be removed.
+	if dockerIgnore, ok := b.context.(builder.DockerIgnoreContext); ok {
+		dockerIgnore.Process([]string{b.options.Dockerfile})
+	}
+	return nil
+}
+
+func (b *Builder) parseDockerfile() error {
 	f, err := b.context.Open(b.options.Dockerfile)
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -625,6 +646,7 @@ func (b *Builder) readDockerfile() error {
 		}
 		return err
 	}
+	defer f.Close()
 	if f, ok := f.(*os.File); ok {
 		// ignoring error because Open already succeeded
 		fi, err := f.Stat()
@@ -636,22 +658,10 @@ func (b *Builder) readDockerfile() error {
 		}
 	}
 	b.dockerfile, err = parser.Parse(f)
-	f.Close()
 	if err != nil {
 		return err
 	}
 
-	// After the Dockerfile has been parsed, we need to check the .dockerignore
-	// file for either "Dockerfile" or ".dockerignore", and if either are
-	// present then erase them from the build context. These files should never
-	// have been sent from the client but we did send them to make sure that
-	// we had the Dockerfile to actually parse, and then we also need the
-	// .dockerignore file to know whether either file should be removed.
-	// Note that this assumes the Dockerfile has been read into memory and
-	// is now safe to be removed.
-	if dockerIgnore, ok := b.context.(builder.DockerIgnoreContext); ok {
-		dockerIgnore.Process([]string{b.options.Dockerfile})
-	}
 	return nil
 }
 

+ 55 - 0
builder/dockerfile/internals_test.go

@@ -0,0 +1,55 @@
+package dockerfile
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/builder"
+	"github.com/docker/docker/pkg/archive"
+	"github.com/docker/engine-api/types"
+)
+
+func TestEmptyDockerfile(t *testing.T) {
+	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
+	defer cleanup()
+
+	createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777)
+
+	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
+
+	if err != nil {
+		t.Fatalf("Error when creating tar stream: %s", err)
+	}
+
+	defer func() {
+		if err = tarStream.Close(); err != nil {
+			t.Fatalf("Error when closing tar stream: %s", err)
+		}
+	}()
+
+	context, err := builder.MakeTarSumContext(tarStream)
+
+	if err != nil {
+		t.Fatalf("Error when creating tar context: %s", err)
+	}
+
+	defer func() {
+		if err = context.Close(); err != nil {
+			t.Fatalf("Error when closing tar context: %s", err)
+		}
+	}()
+
+	options := &types.ImageBuildOptions{}
+
+	b := &Builder{options: options, context: context}
+
+	err = b.readDockerfile()
+
+	if err == nil {
+		t.Fatalf("No error when executing test for empty Dockerfile")
+	}
+
+	if !strings.Contains(err.Error(), "The Dockerfile (Dockerfile) cannot be empty") {
+		t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", "The Dockerfile (Dockerfile) cannot be empty", err.Error())
+	}
+}

+ 26 - 29
cli/cli.go

@@ -39,12 +39,7 @@ func New(handlers ...Handler) *Cli {
 	return cli
 }
 
-// initErr is an error returned upon initialization of a handler implementing Initializer.
-type initErr struct{ error }
-
-func (err initErr) Error() string {
-	return err.Error()
-}
+var errCommandNotFound = errors.New("command not found")
 
 func (cli *Cli) command(args ...string) (func(...string) error, error) {
 	for _, c := range cli.handlers {
@@ -54,35 +49,36 @@ func (cli *Cli) command(args ...string) (func(...string) error, error) {
 		if cmd := c.Command(strings.Join(args, " ")); cmd != nil {
 			if ci, ok := c.(Initializer); ok {
 				if err := ci.Initialize(); err != nil {
-					return nil, initErr{err}
+					return nil, err
 				}
 			}
 			return cmd, nil
 		}
 	}
-	return nil, errors.New("command not found")
+	return nil, errCommandNotFound
 }
 
 // Run executes the specified command.
 func (cli *Cli) Run(args ...string) error {
 	if len(args) > 1 {
 		command, err := cli.command(args[:2]...)
-		switch err := err.(type) {
-		case nil:
+		if err == nil {
 			return command(args[2:]...)
-		case initErr:
-			return err.error
+		}
+		if err != errCommandNotFound {
+			return err
 		}
 	}
 	if len(args) > 0 {
 		command, err := cli.command(args[0])
-		switch err := err.(type) {
-		case nil:
-			return command(args[1:]...)
-		case initErr:
-			return err.error
+		if err != nil {
+			if err == errCommandNotFound {
+				cli.noSuchCommand(args[0])
+				return nil
+			}
+			return err
 		}
-		cli.noSuchCommand(args[0])
+		return command(args[1:]...)
 	}
 	return cli.CmdHelp()
 }
@@ -110,24 +106,25 @@ func (cli *Cli) Command(name string) func(...string) error {
 func (cli *Cli) CmdHelp(args ...string) error {
 	if len(args) > 1 {
 		command, err := cli.command(args[:2]...)
-		switch err := err.(type) {
-		case nil:
+		if err == nil {
 			command("--help")
 			return nil
-		case initErr:
-			return err.error
+		}
+		if err != errCommandNotFound {
+			return err
 		}
 	}
 	if len(args) > 0 {
 		command, err := cli.command(args[0])
-		switch err := err.(type) {
-		case nil:
-			command("--help")
-			return nil
-		case initErr:
-			return err.error
+		if err != nil {
+			if err == errCommandNotFound {
+				cli.noSuchCommand(args[0])
+				return nil
+			}
+			return err
 		}
-		cli.noSuchCommand(args[0])
+		command("--help")
+		return nil
 	}
 
 	if cli.Usage == nil {

+ 0 - 5
cliconfig/config.go

@@ -16,11 +16,6 @@ const (
 	ConfigFileName = "config.json"
 	configFileDir  = ".docker"
 	oldConfigfile  = ".dockercfg"
-
-	// This constant is only used for really old config files when the
-	// URL wasn't saved as part of the config file and it was just
-	// assumed to be this value.
-	defaultIndexserver = "https://index.docker.io/v1/"
 )
 
 var (

+ 3 - 2
cmd/dockerd/daemon.go

@@ -228,10 +228,11 @@ func (cli *DaemonCli) start() (err error) {
 		if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) {
 			logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]")
 		}
-		l, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
+		ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
 		if err != nil {
 			return err
 		}
+		ls = wrapListeners(proto, ls)
 		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
 		if proto == "tcp" {
 			if err := allocateDaemonPort(addr); err != nil {
@@ -239,7 +240,7 @@ func (cli *DaemonCli) start() (err error) {
 			}
 		}
 		logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
-		api.Accept(protoAddrParts[1], l...)
+		api.Accept(protoAddrParts[1], ls...)
 	}
 
 	if err := migrateKey(); err != nil {

+ 74 - 0
cmd/dockerd/daemon_solaris.go

@@ -0,0 +1,74 @@
+// +build solaris
+
+package main
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"path/filepath"
+	"syscall"
+
+	"github.com/docker/docker/libcontainerd"
+	"github.com/docker/docker/pkg/system"
+)
+
+const defaultDaemonConfigFile = ""
+
+// currentUserIsOwner checks whether the current user is the owner of the given
+// file.
+func currentUserIsOwner(f string) bool {
+	if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil {
+		if int(fileInfo.UID()) == os.Getuid() {
+			return true
+		}
+	}
+	return false
+}
+
+// setDefaultUmask sets the umask to 0022 to avoid problems
+// caused by custom umask
+func setDefaultUmask() error {
+	desiredUmask := 0022
+	syscall.Umask(desiredUmask)
+	if umask := syscall.Umask(desiredUmask); umask != desiredUmask {
+		return fmt.Errorf("failed to set umask: expected %#o, got %#o", desiredUmask, umask)
+	}
+
+	return nil
+}
+
+func getDaemonConfDir() string {
+	return "/etc/docker"
+}
+
+// setupConfigReloadTrap configures the USR2 signal to reload the configuration.
+func (cli *DaemonCli) setupConfigReloadTrap() {
+}
+
+// notifySystem sends a message to the host when the server is ready to be used
+func notifySystem() {
+}
+
+func (cli *DaemonCli) getPlatformRemoteOptions() []libcontainerd.RemoteOption {
+	opts := []libcontainerd.RemoteOption{}
+	return opts
+}
+
+// getLibcontainerdRoot gets the root directory for libcontainerd/containerd to
+// store their state.
+func (cli *DaemonCli) getLibcontainerdRoot() string {
+	return filepath.Join(cli.Config.ExecRoot, "libcontainerd")
+}
+
+func allocateDaemonPort(addr string) error {
+	return nil
+}
+
+// notifyShutdown is called after the daemon shuts down but before the process exits.
+func notifyShutdown(err error) {
+}
+
+func wrapListeners(proto string, ls []net.Listener) []net.Listener {
+	return ls
+}

+ 16 - 1
cmd/dockerd/daemon_unix.go

@@ -1,4 +1,4 @@
-// +build !windows
+// +build !windows,!solaris
 
 package main
 
@@ -11,6 +11,7 @@ import (
 	"strconv"
 	"syscall"
 
+	"github.com/docker/docker/cmd/dockerd/hack"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/libcontainerd"
 	"github.com/docker/docker/pkg/system"
@@ -111,3 +112,17 @@ func allocateDaemonPort(addr string) error {
 // notifyShutdown is called after the daemon shuts down but before the process exits.
 func notifyShutdown(err error) {
 }
+
+func wrapListeners(proto string, ls []net.Listener) []net.Listener {
+	if os.Getenv("DOCKER_HTTP_HOST_COMPAT") != "" {
+		switch proto {
+		case "unix":
+			ls[0] = &hack.MalformedHostHeaderOverride{ls[0]}
+		case "fd":
+			for i := range ls {
+				ls[i] = &hack.MalformedHostHeaderOverride{ls[i]}
+			}
+		}
+	}
+	return ls
+}

+ 5 - 0
cmd/dockerd/daemon_windows.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"fmt"
+	"net"
 	"os"
 	"syscall"
 
@@ -75,3 +76,7 @@ func (cli *DaemonCli) getLibcontainerdRoot() string {
 func allocateDaemonPort(addr string) error {
 	return nil
 }
+
+func wrapListeners(proto string, ls []net.Listener) []net.Listener {
+	return ls
+}

+ 116 - 0
cmd/dockerd/hack/malformed_host_override.go

@@ -0,0 +1,116 @@
+// +build !windows
+
+package hack
+
+import "net"
+
+// MalformedHostHeaderOverride is a wrapper to be able
+// to overcome the 400 Bad request coming from old docker
+// clients that send an invalid Host header.
+type MalformedHostHeaderOverride struct {
+	net.Listener
+}
+
+// MalformedHostHeaderOverrideConn wraps the underlying unix
+// connection and keeps track of the first read from http.Server
+// which just reads the headers.
+type MalformedHostHeaderOverrideConn struct {
+	net.Conn
+	first bool
+}
+
+var closeConnHeader = []byte("\r\nConnection: close\r")
+
+// Read reads the first *read* request from http.Server to inspect
+// the Host header. If the Host starts with / then we're talking to
+// an old docker client which send an invalid Host header. To not
+// error out in http.Server we rewrite the first bytes of the request
+// to sanitize the Host header itself.
+// In case we're not dealing with old docker clients the data is just passed
+// to the server w/o modification.
+func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) {
+	// http.Server uses a 4k buffer
+	if l.first && len(b) == 4096 {
+		// This keeps track of the first read from http.Server which just reads
+		// the headers
+		l.first = false
+		// The first read of the connection by http.Server is done limited to
+		// DefaultMaxHeaderBytes (usually 1 << 20) + 4096.
+		// Here we do the first read which gets us all the http headers to
+		// be inspected and modified below.
+		c, err := l.Conn.Read(b)
+		if err != nil {
+			return c, err
+		}
+
+		var (
+			start, end    int
+			firstLineFeed = -1
+			buf           []byte
+		)
+		for i, bb := range b[:c] {
+			if bb == '\n' && firstLineFeed == -1 {
+				firstLineFeed = i
+			}
+			if bb != '\n' {
+				continue
+			}
+			if b[i+1] != 'H' {
+				continue
+			}
+			if b[i+2] != 'o' {
+				continue
+			}
+			if b[i+3] != 's' {
+				continue
+			}
+			if b[i+4] != 't' {
+				continue
+			}
+			if b[i+5] != ':' {
+				continue
+			}
+			if b[i+6] != ' ' {
+				continue
+			}
+			if b[i+7] != '/' {
+				continue
+			}
+			// ensure clients other than the docker clients do not get this hack
+			if i != firstLineFeed {
+				return c, nil
+			}
+			start = i + 7
+			// now find where the value ends
+			for ii, bbb := range b[start:c] {
+				if bbb == '\n' {
+					end = start + ii
+					break
+				}
+			}
+			buf = make([]byte, 0, c+len(closeConnHeader)-(end-start))
+			// strip the value of the host header and
+			// inject `Connection: close` to ensure we don't reuse this connection
+			buf = append(buf, b[:start]...)
+			buf = append(buf, closeConnHeader...)
+			buf = append(buf, b[end:c]...)
+			copy(b, buf)
+			break
+		}
+		if len(buf) == 0 {
+			return c, nil
+		}
+		return len(buf), nil
+	}
+	return l.Conn.Read(b)
+}
+
+// Accept makes the listener accepts connections and wraps the connection
+// in a MalformedHostHeaderOverrideConn initilizing first to true.
+func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) {
+	c, err := l.Listener.Accept()
+	if err != nil {
+		return c, err
+	}
+	return &MalformedHostHeaderOverrideConn{c, true}, nil
+}

+ 115 - 0
cmd/dockerd/hack/malformed_host_override_test.go

@@ -0,0 +1,115 @@
+// +build !windows
+
+package hack
+
+import (
+	"bytes"
+	"io"
+	"net"
+	"strings"
+	"testing"
+)
+
+func TestHeaderOverrideHack(t *testing.T) {
+	client, srv := net.Pipe()
+	tests := [][2][]byte{
+		{
+			[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
+			[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\n"),
+		},
+		{
+			[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\nFoo: Bar\r\n"),
+			[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\nFoo: Bar\r\n"),
+		},
+		{
+			[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something!"),
+			[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something!"),
+		},
+		{
+			[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
+			[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
+		},
+		{
+			[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
+			[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
+		},
+	}
+	l := MalformedHostHeaderOverrideConn{client, true}
+	read := make([]byte, 4096)
+
+	for _, pair := range tests {
+		go func(x []byte) {
+			srv.Write(x)
+		}(pair[0])
+		n, err := l.Read(read)
+		if err != nil && err != io.EOF {
+			t.Fatalf("read: %d - %d, err: %v\n%s", n, len(pair[0]), err, string(read[:n]))
+		}
+		if !bytes.Equal(read[:n], pair[1][:n]) {
+			t.Fatalf("\n%s\n%s\n", read[:n], pair[1][:n])
+		}
+		l.first = true
+		// clean out the slice
+		read = read[:0]
+	}
+	srv.Close()
+	l.Close()
+}
+
+func BenchmarkWithHack(b *testing.B) {
+	client, srv := net.Pipe()
+	done := make(chan struct{})
+	req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
+	read := make([]byte, 4096)
+	b.SetBytes(int64(len(req) * 30))
+
+	l := MalformedHostHeaderOverrideConn{client, true}
+	go func() {
+		for {
+			if _, err := srv.Write(req); err != nil {
+				srv.Close()
+				break
+			}
+			l.first = true // make sure each subsequent run uses the hack parsing
+		}
+		close(done)
+	}()
+
+	for i := 0; i < b.N; i++ {
+		for i := 0; i < 30; i++ {
+			if n, err := l.Read(read); err != nil && err != io.EOF {
+				b.Fatalf("read: %d - %d, err: %v\n%s", n, len(req), err, string(read[:n]))
+			}
+		}
+	}
+	l.Close()
+	<-done
+}
+
+func BenchmarkNoHack(b *testing.B) {
+	client, srv := net.Pipe()
+	done := make(chan struct{})
+	req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
+	read := make([]byte, 4096)
+	b.SetBytes(int64(len(req) * 30))
+
+	go func() {
+		for {
+			if _, err := srv.Write(req); err != nil {
+				srv.Close()
+				break
+			}
+		}
+		close(done)
+	}()
+
+	for i := 0; i < b.N; i++ {
+		for i := 0; i < 30; i++ {
+			if _, err := client.Read(read); err != nil && err != io.EOF {
+				b.Fatal(err)
+			}
+		}
+	}
+	client.Close()
+	<-done
+}

+ 95 - 0
container/container_solaris.go

@@ -0,0 +1,95 @@
+// +build solaris
+
+package container
+
+import (
+	"os"
+	"path/filepath"
+
+	"github.com/docker/docker/volume"
+	"github.com/docker/engine-api/types/container"
+)
+
+// Container holds fields specific to the Solaris implementation. See
+// CommonContainer for standard fields common to all containers.
+type Container struct {
+	CommonContainer
+
+	// fields below here are platform specific.
+	HostnamePath   string
+	HostsPath      string
+	ResolvConfPath string
+}
+
+// ExitStatus provides exit reasons for a container.
+type ExitStatus struct {
+	// The exit code with which the container exited.
+	ExitCode int
+}
+
+// CreateDaemonEnvironment creates a new environment variable slice for this container.
+func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string {
+	return nil
+}
+
+func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
+	return volumeMounts, nil
+}
+
+// TrySetNetworkMount attempts to set the network mounts given a provided destination and
+// the path to use for it; return true if the given destination was a network mount file
+func (container *Container) TrySetNetworkMount(destination string, path string) bool {
+	return true
+}
+
+// NetworkMounts returns the list of network mounts.
+func (container *Container) NetworkMounts() []Mount {
+	var mount []Mount
+	return mount
+}
+
+// CopyImagePathContent copies files in destination to the volume.
+func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
+	return nil
+}
+
+// UnmountIpcMounts unmount Ipc related mounts.
+func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
+}
+
+// IpcMounts returns the list of Ipc related mounts.
+func (container *Container) IpcMounts() []Mount {
+	return nil
+}
+
+// UpdateContainer updates configuration of a container
+func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
+	return nil
+}
+
+// UnmountVolumes explicitly unmounts volumes from the container.
+func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error {
+	return nil
+}
+
+// TmpfsMounts returns the list of tmpfs mounts
+func (container *Container) TmpfsMounts() []Mount {
+	var mounts []Mount
+	return mounts
+}
+
+// cleanResourcePath cleans a resource path and prepares to combine with mnt path
+func cleanResourcePath(path string) string {
+	return filepath.Join(string(os.PathSeparator), path)
+}
+
+// BuildHostnameFile writes the container's hostname file.
+func (container *Container) BuildHostnameFile() error {
+	return nil
+}
+
+// canMountFS determines if the file system for the container
+// can be mounted locally. A no-op on non-Windows platforms
+func (container *Container) canMountFS() bool {
+	return true
+}

+ 7 - 0
container/state_solaris.go

@@ -0,0 +1,7 @@
+package container
+
+// setFromExitStatus is a platform specific helper function to set the state
+// based on the ExitStatus structure.
+func (s *State) setFromExitStatus(exitStatus *ExitStatus) {
+	s.ExitCode = exitStatus.ExitCode
+}

+ 24 - 4
contrib/completion/bash/docker

@@ -966,6 +966,11 @@ _docker_events() {
 			__docker_complete_containers_all
 			return
 			;;
+		daemon)
+			local name=$(__docker_q info | sed -n 's/^\(ID\|Name\): //p')
+			COMPREPLY=( $( compgen -W "$name" -- "${cur##*=}" ) )
+			return
+			;;
 		event)
 			COMPREPLY=( $( compgen -W "
 				attach
@@ -987,6 +992,7 @@ _docker_events() {
 				pause
 				pull
 				push
+				reload
 				rename
 				resize
 				restart
@@ -1012,7 +1018,7 @@ _docker_events() {
 			return
 			;;
 		type)
-			COMPREPLY=( $( compgen -W "container image network volume" -- "${cur##*=}" ) )
+			COMPREPLY=( $( compgen -W "container daemon image network volume" -- "${cur##*=}" ) )
 			return
 			;;
 		volume)
@@ -1024,7 +1030,7 @@ _docker_events() {
 
 	case "$prev" in
 		--filter|-f)
-			COMPREPLY=( $( compgen -S = -W "container event image label network type volume" -- "$cur" ) )
+			COMPREPLY=( $( compgen -S = -W "container daemon event image label network type volume" -- "$cur" ) )
 			__docker_nospace
 			return
 			;;
@@ -1907,15 +1913,29 @@ _docker_save() {
 }
 
 _docker_search() {
+	local key=$(__docker_map_key_of_current_option '--filter|-f')
+	case "$key" in
+		is-automated)
+			COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+			return
+			;;
+		is-official)
+			COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+			return
+			;;
+	esac
+
 	case "$prev" in
-		--stars|-s)
+		--filter|-f)
+			COMPREPLY=( $( compgen -S = -W "is-automated is-official stars" -- "$cur" ) )
+			__docker_nospace
 			return
 			;;
 	esac
 
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--automated --help --no-trunc --stars -s" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--filter --help --no-trunc" -- "$cur" ) )
 			;;
 	esac
 }

+ 62 - 3
contrib/completion/zsh/_docker

@@ -311,6 +311,54 @@ __docker_complete_ps_filters() {
     return ret
 }
 
+__docker_complete_search_filters() {
+    [[ $PREFIX = -* ]] && return 1
+    integer ret=1
+    declare -a boolean_opts opts
+
+    boolean_opts=('true' 'false')
+    opts=('is-automated' 'is-official' 'stars')
+
+    if compset -P '*='; then
+        case "${${words[-1]%=*}#*=}" in
+            (is-automated|is-official)
+                _describe -t boolean-filter-opts "filter options" boolean_opts && ret=0
+                ;;
+            *)
+                _message 'value' && ret=0
+                ;;
+        esac
+    else
+        _describe -t filter-opts "filter options" opts -qS "=" && ret=0
+    fi
+
+    return ret
+}
+
+__docker_complete_images_filters() {
+    [[ $PREFIX = -* ]] && return 1
+    integer ret=1
+    declare -a boolean_opts opts
+
+    boolean_opts=('true' 'false')
+    opts=('dangling' 'label')
+
+    if compset -P '*='; then
+        case "${${words[-1]%=*}#*=}" in
+            (dangling)
+                _describe -t boolean-filter-opts "filter options" boolean_opts && ret=0
+                ;;
+            *)
+                _message 'value' && ret=0
+                ;;
+        esac
+    else
+        _describe -t filter-opts "Filter Options" opts -qS "=" && ret=0
+    fi
+
+    return ret
+}
+
 __docker_network_complete_ls_filters() {
     [[ $PREFIX = -* ]] && return 1
     integer ret=1
@@ -929,11 +977,17 @@ __docker_subcommand() {
                 $opts_help \
                 "($help -a --all)"{-a,--all}"[Show all images]" \
                 "($help)--digests[Show digests]" \
-                "($help)*"{-f=,--filter=}"[Filter values]:filter: " \
+                "($help)*"{-f=,--filter=}"[Filter values]:filter:->filter-options" \
                 "($help)--format[Pretty-print containers using a Go template]:format: " \
                 "($help)--no-trunc[Do not truncate output]" \
                 "($help -q --quiet)"{-q,--quiet}"[Only show numeric IDs]" \
                 "($help -): :__docker_repositories" && ret=0
+
+            case $state in
+                (filter-options)
+                    __docker_complete_images_filters && ret=0
+                    ;;
+            esac
             ;;
         (import)
             _arguments $(__docker_arguments) \
@@ -1126,10 +1180,15 @@ __docker_subcommand() {
         (search)
             _arguments $(__docker_arguments) \
                 $opts_help \
-                "($help)--automated[Only show automated builds]" \
+                "($help)*"{-f=,--filter=}"[Filter values]:filter:->filter-options" \
                 "($help)--no-trunc[Do not truncate output]" \
-                "($help -s --stars)"{-s=,--stars=}"[Only display with at least X stars]:stars:(0 10 100 1000)" \
                 "($help -):term: " && ret=0
+
+            case $state in
+                (filter-options)
+                    __docker_complete_search_filters && ret=0
+                    ;;
+            esac
             ;;
         (start)
             _arguments $(__docker_arguments) \

+ 1 - 1
daemon/config.go

@@ -88,7 +88,7 @@ type CommonConfig struct {
 	Root                 string              `json:"graph,omitempty"`
 	SocketGroup          string              `json:"group,omitempty"`
 	TrustKeyPath         string              `json:"-"`
-	CorsHeaders          string              `json:"api-cors-headers,omitempty"`
+	CorsHeaders          string              `json:"api-cors-header,omitempty"`
 	EnableCors           bool                `json:"api-enable-cors,omitempty"`
 
 	// ClusterStore is the storage backend used for the cluster information. It is used by both

+ 39 - 0
daemon/config_solaris.go

@@ -0,0 +1,39 @@
+package daemon
+
+import (
+	flag "github.com/docker/docker/pkg/mflag"
+)
+
+var (
+	defaultPidFile = "/var/run/docker.pid"
+	defaultGraph   = "/var/lib/docker"
+	defaultExec    = "zones"
+)
+
+// Config defines the configuration of a docker daemon.
+// These are the configuration settings that you pass
+// to the docker daemon when you launch it with say: `docker -d -e lxc`
+type Config struct {
+	CommonConfig
+
+	// Fields below here are platform specific.
+	ExecRoot string `json:"exec-root,omitempty"`
+}
+
+// bridgeConfig stores all the bridge driver specific
+// configuration.
+type bridgeConfig struct {
+	commonBridgeConfig
+}
+
+// InstallFlags adds command-line options to the top-level flag parser for
+// the current process.
+// Subsequent calls to `flag.Parse` will populate config with values parsed
+// from the command-line.
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+	// First handle install flags which are consistent cross-platform
+	config.InstallCommonFlags(cmd, usageFn)
+
+	// Then platform-specific install flags
+	config.attachExperimentalFlags(cmd, usageFn)
+}

+ 1 - 1
daemon/config_unix.go

@@ -41,7 +41,7 @@ type bridgeConfig struct {
 	EnableIPv6                  bool   `json:"ipv6,omitempty"`
 	EnableIPTables              bool   `json:"iptables,omitempty"`
 	EnableIPForward             bool   `json:"ip-forward,omitempty"`
-	EnableIPMasq                bool   `json:"ip-mask,omitempty"`
+	EnableIPMasq                bool   `json:"ip-masq,omitempty"`
 	EnableUserlandProxy         bool   `json:"userland-proxy,omitempty"`
 	DefaultIP                   net.IP `json:"ip,omitempty"`
 	IP                          string `json:"bip,omitempty"`

+ 50 - 0
daemon/container_operations_solaris.go

@@ -0,0 +1,50 @@
+// +build solaris
+
+package daemon
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/container"
+	networktypes "github.com/docker/engine-api/types/network"
+	"github.com/docker/libnetwork"
+)
+
+func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
+	return nil, nil
+}
+
+// ConnectToNetwork connects a container to a network
+func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
+	return fmt.Errorf("Solaris does not support connecting a running container to a network")
+}
+
+// getSize returns real size & virtual size
+func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
+	return 0, 0
+}
+
+// DisconnectFromNetwork disconnects a container from the network
+func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
+	return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
+}
+
+func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
+	return nil
+}
+
+func (daemon *Daemon) mountVolumes(container *container.Container) error {
+	return nil
+}
+
+func killProcessDirectly(container *container.Container) error {
+	return nil
+}
+
+func detachMounted(path string) error {
+	return nil
+}
+
+func isLinkable(child *container.Container) bool {
+	return false
+}

+ 4 - 0
daemon/create.go

@@ -9,6 +9,7 @@ import (
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/runconfig"
 	volumestore "github.com/docker/docker/volume/store"
 	"github.com/docker/engine-api/types"
 	containertypes "github.com/docker/engine-api/types/container"
@@ -122,6 +123,9 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
 	if params.NetworkingConfig != nil {
 		endpointsConfigs = params.NetworkingConfig.EndpointsConfig
 	}
+	// Make sure NetworkMode has an acceptable value. We do this to ensure
+	// backwards API compatibility.
+	container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
 
 	if err := daemon.updateContainerNetworkSettings(container, endpointsConfigs); err != nil {
 		return nil, err

+ 106 - 286
daemon/daemon.go

@@ -6,6 +6,7 @@
 package daemon
 
 import (
+	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -15,6 +16,7 @@ import (
 	"path/filepath"
 	"regexp"
 	"runtime"
+	"strconv"
 	"strings"
 	"sync"
 	"syscall"
@@ -23,7 +25,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	containerd "github.com/docker/containerd/api/grpc/types"
 	"github.com/docker/docker/api"
-	"github.com/docker/docker/builder"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/exec"
@@ -40,7 +41,6 @@ import (
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/image/tarexport"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/libcontainerd"
 	"github.com/docker/docker/migrate/v1"
@@ -64,6 +64,7 @@ import (
 	volumedrivers "github.com/docker/docker/volume/drivers"
 	"github.com/docker/docker/volume/local"
 	"github.com/docker/docker/volume/store"
+	"github.com/docker/engine-api/types/filters"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/libnetwork"
 	nwconfig "github.com/docker/libnetwork/config"
@@ -78,15 +79,6 @@ var (
 	errSystemNotSupported = fmt.Errorf("The Docker daemon is not supported on this platform.")
 )
 
-// ErrImageDoesNotExist is error returned when no image can be found for a reference.
-type ErrImageDoesNotExist struct {
-	RefOrID string
-}
-
-func (e ErrImageDoesNotExist) Error() string {
-	return fmt.Sprintf("no such id: %s", e.RefOrID)
-}
-
 // Daemon holds information about the Docker daemon.
 type Daemon struct {
 	ID                        string
@@ -286,11 +278,6 @@ func (daemon *Daemon) restore() error {
 			defer wg.Done()
 			rm := c.RestartManager(false)
 			if c.IsRunning() || c.IsPaused() {
-				// Fix activityCount such that graph mounts can be unmounted later
-				if err := daemon.layerStore.ReinitRWLayer(c.RWLayer); err != nil {
-					logrus.Errorf("Failed to ReinitRWLayer for %s due to %s", c.ID, err)
-					return
-				}
 				if err := daemon.containerd.Restore(c.ID, libcontainerd.WithRestartManager(rm)); err != nil {
 					logrus.Errorf("Failed to restore with containerd: %q", err)
 					return
@@ -808,7 +795,7 @@ func NewDaemon(config *Config, registryService *registry.Service, containerdRemo
 	sysInfo := sysinfo.New(false)
 	// Check if Devices cgroup is mounted, it is hard requirement for container security,
 	// on Linux/FreeBSD.
-	if runtime.GOOS != "windows" && !sysInfo.CgroupDevicesEnabled {
+	if runtime.GOOS != "windows" && runtime.GOOS != "solaris" && !sysInfo.CgroupDevicesEnabled {
 		return nil, fmt.Errorf("Devices cgroup isn't mounted")
 	}
 
@@ -1006,221 +993,6 @@ func isBrokenPipe(e error) bool {
 	return e == syscall.EPIPE
 }
 
-// ExportImage exports a list of images to the given output stream. The
-// exported images are archived into a tar when written to the output
-// stream. All images with the given tag and all versions containing
-// the same tag are exported. names is the set of tags to export, and
-// outStream is the writer which the images are written to.
-func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
-	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
-	return imageExporter.Save(names, outStream)
-}
-
-// LookupImage looks up an image by name and returns it as an ImageInspect
-// structure.
-func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
-	img, err := daemon.GetImage(name)
-	if err != nil {
-		return nil, fmt.Errorf("No such image: %s", name)
-	}
-
-	refs := daemon.referenceStore.References(img.ID())
-	repoTags := []string{}
-	repoDigests := []string{}
-	for _, ref := range refs {
-		switch ref.(type) {
-		case reference.NamedTagged:
-			repoTags = append(repoTags, ref.String())
-		case reference.Canonical:
-			repoDigests = append(repoDigests, ref.String())
-		}
-	}
-
-	var size int64
-	var layerMetadata map[string]string
-	layerID := img.RootFS.ChainID()
-	if layerID != "" {
-		l, err := daemon.layerStore.Get(layerID)
-		if err != nil {
-			return nil, err
-		}
-		defer layer.ReleaseAndLog(daemon.layerStore, l)
-		size, err = l.Size()
-		if err != nil {
-			return nil, err
-		}
-
-		layerMetadata, err = l.Metadata()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	comment := img.Comment
-	if len(comment) == 0 && len(img.History) > 0 {
-		comment = img.History[len(img.History)-1].Comment
-	}
-
-	imageInspect := &types.ImageInspect{
-		ID:              img.ID().String(),
-		RepoTags:        repoTags,
-		RepoDigests:     repoDigests,
-		Parent:          img.Parent.String(),
-		Comment:         comment,
-		Created:         img.Created.Format(time.RFC3339Nano),
-		Container:       img.Container,
-		ContainerConfig: &img.ContainerConfig,
-		DockerVersion:   img.DockerVersion,
-		Author:          img.Author,
-		Config:          img.Config,
-		Architecture:    img.Architecture,
-		Os:              img.OS,
-		Size:            size,
-		VirtualSize:     size, // TODO: field unused, deprecate
-		RootFS:          rootFSToAPIType(img.RootFS),
-	}
-
-	imageInspect.GraphDriver.Name = daemon.GraphDriverName()
-
-	imageInspect.GraphDriver.Data = layerMetadata
-
-	return imageInspect, nil
-}
-
-// LoadImage uploads a set of images into the repository. This is the
-// complement of ImageExport.  The input stream is an uncompressed tar
-// ball containing images and metadata.
-func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
-	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
-	return imageExporter.Load(inTar, outStream, quiet)
-}
-
-// ImageHistory returns a slice of ImageHistory structures for the specified image
-// name by walking the image lineage.
-func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
-	img, err := daemon.GetImage(name)
-	if err != nil {
-		return nil, err
-	}
-
-	history := []*types.ImageHistory{}
-
-	layerCounter := 0
-	rootFS := *img.RootFS
-	rootFS.DiffIDs = nil
-
-	for _, h := range img.History {
-		var layerSize int64
-
-		if !h.EmptyLayer {
-			if len(img.RootFS.DiffIDs) <= layerCounter {
-				return nil, fmt.Errorf("too many non-empty layers in History section")
-			}
-
-			rootFS.Append(img.RootFS.DiffIDs[layerCounter])
-			l, err := daemon.layerStore.Get(rootFS.ChainID())
-			if err != nil {
-				return nil, err
-			}
-			layerSize, err = l.DiffSize()
-			layer.ReleaseAndLog(daemon.layerStore, l)
-			if err != nil {
-				return nil, err
-			}
-
-			layerCounter++
-		}
-
-		history = append([]*types.ImageHistory{{
-			ID:        "<missing>",
-			Created:   h.Created.Unix(),
-			CreatedBy: h.CreatedBy,
-			Comment:   h.Comment,
-			Size:      layerSize,
-		}}, history...)
-	}
-
-	// Fill in image IDs and tags
-	histImg := img
-	id := img.ID()
-	for _, h := range history {
-		h.ID = id.String()
-
-		var tags []string
-		for _, r := range daemon.referenceStore.References(id) {
-			if _, ok := r.(reference.NamedTagged); ok {
-				tags = append(tags, r.String())
-			}
-		}
-
-		h.Tags = tags
-
-		id = histImg.Parent
-		if id == "" {
-			break
-		}
-		histImg, err = daemon.GetImage(id.String())
-		if err != nil {
-			break
-		}
-	}
-
-	return history, nil
-}
-
-// GetImageID returns an image ID corresponding to the image referred to by
-// refOrID.
-func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
-	id, ref, err := reference.ParseIDOrReference(refOrID)
-	if err != nil {
-		return "", err
-	}
-	if id != "" {
-		if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
-			return "", ErrImageDoesNotExist{refOrID}
-		}
-		return image.ID(id), nil
-	}
-
-	if id, err := daemon.referenceStore.Get(ref); err == nil {
-		return id, nil
-	}
-	if tagged, ok := ref.(reference.NamedTagged); ok {
-		if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
-			for _, namedRef := range daemon.referenceStore.References(id) {
-				if namedRef.Name() == ref.Name() {
-					return id, nil
-				}
-			}
-		}
-	}
-
-	// Search based on ID
-	if id, err := daemon.imageStore.Search(refOrID); err == nil {
-		return id, nil
-	}
-
-	return "", ErrImageDoesNotExist{refOrID}
-}
-
-// GetImage returns an image corresponding to the image referred to by refOrID.
-func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
-	imgID, err := daemon.GetImageID(refOrID)
-	if err != nil {
-		return nil, err
-	}
-	return daemon.imageStore.Get(imgID)
-}
-
-// GetImageOnBuild looks up a Docker image referenced by `name`.
-func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
-	img, err := daemon.GetImage(name)
-	if err != nil {
-		return nil, err
-	}
-	return img, nil
-}
-
 // GraphDriverName returns the name of the graph driver used by the layer.Store
 func (daemon *Daemon) GraphDriverName() string {
 	return daemon.layerStore.DriverName()
@@ -1241,57 +1013,6 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
 	return uid, gid
 }
 
-// GetCachedImage returns the most recent created image that is a child
-// of the image with imgID, that had the same config when it was
-// created. nil is returned if a child cannot be found. An error is
-// returned if the parent image cannot be found.
-func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
-	// Loop on the children of the given image and check the config
-	getMatch := func(siblings []image.ID) (*image.Image, error) {
-		var match *image.Image
-		for _, id := range siblings {
-			img, err := daemon.imageStore.Get(id)
-			if err != nil {
-				return nil, fmt.Errorf("unable to find image %q", id)
-			}
-
-			if runconfig.Compare(&img.ContainerConfig, config) {
-				// check for the most up to date match
-				if match == nil || match.Created.Before(img.Created) {
-					match = img
-				}
-			}
-		}
-		return match, nil
-	}
-
-	// In this case, this is `FROM scratch`, which isn't an actual image.
-	if imgID == "" {
-		images := daemon.imageStore.Map()
-		var siblings []image.ID
-		for id, img := range images {
-			if img.Parent == imgID {
-				siblings = append(siblings, id)
-			}
-		}
-		return getMatch(siblings)
-	}
-
-	// find match from child images
-	siblings := daemon.imageStore.Children(imgID)
-	return getMatch(siblings)
-}
-
-// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
-// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
-func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
-	cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
-	if cache == nil || err != nil {
-		return "", err
-	}
-	return cache.ID().String(), nil
-}
-
 // tempDir returns the default directory to use for temporary files.
 func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
 	var tmpDir string
@@ -1427,12 +1148,85 @@ func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *ty
 	return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx))
 }
 
+var acceptedSearchFilterTags = map[string]bool{
+	"is-automated": true,
+	"is-official":  true,
+	"stars":        true,
+}
+
 // SearchRegistryForImages queries the registry for images matching
 // term. authConfig is used to login.
-func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, term string,
+func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
 	authConfig *types.AuthConfig,
 	headers map[string][]string) (*registrytypes.SearchResults, error) {
-	return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
+
+	searchFilters, err := filters.FromParam(filtersArgs)
+	if err != nil {
+		return nil, err
+	}
+	if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
+		return nil, err
+	}
+
+	unfilteredResult, err := daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
+	if err != nil {
+		return nil, err
+	}
+
+	var isAutomated, isOfficial bool
+	var hasStarFilter = 0
+	if searchFilters.Include("is-automated") {
+		if searchFilters.ExactMatch("is-automated", "true") {
+			isAutomated = true
+		} else if !searchFilters.ExactMatch("is-automated", "false") {
+			return nil, fmt.Errorf("Invalid filter 'is-automated=%s'", searchFilters.Get("is-automated"))
+		}
+	}
+	if searchFilters.Include("is-official") {
+		if searchFilters.ExactMatch("is-official", "true") {
+			isOfficial = true
+		} else if !searchFilters.ExactMatch("is-official", "false") {
+			return nil, fmt.Errorf("Invalid filter 'is-official=%s'", searchFilters.Get("is-official"))
+		}
+	}
+	if searchFilters.Include("stars") {
+		hasStars := searchFilters.Get("stars")
+		for _, hasStar := range hasStars {
+			iHasStar, err := strconv.Atoi(hasStar)
+			if err != nil {
+				return nil, fmt.Errorf("Invalid filter 'stars=%s'", hasStar)
+			}
+			if iHasStar > hasStarFilter {
+				hasStarFilter = iHasStar
+			}
+		}
+	}
+
+	filteredResults := []registrytypes.SearchResult{}
+	for _, result := range unfilteredResult.Results {
+		if searchFilters.Include("is-automated") {
+			if isAutomated != result.IsAutomated {
+				continue
+			}
+		}
+		if searchFilters.Include("is-official") {
+			if isOfficial != result.IsOfficial {
+				continue
+			}
+		}
+		if searchFilters.Include("stars") {
+			if result.StarCount < hasStarFilter {
+				continue
+			}
+		}
+		filteredResults = append(filteredResults, result)
+	}
+
+	return &registrytypes.SearchResults{
+		Query:      unfilteredResult.Query,
+		NumResults: len(filteredResults),
+		Results:    filteredResults,
+	}, nil
 }
 
 // IsShuttingDown tells whether the daemon is shutting down or not
@@ -1539,6 +1333,11 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
 func (daemon *Daemon) Reload(config *Config) error {
 	daemon.configStore.reloadLock.Lock()
 	defer daemon.configStore.reloadLock.Unlock()
+
+	if err := daemon.reloadClusterDiscovery(config); err != nil {
+		return err
+	}
+
 	if config.IsValueSet("labels") {
 		daemon.configStore.Labels = config.Labels
 	}
@@ -1572,7 +1371,28 @@ func (daemon *Daemon) Reload(config *Config) error {
 		daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
 	}
 
-	return daemon.reloadClusterDiscovery(config)
+	// We emit daemon reload event here with updatable configurations
+	attributes := map[string]string{}
+	attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
+	attributes["cluster-store"] = daemon.configStore.ClusterStore
+	if daemon.configStore.ClusterOpts != nil {
+		opts, _ := json.Marshal(daemon.configStore.ClusterOpts)
+		attributes["cluster-store-opts"] = string(opts)
+	} else {
+		attributes["cluster-store-opts"] = "{}"
+	}
+	attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
+	if daemon.configStore.Labels != nil {
+		labels, _ := json.Marshal(daemon.configStore.Labels)
+		attributes["labels"] = string(labels)
+	} else {
+		attributes["labels"] = "[]"
+	}
+	attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
+	attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
+	daemon.LogDaemonEventWithAttributes("reload", attributes)
+
+	return nil
 }
 
 func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {

+ 159 - 0
daemon/daemon_solaris.go

@@ -0,0 +1,159 @@
+// +build solaris,cgo
+
+package daemon
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/container"
+	"github.com/docker/docker/image"
+	"github.com/docker/docker/layer"
+	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/pkg/parsers/kernel"
+	"github.com/docker/docker/reference"
+	"github.com/docker/engine-api/types"
+	containertypes "github.com/docker/engine-api/types/container"
+	"github.com/docker/libnetwork"
+	nwconfig "github.com/docker/libnetwork/config"
+)
+
+//#include <zone.h>
+import "C"
+
+const (
+	defaultVirtualSwitch = "Virtual Switch"
+	platformSupported    = true
+	solarisMinCPUShares  = 1
+	solarisMaxCPUShares  = 65535
+)
+
+func (daemon *Daemon) cleanupMountsByID(id string) error {
+	return nil
+}
+
+func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
+	return nil
+}
+
+func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
+	return nil, nil, nil
+}
+
+func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
+	return nil
+}
+
+// setupInitLayer populates a directory with mountpoints suitable
+// for bind-mounting dockerinit into the container. The mountpoint is simply an
+// empty file at /.dockerinit
+//
+// This extra layer is used by all containers as the top-most ro layer. It protects
+// the container from unwanted side-effects on the rw layer.
+func setupInitLayer(initLayer string, rootUID, rootGID int) error {
+	return nil
+}
+
+func checkKernel() error {
+	// solaris can rely upon checkSystem() below, we don't skew kernel versions
+	return nil
+}
+
+func (daemon *Daemon) getCgroupDriver() string {
+	return ""
+}
+
+func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
+	return nil
+}
+
+// verifyPlatformContainerSettings performs platform-specific validation of the
+// hostconfig and config structures.
+func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
+	warnings := []string{}
+	return warnings, nil
+}
+
+// verifyDaemonSettings performs validation of daemon config struct
+func verifyDaemonSettings(config *Config) error {
+	// checkSystem validates platform-specific requirements
+	return nil
+}
+
+func checkSystem() error {
+	// check OS version for compatibility, ensure running in global zone
+	var err error
+	var id C.zoneid_t
+
+	if id, err = C.getzoneid(); err != nil {
+		return fmt.Errorf("Exiting. Error getting zone id: %+v", err)
+	}
+	if int(id) != 0 {
+		return fmt.Errorf("Exiting because the Docker daemon is not running in the global zone")
+	}
+
+	v, err := kernel.GetKernelVersion()
+	if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 5, Major: 12, Minor: 0}) < 0 {
+		return fmt.Errorf("Your Solaris kernel version: %s doesn't support Docker. Please upgrade to 5.12.0", v.String())
+	}
+	return err
+}
+
+// configureMaxThreads sets the Go runtime max threads threshold
+// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
+func configureMaxThreads(config *Config) error {
+	return nil
+}
+
+// configureKernelSecuritySupport configures and validate security support for the kernel
+func configureKernelSecuritySupport(config *Config, driverName string) error {
+	return nil
+}
+
+func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) {
+	return nil, nil
+}
+
+// registerLinks sets up links between containers and writes the
+// configuration out for persistence.
+func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
+	return nil
+}
+
+func (daemon *Daemon) cleanupMounts() error {
+	return nil
+}
+
+// conditionalMountOnStart is a platform specific helper function during the
+// container start to call mount.
+func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
+	return nil
+}
+
+// conditionalUnmountOnCleanup is a platform specific helper function called
+// during the cleanup of a container to unmount.
+func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
+	return daemon.Unmount(container)
+}
+
+func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
+	// Solaris has no custom images to register
+	return nil
+}
+
+func driverOptions(config *Config) []nwconfig.Option {
+	return []nwconfig.Option{}
+}
+
+func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
+	return nil, nil
+}
+
+// setDefaultIsolation determine the default isolation mode for the
+// daemon to run in. This is only applicable on Windows
+func (daemon *Daemon) setDefaultIsolation() error {
+	return nil
+}
+
+func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
+	return types.RootFS{}
+}

+ 3 - 1
daemon/daemon_unix.go

@@ -488,7 +488,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
 	if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
 		return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
 	}
-	if sysInfo.IPv4ForwardingDisabled {
+
+	// ip-forwarding does not affect container with '--net=host' (or '--net=none')
+	if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) {
 		warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
 		logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
 	}

+ 1 - 1
daemon/daemon_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux,!freebsd,!windows
+// +build !linux,!freebsd,!windows,!solaris
 
 package daemon
 

+ 10 - 0
daemon/daemon_windows.go

@@ -470,6 +470,10 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
 // daemon to run in. This is only applicable on Windows
 func (daemon *Daemon) setDefaultIsolation() error {
 	daemon.defaultIsolation = containertypes.Isolation("process")
+	// On client SKUs, default to Hyper-V
+	if system.IsWindowsClient() {
+		daemon.defaultIsolation = containertypes.Isolation("hyperv")
+	}
 	for _, option := range daemon.configStore.ExecOptions {
 		key, val, err := parsers.ParseKeyValueOpt(option)
 		if err != nil {
@@ -485,6 +489,12 @@ func (daemon *Daemon) setDefaultIsolation() error {
 			if containertypes.Isolation(val).IsHyperV() {
 				daemon.defaultIsolation = containertypes.Isolation("hyperv")
 			}
+			if containertypes.Isolation(val).IsProcess() {
+				if system.IsWindowsClient() {
+					return fmt.Errorf("Windows client operating systems only support Hyper-V containers")
+				}
+				daemon.defaultIsolation = containertypes.Isolation("process")
+			}
 		default:
 			return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)
 		}

+ 1 - 1
daemon/debugtrap_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux,!darwin,!freebsd,!windows
+// +build !linux,!darwin,!freebsd,!windows,!solaris
 
 package daemon
 

+ 14 - 0
daemon/events.go

@@ -80,6 +80,20 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
 	daemon.EventsService.Log(action, events.NetworkEventType, actor)
 }
 
+// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
+func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
+	if daemon.EventsService != nil {
+		if info, err := daemon.SystemInfo(); err == nil && info.Name != "" {
+			attributes["name"] = info.Name
+		}
+		actor := events.Actor{
+			ID:         daemon.ID,
+			Attributes: attributes,
+		}
+		daemon.EventsService.Log(action, events.DaemonEventType, actor)
+	}
+}
+
 // SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
 func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
 	ef := daemonevents.NewFilter(filter)

+ 5 - 0
daemon/events/filter.go

@@ -20,6 +20,7 @@ func NewFilter(filter filters.Args) *Filter {
 func (ef *Filter) Include(ev events.Message) bool {
 	return ef.filter.ExactMatch("event", ev.Action) &&
 		ef.filter.ExactMatch("type", ev.Type) &&
+		ef.matchDaemon(ev) &&
 		ef.matchContainer(ev) &&
 		ef.matchVolume(ev) &&
 		ef.matchNetwork(ev) &&
@@ -34,6 +35,10 @@ func (ef *Filter) matchLabels(attributes map[string]string) bool {
 	return ef.filter.MatchKVList("label", attributes)
 }
 
+func (ef *Filter) matchDaemon(ev events.Message) bool {
+	return ef.fuzzyMatchName(ev, events.DaemonEventType)
+}
+
 func (ef *Filter) matchContainer(ev events.Message) bool {
 	return ef.fuzzyMatchName(ev, events.ContainerEventType)
 }

+ 11 - 0
daemon/exec_solaris.go

@@ -0,0 +1,11 @@
+package daemon
+
+import (
+	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/exec"
+	"github.com/docker/docker/libcontainerd"
+)
+
+func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error {
+	return nil
+}

+ 8 - 0
daemon/graphdriver/aufs/aufs.go

@@ -70,6 +70,7 @@ type Driver struct {
 	root          string
 	uidMaps       []idtools.IDMap
 	gidMaps       []idtools.IDMap
+	ctr           *graphdriver.RefCounter
 	pathCacheLock sync.Mutex
 	pathCache     map[string]string
 }
@@ -108,6 +109,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		uidMaps:   uidMaps,
 		gidMaps:   gidMaps,
 		pathCache: make(map[string]string),
+		ctr:       graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)),
 	}
 
 	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
@@ -320,6 +322,9 @@ func (a *Driver) Get(id, mountLabel string) (string, error) {
 			m = a.getMountpoint(id)
 		}
 	}
+	if count := a.ctr.Increment(m); count > 1 {
+		return m, nil
+	}
 
 	// If a dir does not have a parent ( no layers )do not try to mount
 	// just return the diff path to the data
@@ -344,6 +349,9 @@ func (a *Driver) Put(id string) error {
 		a.pathCache[id] = m
 	}
 	a.pathCacheLock.Unlock()
+	if count := a.ctr.Decrement(m); count > 0 {
+		return nil
+	}
 
 	err := a.unmount(m)
 	if err != nil {

+ 47 - 12
daemon/graphdriver/counter.go

@@ -2,31 +2,66 @@ package graphdriver
 
 import "sync"
 
+type minfo struct {
+	check bool
+	count int
+}
+
 // RefCounter is a generic counter for use by graphdriver Get/Put calls
 type RefCounter struct {
-	counts map[string]int
-	mu     sync.Mutex
+	counts  map[string]*minfo
+	mu      sync.Mutex
+	checker Checker
 }
 
 // NewRefCounter returns a new RefCounter
-func NewRefCounter() *RefCounter {
-	return &RefCounter{counts: make(map[string]int)}
+func NewRefCounter(c Checker) *RefCounter {
+	return &RefCounter{
+		checker: c,
+		counts:  make(map[string]*minfo),
+	}
 }
 
 // Increment increaes the ref count for the given id and returns the current count
-func (c *RefCounter) Increment(id string) int {
+func (c *RefCounter) Increment(path string) int {
 	c.mu.Lock()
-	c.counts[id]++
-	count := c.counts[id]
+	m := c.counts[path]
+	if m == nil {
+		m = &minfo{}
+		c.counts[path] = m
+	}
+	// if we are checking this path for the first time check to make sure
+	// if it was already mounted on the system and make sure we have a correct ref
+	// count if it is mounted as it is in use.
+	if !m.check {
+		m.check = true
+		if c.checker.IsMounted(path) {
+			m.count++
+		}
+	}
+	m.count++
 	c.mu.Unlock()
-	return count
+	return m.count
 }
 
 // Decrement decreases the ref count for the given id and returns the current count
-func (c *RefCounter) Decrement(id string) int {
+func (c *RefCounter) Decrement(path string) int {
 	c.mu.Lock()
-	c.counts[id]--
-	count := c.counts[id]
+	m := c.counts[path]
+	if m == nil {
+		m = &minfo{}
+		c.counts[path] = m
+	}
+	// if we are checking this path for the first time check to make sure
+	// if it was already mounted on the system and make sure we have a correct ref
+	// count if it is mounted as it is in use.
+	if !m.check {
+		m.check = true
+		if c.checker.IsMounted(path) {
+			m.count++
+		}
+	}
+	m.count--
 	c.mu.Unlock()
-	return count
+	return m.count
 }

+ 10 - 10
daemon/graphdriver/devmapper/driver.go

@@ -47,7 +47,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		home:      home,
 		uidMaps:   uidMaps,
 		gidMaps:   gidMaps,
-		ctr:       graphdriver.NewRefCounter(),
+		ctr:       graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
 	}
 
 	return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
@@ -160,35 +160,35 @@ func (d *Driver) Remove(id string) error {
 // Get mounts a device with given id into the root filesystem
 func (d *Driver) Get(id, mountLabel string) (string, error) {
 	mp := path.Join(d.home, "mnt", id)
-	if count := d.ctr.Increment(id); count > 1 {
+	if count := d.ctr.Increment(mp); count > 1 {
 		return mp, nil
 	}
 
 	uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
 	if err != nil {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mp)
 		return "", err
 	}
 
 	// Create the target directories if they don't exist
 	if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mp)
 		return "", err
 	}
 	if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mp)
 		return "", err
 	}
 
 	// Mount the device
 	if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mp)
 		return "", err
 	}
 
 	rootFs := path.Join(mp, "rootfs")
 	if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mp)
 		d.DeviceSet.UnmountDevice(id, mp)
 		return "", err
 	}
@@ -198,7 +198,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 		// Create an "id" file with the container/image id in it to help reconstruct this in case
 		// of later problems
 		if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
-			d.ctr.Decrement(id)
+			d.ctr.Decrement(mp)
 			d.DeviceSet.UnmountDevice(id, mp)
 			return "", err
 		}
@@ -209,10 +209,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 
 // Put unmounts a device and removes it.
 func (d *Driver) Put(id string) error {
-	if count := d.ctr.Decrement(id); count > 0 {
+	mp := path.Join(d.home, "mnt", id)
+	if count := d.ctr.Decrement(mp); count > 0 {
 		return nil
 	}
-	mp := path.Join(d.home, "mnt", id)
 	err := d.DeviceSet.UnmountDevice(id, mp)
 	if err != nil {
 		logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)

+ 6 - 0
daemon/graphdriver/driver.go

@@ -113,6 +113,12 @@ type FileGetCloser interface {
 	Close() error
 }
 
+// Checker makes checks on specified filesystems.
+type Checker interface {
+	// IsMounted returns true if the provided path is mounted for the specific checker
+	IsMounted(path string) bool
+}
+
 func init() {
 	drivers = make(map[string]InitFunc)
 }

+ 32 - 0
daemon/graphdriver/driver_linux.go

@@ -5,6 +5,8 @@ package graphdriver
 import (
 	"path/filepath"
 	"syscall"
+
+	"github.com/docker/docker/pkg/mount"
 )
 
 const (
@@ -89,6 +91,36 @@ func GetFSMagic(rootpath string) (FsMagic, error) {
 	return FsMagic(buf.Type), nil
 }
 
+// NewFsChecker returns a checker configured for the provied FsMagic
+func NewFsChecker(t FsMagic) Checker {
+	return &fsChecker{
+		t: t,
+	}
+}
+
+type fsChecker struct {
+	t FsMagic
+}
+
+func (c *fsChecker) IsMounted(path string) bool {
+	m, _ := Mounted(c.t, path)
+	return m
+}
+
+// NewDefaultChecker returns a check that parses /proc/mountinfo to check
+// if the specified path is mounted.
+func NewDefaultChecker() Checker {
+	return &defaultChecker{}
+}
+
+type defaultChecker struct {
+}
+
+func (c *defaultChecker) IsMounted(path string) bool {
+	m, _ := mount.Mounted(path)
+	return m
+}
+
 // Mounted checks if the given path is mounted as the fs type
 func Mounted(fsType FsMagic, mountPath string) (bool, error) {
 	var buf syscall.Statfs_t

+ 65 - 0
daemon/graphdriver/driver_solaris.go

@@ -0,0 +1,65 @@
+// +build solaris,cgo
+
+package graphdriver
+
+/*
+#include <sys/statvfs.h>
+#include <stdlib.h>
+
+static inline struct statvfs *getstatfs(char *s) {
+        struct statvfs *buf;
+        int err;
+        buf = (struct statvfs *)malloc(sizeof(struct statvfs));
+        err = statvfs(s, buf);
+        return buf;
+}
+*/
+import "C"
+import (
+	"path/filepath"
+	"unsafe"
+
+	log "github.com/Sirupsen/logrus"
+)
+
+const (
+	// FsMagicZfs filesystem id for Zfs
+	FsMagicZfs = FsMagic(0x2fc12fc1)
+)
+
+var (
+	// Slice of drivers that should be used in an order
+	priority = []string{
+		"zfs",
+	}
+
+	// FsNames maps filesystem id to name of the filesystem.
+	FsNames = map[FsMagic]string{
+		FsMagicZfs: "zfs",
+	}
+)
+
+// GetFSMagic returns the filesystem id given the path.
+func GetFSMagic(rootpath string) (FsMagic, error) {
+	return 0, nil
+}
+
+// Mounted checks if the given path is mounted as the fs type
+//Solaris supports only ZFS for now
+func Mounted(fsType FsMagic, mountPath string) (bool, error) {
+
+	cs := C.CString(filepath.Dir(mountPath))
+	buf := C.getstatfs(cs)
+
+	// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
+	if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
+		(buf.f_basetype[3] != 0) {
+		log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", mountPath)
+		C.free(unsafe.Pointer(buf))
+		return false, ErrPrerequisites
+	}
+
+	C.free(unsafe.Pointer(buf))
+	C.free(unsafe.Pointer(cs))
+	return true, nil
+}

+ 1 - 1
daemon/graphdriver/driver_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux,!windows,!freebsd
+// +build !linux,!windows,!freebsd,!solaris
 
 package graphdriver
 

+ 0 - 2
daemon/graphdriver/driver_windows.go

@@ -4,8 +4,6 @@ var (
 	// Slice of drivers that should be used in order
 	priority = []string{
 		"windowsfilter",
-		"windowsdiff",
-		"vfs",
 	}
 )
 

+ 28 - 78
daemon/graphdriver/overlay/overlay.go

@@ -9,7 +9,6 @@ import (
 	"os"
 	"os/exec"
 	"path"
-	"sync"
 	"syscall"
 
 	"github.com/Sirupsen/logrus"
@@ -92,12 +91,10 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Rea
 
 // Driver contains information about the home directory and the list of active mounts that are created using this driver.
 type Driver struct {
-	home          string
-	pathCacheLock sync.Mutex
-	pathCache     map[string]string
-	uidMaps       []idtools.IDMap
-	gidMaps       []idtools.IDMap
-	ctr           *graphdriver.RefCounter
+	home    string
+	uidMaps []idtools.IDMap
+	gidMaps []idtools.IDMap
+	ctr     *graphdriver.RefCounter
 }
 
 func init() {
@@ -141,11 +138,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 	}
 
 	d := &Driver{
-		home:      home,
-		pathCache: make(map[string]string),
-		uidMaps:   uidMaps,
-		gidMaps:   gidMaps,
-		ctr:       graphdriver.NewRefCounter(),
+		home:    home,
+		uidMaps: uidMaps,
+		gidMaps: gidMaps,
+		ctr:     graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
 	}
 
 	return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
@@ -328,110 +324,64 @@ func (d *Driver) Remove(id string) error {
 	if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
 		return err
 	}
-	d.pathCacheLock.Lock()
-	delete(d.pathCache, id)
-	d.pathCacheLock.Unlock()
 	return nil
 }
 
 // Get creates and mounts the required file system for the given id and returns the mount path.
-func (d *Driver) Get(id string, mountLabel string) (string, error) {
+func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
 	dir := d.dir(id)
 	if _, err := os.Stat(dir); err != nil {
 		return "", err
 	}
-
 	// If id has a root, just return it
 	rootDir := path.Join(dir, "root")
 	if _, err := os.Stat(rootDir); err == nil {
-		d.pathCacheLock.Lock()
-		d.pathCache[id] = rootDir
-		d.pathCacheLock.Unlock()
 		return rootDir, nil
 	}
-
-	lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
-	if err != nil {
-		return "", err
-	}
-	lowerDir := path.Join(d.dir(string(lowerID)), "root")
-	upperDir := path.Join(dir, "upper")
-	workDir := path.Join(dir, "work")
 	mergedDir := path.Join(dir, "merged")
-
-	if count := d.ctr.Increment(id); count > 1 {
+	if count := d.ctr.Increment(mergedDir); count > 1 {
 		return mergedDir, nil
 	}
-
-	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
-
-	// if it's mounted already, just return
-	mounted, err := d.mounted(mergedDir)
+	defer func() {
+		if err != nil {
+			if c := d.ctr.Decrement(mergedDir); c <= 0 {
+				syscall.Unmount(mergedDir, 0)
+			}
+		}
+	}()
+	lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
 	if err != nil {
-		d.ctr.Decrement(id)
 		return "", err
 	}
-	if mounted {
-		d.ctr.Decrement(id)
-		return mergedDir, nil
-	}
-
+	var (
+		lowerDir = path.Join(d.dir(string(lowerID)), "root")
+		upperDir = path.Join(dir, "upper")
+		workDir  = path.Join(dir, "work")
+		opts     = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
+	)
 	if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
-		d.ctr.Decrement(id)
 		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
 	}
 	// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
 	// user namespace requires this to move a directory from lower to upper.
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
 	if err != nil {
-		d.ctr.Decrement(id)
-		syscall.Unmount(mergedDir, 0)
 		return "", err
 	}
-
 	if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
-		d.ctr.Decrement(id)
-		syscall.Unmount(mergedDir, 0)
 		return "", err
 	}
-
-	d.pathCacheLock.Lock()
-	d.pathCache[id] = mergedDir
-	d.pathCacheLock.Unlock()
-
 	return mergedDir, nil
 }
 
-func (d *Driver) mounted(dir string) (bool, error) {
-	return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir)
-}
-
 // Put unmounts the mount path created for the give id.
 func (d *Driver) Put(id string) error {
-	if count := d.ctr.Decrement(id); count > 0 {
+	mountpoint := path.Join(d.dir(id), "merged")
+	if count := d.ctr.Decrement(mountpoint); count > 0 {
 		return nil
 	}
-	d.pathCacheLock.Lock()
-	mountpoint, exists := d.pathCache[id]
-	d.pathCacheLock.Unlock()
-
-	if !exists {
-		logrus.Debugf("Put on a non-mounted device %s", id)
-		// but it might be still here
-		if d.Exists(id) {
-			mountpoint = path.Join(d.dir(id), "merged")
-		}
-
-		d.pathCacheLock.Lock()
-		d.pathCache[id] = mountpoint
-		d.pathCacheLock.Unlock()
-	}
-
-	if mounted, err := d.mounted(mountpoint); mounted || err != nil {
-		if err = syscall.Unmount(mountpoint, 0); err != nil {
-			logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
-		}
-		return err
+	if err := syscall.Unmount(mountpoint, 0); err != nil {
+		logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
 	}
 	return nil
 }

+ 1 - 1
daemon/graphdriver/register/register_zfs.go

@@ -1,4 +1,4 @@
-// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd
+// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd, solaris
 
 package register
 

+ 34 - 44
daemon/graphdriver/windows/windows.go

@@ -15,6 +15,7 @@ import (
 	"path/filepath"
 	"strconv"
 	"strings"
+	"sync"
 	"syscall"
 	"time"
 	"unsafe"
@@ -26,7 +27,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/graphdriver"
 	"github.com/docker/docker/pkg/archive"
-	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/longpath"
@@ -35,28 +35,33 @@ import (
 	"github.com/vbatts/tar-split/tar/storage"
 )
 
+// filterDriver is an HCSShim driver type for the Windows Filter driver.
+const filterDriver = 1
+
 // init registers the windows graph drivers to the register.
 func init() {
 	graphdriver.Register("windowsfilter", InitFilter)
-	graphdriver.Register("windowsdiff", InitDiff)
 	reexec.Register("docker-windows-write-layer", writeLayer)
 }
 
-const (
-	// diffDriver is an hcsshim driver type
-	diffDriver = iota
-	// filterDriver is an hcsshim driver type
-	filterDriver
-)
+type checker struct {
+}
+
+func (c *checker) IsMounted(path string) bool {
+	return false
+}
 
 // Driver represents a windows graph driver.
 type Driver struct {
 	// info stores the shim driver information
 	info hcsshim.DriverInfo
+	ctr  *graphdriver.RefCounter
+	// it is safe for windows to use a cache here because it does not support
+	// restoring containers when the daemon dies.
+	cacheMu sync.Mutex
+	cache   map[string]string
 }
 
-var _ graphdriver.DiffGetterDriver = &Driver{}
-
 func isTP5OrOlder() bool {
 	return system.GetOSVersion().Build <= 14300
 }
@@ -69,32 +74,15 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
 			HomeDir: home,
 			Flavour: filterDriver,
 		},
-	}
-	return d, nil
-}
-
-// InitDiff returns a new Windows differencing disk driver.
-func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
-	logrus.Debugf("WindowsGraphDriver InitDiff at %s", home)
-	d := &Driver{
-		info: hcsshim.DriverInfo{
-			HomeDir: home,
-			Flavour: diffDriver,
-		},
+		cache: make(map[string]string),
+		ctr:   graphdriver.NewRefCounter(&checker{}),
 	}
 	return d, nil
 }
 
 // String returns the string representation of a driver.
 func (d *Driver) String() string {
-	switch d.info.Flavour {
-	case diffDriver:
-		return "windowsdiff"
-	case filterDriver:
-		return "windowsfilter"
-	default:
-		return "Unknown driver flavour"
-	}
+	return "Windows filter storage driver"
 }
 
 // Status returns the status of the driver.
@@ -238,17 +226,23 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 	if err != nil {
 		return "", err
 	}
+	if count := d.ctr.Increment(rID); count > 1 {
+		return d.cache[rID], nil
+	}
 
 	// Getting the layer paths must be done outside of the lock.
 	layerChain, err := d.getLayerChain(rID)
 	if err != nil {
+		d.ctr.Decrement(rID)
 		return "", err
 	}
 
 	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
+		d.ctr.Decrement(rID)
 		return "", err
 	}
 	if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
+		d.ctr.Decrement(rID)
 		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
 			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
 		}
@@ -257,11 +251,15 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 
 	mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
 	if err != nil {
+		d.ctr.Decrement(rID)
 		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
 			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
 		}
 		return "", err
 	}
+	d.cacheMu.Lock()
+	d.cache[rID] = mountPath
+	d.cacheMu.Unlock()
 
 	// If the layer has a mount path, use that. Otherwise, use the
 	// folder path.
@@ -282,6 +280,12 @@ func (d *Driver) Put(id string) error {
 	if err != nil {
 		return err
 	}
+	if count := d.ctr.Decrement(rID); count > 0 {
+		return nil
+	}
+	d.cacheMu.Lock()
+	delete(d.cache, rID)
+	d.cacheMu.Unlock()
 
 	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
 		return err
@@ -390,20 +394,6 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
 // new layer in bytes.
 // The layer should not be mounted when calling this function
 func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
-	if d.info.Flavour == diffDriver {
-		start := time.Now().UTC()
-		logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
-		destination := d.dir(id)
-		destination = filepath.Dir(destination)
-		size, err := chrootarchive.ApplyUncompressedLayer(destination, diff, nil)
-		if err != nil {
-			return 0, err
-		}
-		logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
-
-		return size, nil
-	}
-
 	var layerChain []string
 	if parent != "" {
 		rPId, err := d.resolveID(parent)

+ 9 - 9
daemon/graphdriver/zfs/zfs.go

@@ -1,4 +1,4 @@
-// +build linux freebsd
+// +build linux freebsd solaris
 
 package zfs
 
@@ -105,7 +105,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
 		filesystemsCache: filesystemsCache,
 		uidMaps:          uidMaps,
 		gidMaps:          gidMaps,
-		ctr:              graphdriver.NewRefCounter(),
+		ctr:              graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
 	}
 	return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
 }
@@ -307,7 +307,7 @@ func (d *Driver) Remove(id string) error {
 // Get returns the mountpoint for the given id after creating the target directories if necessary.
 func (d *Driver) Get(id, mountLabel string) (string, error) {
 	mountpoint := d.mountPath(id)
-	if count := d.ctr.Increment(id); count > 1 {
+	if count := d.ctr.Increment(mountpoint); count > 1 {
 		return mountpoint, nil
 	}
 
@@ -317,17 +317,17 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
 	if err != nil {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mountpoint)
 		return "", err
 	}
 	// Create the target directories if they don't exist
 	if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mountpoint)
 		return "", err
 	}
 
 	if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mountpoint)
 		return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
 	}
 
@@ -335,7 +335,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 	// permissions instead of the remapped root uid:gid (if user namespaces are enabled):
 	if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
 		mount.Unmount(mountpoint)
-		d.ctr.Decrement(id)
+		d.ctr.Decrement(mountpoint)
 		return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
 	}
 
@@ -344,10 +344,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
 
 // Put removes the existing mountpoint for the given id if it exists.
 func (d *Driver) Put(id string) error {
-	if count := d.ctr.Decrement(id); count > 0 {
+	mountpoint := d.mountPath(id)
+	if count := d.ctr.Decrement(mountpoint); count > 0 {
 		return nil
 	}
-	mountpoint := d.mountPath(id)
 	mounted, err := graphdriver.Mounted(graphdriver.FsMagicZfs, mountpoint)
 	if err != nil || !mounted {
 		return err

+ 59 - 0
daemon/graphdriver/zfs/zfs_solaris.go

@@ -0,0 +1,59 @@
+// +build solaris,cgo
+
+package zfs
+
+/*
+#include <sys/statvfs.h>
+#include <stdlib.h>
+
+static inline struct statvfs *getstatfs(char *s) {
+        struct statvfs *buf;
+        int err;
+        buf = (struct statvfs *)malloc(sizeof(struct statvfs));
+        err = statvfs(s, buf);
+        return buf;
+}
+*/
+import "C"
+import (
+	"path/filepath"
+	"strings"
+	"unsafe"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/docker/daemon/graphdriver"
+)
+
+func checkRootdirFs(rootdir string) error {
+
+	cs := C.CString(filepath.Dir(rootdir))
+	buf := C.getstatfs(cs)
+
+	// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
+	if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
+		(buf.f_basetype[3] != 0) {
+		log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
+		C.free(unsafe.Pointer(buf))
+		return graphdriver.ErrPrerequisites
+	}
+
+	C.free(unsafe.Pointer(buf))
+	C.free(unsafe.Pointer(cs))
+	return nil
+}
+
+/* rootfs is introduced to comply with the OCI spec
+which states that root filesystem must be mounted at <CID>/rootfs/ instead of <CID>/
+*/
+func getMountpoint(id string) string {
+	maxlen := 12
+
+	// we need to preserve filesystem suffix
+	suffix := strings.SplitN(id, "-", 2)
+
+	if len(suffix) > 1 {
+		return filepath.Join(id[:maxlen]+"-"+suffix[1], "rootfs", "root")
+	}
+
+	return filepath.Join(id[:maxlen], "rootfs", "root")
+}

+ 1 - 1
daemon/graphdriver/zfs/zfs_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux,!freebsd
+// +build !linux,!freebsd,!solaris
 
 package zfs
 

+ 124 - 0
daemon/image.go

@@ -0,0 +1,124 @@
+package daemon
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/builder"
+	"github.com/docker/docker/image"
+	"github.com/docker/docker/reference"
+	"github.com/docker/docker/runconfig"
+	containertypes "github.com/docker/engine-api/types/container"
+)
+
+// ErrImageDoesNotExist is error returned when no image can be found for a reference.
+type ErrImageDoesNotExist struct {
+	RefOrID string
+}
+
+func (e ErrImageDoesNotExist) Error() string {
+	return fmt.Sprintf("no such id: %s", e.RefOrID)
+}
+
+// GetImageID returns an image ID corresponding to the image referred to by
+// refOrID.
+func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
+	id, ref, err := reference.ParseIDOrReference(refOrID)
+	if err != nil {
+		return "", err
+	}
+	if id != "" {
+		if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
+			return "", ErrImageDoesNotExist{refOrID}
+		}
+		return image.ID(id), nil
+	}
+
+	if id, err := daemon.referenceStore.Get(ref); err == nil {
+		return id, nil
+	}
+	if tagged, ok := ref.(reference.NamedTagged); ok {
+		if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
+			for _, namedRef := range daemon.referenceStore.References(id) {
+				if namedRef.Name() == ref.Name() {
+					return id, nil
+				}
+			}
+		}
+	}
+
+	// Search based on ID
+	if id, err := daemon.imageStore.Search(refOrID); err == nil {
+		return id, nil
+	}
+
+	return "", ErrImageDoesNotExist{refOrID}
+}
+
+// GetImage returns an image corresponding to the image referred to by refOrID.
+func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
+	imgID, err := daemon.GetImageID(refOrID)
+	if err != nil {
+		return nil, err
+	}
+	return daemon.imageStore.Get(imgID)
+}
+
+// GetImageOnBuild looks up a Docker image referenced by `name`.
+func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
+	img, err := daemon.GetImage(name)
+	if err != nil {
+		return nil, err
+	}
+	return img, nil
+}
+
+// GetCachedImage returns the most recent created image that is a child
+// of the image with imgID, that had the same config when it was
+// created. nil is returned if a child cannot be found. An error is
+// returned if the parent image cannot be found.
+func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
+	// Loop on the children of the given image and check the config
+	getMatch := func(siblings []image.ID) (*image.Image, error) {
+		var match *image.Image
+		for _, id := range siblings {
+			img, err := daemon.imageStore.Get(id)
+			if err != nil {
+				return nil, fmt.Errorf("unable to find image %q", id)
+			}
+
+			if runconfig.Compare(&img.ContainerConfig, config) {
+				// check for the most up to date match
+				if match == nil || match.Created.Before(img.Created) {
+					match = img
+				}
+			}
+		}
+		return match, nil
+	}
+
+	// In this case, this is `FROM scratch`, which isn't an actual image.
+	if imgID == "" {
+		images := daemon.imageStore.Map()
+		var siblings []image.ID
+		for id, img := range images {
+			if img.Parent == imgID {
+				siblings = append(siblings, id)
+			}
+		}
+		return getMatch(siblings)
+	}
+
+	// find match from child images
+	siblings := daemon.imageStore.Children(imgID)
+	return getMatch(siblings)
+}
+
+// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
+// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
+func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
+	cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
+	if cache == nil || err != nil {
+		return "", err
+	}
+	return cache.ID().String(), nil
+}

+ 25 - 0
daemon/image_exporter.go

@@ -0,0 +1,25 @@
+package daemon
+
+import (
+	"io"
+
+	"github.com/docker/docker/image/tarexport"
+)
+
+// ExportImage exports a list of images to the given output stream. The
+// exported images are archived into a tar when written to the output
+// stream. All images with the given tag and all versions containing
+// the same tag are exported. names is the set of tags to export, and
+// outStream is the writer which the images are written to.
+func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
+	return imageExporter.Save(names, outStream)
+}
+
+// LoadImage uploads a set of images into the repository. This is the
+// complement of ImageExport.  The input stream is an uncompressed tar
+// ball containing images and metadata.
+func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
+	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
+	return imageExporter.Load(inTar, outStream, quiet)
+}

+ 82 - 0
daemon/image_history.go

@@ -0,0 +1,82 @@
+package daemon
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/layer"
+	"github.com/docker/docker/reference"
+	"github.com/docker/engine-api/types"
+)
+
+// ImageHistory returns a slice of ImageHistory structures for the specified image
+// name by walking the image lineage.
+func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
+	img, err := daemon.GetImage(name)
+	if err != nil {
+		return nil, err
+	}
+
+	history := []*types.ImageHistory{}
+
+	layerCounter := 0
+	rootFS := *img.RootFS
+	rootFS.DiffIDs = nil
+
+	for _, h := range img.History {
+		var layerSize int64
+
+		if !h.EmptyLayer {
+			if len(img.RootFS.DiffIDs) <= layerCounter {
+				return nil, fmt.Errorf("too many non-empty layers in History section")
+			}
+
+			rootFS.Append(img.RootFS.DiffIDs[layerCounter])
+			l, err := daemon.layerStore.Get(rootFS.ChainID())
+			if err != nil {
+				return nil, err
+			}
+			layerSize, err = l.DiffSize()
+			layer.ReleaseAndLog(daemon.layerStore, l)
+			if err != nil {
+				return nil, err
+			}
+
+			layerCounter++
+		}
+
+		history = append([]*types.ImageHistory{{
+			ID:        "<missing>",
+			Created:   h.Created.Unix(),
+			CreatedBy: h.CreatedBy,
+			Comment:   h.Comment,
+			Size:      layerSize,
+		}}, history...)
+	}
+
+	// Fill in image IDs and tags
+	histImg := img
+	id := img.ID()
+	for _, h := range history {
+		h.ID = id.String()
+
+		var tags []string
+		for _, r := range daemon.referenceStore.References(id) {
+			if _, ok := r.(reference.NamedTagged); ok {
+				tags = append(tags, r.String())
+			}
+		}
+
+		h.Tags = tags
+
+		id = histImg.Parent
+		if id == "" {
+			break
+		}
+		histImg, err = daemon.GetImage(id.String())
+		if err != nil {
+			break
+		}
+	}
+
+	return history, nil
+}

+ 81 - 0
daemon/image_inspect.go

@@ -0,0 +1,81 @@
+package daemon
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/docker/docker/layer"
+	"github.com/docker/docker/reference"
+	"github.com/docker/engine-api/types"
+)
+
+// LookupImage looks up an image by name and returns it as an ImageInspect
+// structure.
+func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
+	img, err := daemon.GetImage(name)
+	if err != nil {
+		return nil, fmt.Errorf("No such image: %s", name)
+	}
+
+	refs := daemon.referenceStore.References(img.ID())
+	repoTags := []string{}
+	repoDigests := []string{}
+	for _, ref := range refs {
+		switch ref.(type) {
+		case reference.NamedTagged:
+			repoTags = append(repoTags, ref.String())
+		case reference.Canonical:
+			repoDigests = append(repoDigests, ref.String())
+		}
+	}
+
+	var size int64
+	var layerMetadata map[string]string
+	layerID := img.RootFS.ChainID()
+	if layerID != "" {
+		l, err := daemon.layerStore.Get(layerID)
+		if err != nil {
+			return nil, err
+		}
+		defer layer.ReleaseAndLog(daemon.layerStore, l)
+		size, err = l.Size()
+		if err != nil {
+			return nil, err
+		}
+
+		layerMetadata, err = l.Metadata()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	comment := img.Comment
+	if len(comment) == 0 && len(img.History) > 0 {
+		comment = img.History[len(img.History)-1].Comment
+	}
+
+	imageInspect := &types.ImageInspect{
+		ID:              img.ID().String(),
+		RepoTags:        repoTags,
+		RepoDigests:     repoDigests,
+		Parent:          img.Parent.String(),
+		Comment:         comment,
+		Created:         img.Created.Format(time.RFC3339Nano),
+		Container:       img.Container,
+		ContainerConfig: &img.ContainerConfig,
+		DockerVersion:   img.DockerVersion,
+		Author:          img.Author,
+		Config:          img.Config,
+		Architecture:    img.Architecture,
+		Os:              img.OS,
+		Size:            size,
+		VirtualSize:     size, // TODO: field unused, deprecate
+		RootFS:          rootFSToAPIType(img.RootFS),
+	}
+
+	imageInspect.GraphDriver.Name = daemon.GraphDriverName()
+
+	imageInspect.GraphDriver.Data = layerMetadata
+
+	return imageInspect, nil
+}

+ 40 - 0
daemon/inspect_solaris.go

@@ -0,0 +1,40 @@
+package daemon
+
+import (
+	"github.com/docker/docker/api/types/backend"
+	"github.com/docker/docker/container"
+	"github.com/docker/docker/daemon/exec"
+	"github.com/docker/engine-api/types"
+)
+
+// This sets platform-specific fields
+func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
+	return contJSONBase
+}
+
+// containerInspectPre120 get containers for pre 1.20 APIs.
+func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) {
+	return daemon.containerInspectCurrent(name, false)
+}
+
+func addMountPoints(container *container.Container) []types.MountPoint {
+	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
+}
+
+func inspectExecProcessConfig(e *exec.Config) *backend.ExecProcessConfig {
+	return &backend.ExecProcessConfig{
+		Tty:        e.Tty,
+		Entrypoint: e.Entrypoint,
+		Arguments:  e.Args,
+	}
+}

+ 1 - 1
daemon/inspect_unix.go

@@ -1,4 +1,4 @@
-// +build !windows
+// +build !windows,!solaris
 
 package daemon
 

+ 1 - 1
daemon/list_unix.go

@@ -1,4 +1,4 @@
-// +build linux freebsd
+// +build linux freebsd solaris
 
 package daemon
 

+ 18 - 0
daemon/monitor_solaris.go

@@ -0,0 +1,18 @@
+package daemon
+
+import (
+	"github.com/docker/docker/container"
+	"github.com/docker/docker/libcontainerd"
+)
+
+// platformConstructExitStatus returns a platform specific exit status structure
+func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatus {
+	return &container.ExitStatus{
+		ExitCode: int(e.ExitCode),
+	}
+}
+
+// postRunProcessing perfoms any processing needed on the container after it has stopped.
+func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
+	return nil
+}

+ 12 - 0
daemon/oci_solaris.go

@@ -0,0 +1,12 @@
+package daemon
+
+import (
+	"github.com/docker/docker/container"
+	"github.com/docker/docker/libcontainerd"
+	"github.com/docker/docker/oci"
+)
+
+func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
+	s := oci.DefaultSpec()
+	return (*libcontainerd.Spec)(&s), nil
+}

+ 1 - 1
daemon/seccomp_linux.go

@@ -35,7 +35,7 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
 			return err
 		}
 	} else {
-		profile, err = seccomp.GetDefaultProfile()
+		profile, err = seccomp.GetDefaultProfile(rs)
 		if err != nil {
 			return err
 		}

+ 34 - 0
daemon/stats_collector_solaris.go

@@ -0,0 +1,34 @@
+package daemon
+
+import (
+	"github.com/docker/docker/container"
+	"time"
+)
+
+// newStatsCollector returns a new statsCollector for collection stats
+// for a registered container at the specified interval. The collector allows
+// non-running containers to be added and will start processing stats when
+// they are started.
+func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector {
+	return &statsCollector{}
+}
+
+// statsCollector manages and provides container resource stats
+type statsCollector struct {
+}
+
+// collect registers the container with the collector and adds it to
+// the event loop for collection on the specified interval returning
+// a channel for the subscriber to receive on.
+func (s *statsCollector) collect(c *container.Container) chan interface{} {
+	return nil
+}
+
+// stopCollection closes the channels for all subscribers and removes
+// the container from metrics collection.
+func (s *statsCollector) stopCollection(c *container.Container) {
+}
+
+// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
+func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
+}

+ 1 - 1
daemon/stats_collector_unix.go

@@ -1,4 +1,4 @@
-// +build !windows
+// +build !windows,!solaris
 
 package daemon
 

+ 11 - 0
daemon/update_solaris.go

@@ -0,0 +1,11 @@
+package daemon
+
+import (
+	"github.com/docker/docker/libcontainerd"
+	"github.com/docker/engine-api/types/container"
+)
+
+func toContainerdResources(resources container.Resources) libcontainerd.Resources {
+	var r libcontainerd.Resources
+	return r
+}

+ 0 - 4
distribution/xfer/download_test.go

@@ -121,10 +121,6 @@ func (ls *mockLayerStore) GetMountID(string) (string, error) {
 	return "", errors.New("not implemented")
 }
 
-func (ls *mockLayerStore) ReinitRWLayer(layer.RWLayer) error {
-	return errors.New("not implemented")
-}
-
 func (ls *mockLayerStore) Cleanup() error {
 	return nil
 }

+ 2 - 3
docs/Makefile

@@ -23,9 +23,8 @@ HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER
 HUGO_BIND_IP=0.0.0.0
 
 GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
-DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
-DOCKER_DOCS_IMAGE := docs-base$(if $(GIT_BRANCH),:$(GIT_BRANCH))
-
+GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
+DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
 
 DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) -e AWS_S3_BUCKET -e NOCACHE
 

+ 7 - 0
docs/breaking_changes.md

@@ -22,6 +22,13 @@ Unfortunately, Docker is a fast moving project, and newly introduced features
 may sometime introduce breaking changes and/or incompatibilities. This page
 documents these by Engine version.
 
+# Engine 1.12
+
+Docker clients <= 1.9.2 used an invalid Host header when making request to the
+daemon. Docker 1.12 is built using golang 1.6 which is now checking the validity
+of the Host header and as such clients <= 1.9.2 can't talk anymore to the daemon. 
+[An environment variable was added to overcome this issue.](reference/commandline/dockerd.md#miscellaneous-options)
+
 # Engine 1.10
 
 There were two breaking changes in the 1.10 release.

+ 9 - 0
docs/deprecated.md

@@ -58,6 +58,15 @@ defining it at container creation (`POST /containers/create`).
 The `docker ps --before` and `docker ps --since` options are deprecated.
 Use `docker ps --filter=before=...` and `docker ps --filter=since=...` instead.
 
+### Docker search 'automated' and 'stars' options
+
+**Deprecated in Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
+
+**Removed In Release: v1.14**
+
+The `docker search --automated` and `docker search --stars` options are deprecated.
+Use `docker search --filter=is-automated=...` and `docker search --filter=stars=...` instead.
+
 ### Command line short variant options
 **Deprecated In Release: v1.9**
 

+ 3 - 0
docs/reference/api/docker_remote_api.md

@@ -118,6 +118,9 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /containers/create` now takes `MaximumIOps` and `MaximumIOBps` fields. Windows daemon only.
 * `POST /containers/create` now returns a HTTP 400 "bad parameter" message
   if no command is specified (instead of a HTTP 500 "server error")
+* `GET /images/search` now takes a `filters` query parameter.
+* `GET /events` now supports a `reload` event that is emitted when the daemon configuration is reloaded.
+* `GET /events` now supports filtering by daemon name or ID.
 
 ### v1.23 API changes
 

+ 10 - 1
docs/reference/api/docker_remote_api_v1.24.md

@@ -2133,6 +2133,10 @@ Search for an image on [Docker Hub](https://hub.docker.com).
 Query Parameters:
 
 -   **term** – term to search
+-   **filters** – a JSON encoded value of the filters (a map[string][]string) to process on the images list. Available filters:
+  -   `stars=<number>`
+  -   `is-automated=(true|false)`
+  -   `is-official=(true|false)`
 
 Status Codes:
 
@@ -2416,6 +2420,10 @@ Docker networks report the following events:
 
     create, connect, disconnect, destroy
 
+Docker daemon report the following event:
+
+    reload
+
 **Example request**:
 
     GET /events?since=1374067924
@@ -2585,9 +2593,10 @@ Query Parameters:
   -   `event=<string>`; -- event to filter
   -   `image=<string>`; -- image to filter
   -   `label=<string>`; -- image and container label to filter
-  -   `type=<string>`; -- either `container` or `image` or `volume` or `network`
+  -   `type=<string>`; -- either `container` or `image` or `volume` or `network` or `daemon`
   -   `volume=<string>`; -- volume to filter
   -   `network=<string>`; -- network to filter
+  -   `daemon=<string>`; -- daemon name or id to filter
 
 Status Codes:
 

+ 9 - 0
docs/reference/builder.md

@@ -362,6 +362,15 @@ RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
 > `RUN [ "echo", "$HOME" ]` will not do variable substitution on `$HOME`.
 > If you want shell processing then either use the *shell* form or execute
 > a shell directly, for example: `RUN [ "sh", "-c", "echo $HOME" ]`.
+>
+> **Note**:
+> In the *JSON* form, it is necessary to escape backslashes. This is
+> particularly relevant on Windows where the backslash is the path seperator.
+> The following line would otherwise be treated as *shell* form due to not
+> being valid JSON, and fail in an unexpected way:
+> `RUN ["c:\windows\system32\tasklist.exe"]`
+> The correct syntax for this example is:
+> `RUN ["c:\\windows\\system32\\tasklist.exe"]`
 
 The cache for `RUN` instructions isn't invalidated automatically during
 the next build. The cache for an instruction like

+ 5 - 1
docs/reference/commandline/build.md

@@ -225,7 +225,8 @@ uploaded context. The builder reference contains detailed information on
     $ docker build -t vieux/apache:2.0 .
 
 This will build like the previous example, but it will then tag the resulting
-image. The repository name will be `vieux/apache` and the tag will be `2.0`
+image. The repository name will be `vieux/apache` and the tag will be `2.0`.
+[Read more about valid tags](tag.md).
 
 You can apply multiple tags to an image. For example, you can apply the `latest`
 tag to a newly built image and add another tag that references a specific
@@ -298,6 +299,9 @@ accessed like regular environment variables in the `RUN` instruction of the
 Dockerfile. Also, these values don't persist in the intermediate or final images
 like `ENV` values do.
 
+Using this flag will not alter the output you see when the `ARG` lines from the
+Dockerfile are echoed during the build process.
+
 For detailed information on using `ARG` and `ENV` instructions, see the
 [Dockerfile reference](../builder.md).
 

+ 1 - 0
docs/reference/commandline/commit.md

@@ -24,6 +24,7 @@ It can be useful to commit a container's file changes or settings into a new
 image. This allows you debug a container by running an interactive shell, or to
 export a working dataset to another server. Generally, it is better to use
 Dockerfiles to manage your images in a documented and maintainable way.
+[Read more about valid image names and tags](tag.md).
 
 The commit operation will not include any data contained in
 volumes mounted inside the container.

+ 11 - 1
docs/reference/commandline/cp.md

@@ -81,7 +81,17 @@ you must be explicit with a relative or absolute path, for example:
     `/path/to/file:name.txt` or `./file:name.txt`
 
 It is not possible to copy certain system files such as resources under
-`/proc`, `/sys`, `/dev`, and mounts created by the user in the container.
+`/proc`, `/sys`, `/dev`, [tmpfs](run.md#mount-tmpfs-tmpfs), and mounts created by
+the user in the container. However, you can still copy such files by manually
+running `tar` in `docker exec`. For example (consider `SRC_PATH` and `DEST_PATH`
+are directories):
+
+    $ docker exec foo tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | tar Cxf DEST_PATH -
+
+or
+
+    $ tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | docker exec -i foo tar Cxf DEST_PATH -
+
 
 Using `-` as the `SRC_PATH` streams the contents of `STDIN` as a tar archive.
 The command extracts the content of the tar to the `DEST_PATH` in container's

+ 2 - 1
docs/reference/commandline/create.md

@@ -164,7 +164,8 @@ Linux namespaces. On Microsoft Windows, you can specify these values:
 
 | Value     | Description                                                                                                                                                   |
 |-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value.  |
+| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value if the
+daemon is running on Windows server, or `hyperv` if running on Windows client.  |
 | `process` | Namespace isolation only.                                                                                                                                     |
 | `hyperv`   | Hyper-V hypervisor partition-based isolation.                                                                                                                  |
 

Some files were not shown because too many files changed in this diff