Quellcode durchsuchen

Windows: Graph remove custom interface and add central store

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>

Windows: add support for images stored in alternate location.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>
Stefan J. Wernli vor 10 Jahren
Ursprung
Commit
dfbb5520e3

+ 0 - 8
builder/internals.go

@@ -241,20 +241,12 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
 	}
 	defer container.Unmount()
 
-	if err := container.PrepareStorage(); err != nil {
-		return err
-	}
-
 	for _, ci := range copyInfos {
 		if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil {
 			return err
 		}
 	}
 
-	if err := container.CleanupStorage(); err != nil {
-		return err
-	}
-
 	if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
 		return err
 	}

+ 15 - 16
daemon/container.go

@@ -227,6 +227,21 @@ func (container *Container) GetRootResourcePath(path string) (string, error) {
 	return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root)
 }
 
+func (container *Container) ExportRw() (archive.Archive, error) {
+	if container.daemon == nil {
+		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
+	}
+	archive, err := container.daemon.Diff(container)
+	if err != nil {
+		return nil, err
+	}
+	return ioutils.NewReadCloserWrapper(archive, func() error {
+			err := archive.Close()
+			return err
+		}),
+		nil
+}
+
 func (container *Container) Start() (err error) {
 	container.Lock()
 	defer container.Unlock()
@@ -258,12 +273,6 @@ func (container *Container) Start() (err error) {
 		return err
 	}
 
-	// No-op if non-Windows. Once the container filesystem is mounted,
-	// prepare the layer to boot using the Windows driver.
-	if err := container.PrepareStorage(); err != nil {
-		return err
-	}
-
 	// Make sure NetworkMode has an acceptable value. We do this to ensure
 	// backwards API compatibility.
 	container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig)
@@ -345,10 +354,6 @@ func (container *Container) isNetworkAllocated() bool {
 func (container *Container) cleanup() {
 	container.ReleaseNetwork()
 
-	if err := container.CleanupStorage(); err != nil {
-		logrus.Errorf("%v: Failed to cleanup storage: %v", container.ID, err)
-	}
-
 	if err := container.Unmount(); err != nil {
 		logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
 	}
@@ -658,14 +663,8 @@ func (container *Container) Copy(resource string) (rc io.ReadCloser, err error)
 		return nil, err
 	}
 
-	if err := container.PrepareStorage(); err != nil {
-		container.Unmount()
-		return nil, err
-	}
-
 	reader := ioutils.NewReadCloserWrapper(archive, func() error {
 		err := archive.Close()
-		container.CleanupStorage()
 		container.UnmountVolumes(true)
 		container.Unmount()
 		container.Unlock()

+ 0 - 25
daemon/container_unix.go

@@ -18,9 +18,7 @@ import (
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/daemon/network"
-	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/directory"
-	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/system"
@@ -948,21 +946,6 @@ func (container *Container) initializeNetworking() error {
 	return container.buildHostnameFile()
 }
 
-func (container *Container) ExportRw() (archive.Archive, error) {
-	if container.daemon == nil {
-		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
-	}
-	archive, err := container.daemon.Diff(container)
-	if err != nil {
-		return nil, err
-	}
-	return ioutils.NewReadCloserWrapper(archive, func() error {
-			err := archive.Close()
-			return err
-		}),
-		nil
-}
-
 func (container *Container) getIpcContainer() (*Container, error) {
 	containerID := container.hostConfig.IpcMode.Container()
 	c, err := container.daemon.Get(containerID)
@@ -1107,14 +1090,6 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error {
 	return nil
 }
 
-func (container *Container) PrepareStorage() error {
-	return nil
-}
-
-func (container *Container) CleanupStorage() error {
-	return nil
-}
-
 func (container *Container) networkMounts() []execdriver.Mount {
 	var mounts []execdriver.Mount
 	mode := "Z"

+ 17 - 58
daemon/container_windows.go

@@ -4,14 +4,9 @@ package daemon
 
 import (
 	"fmt"
-	"path/filepath"
 	"strings"
 
 	"github.com/docker/docker/daemon/execdriver"
-	"github.com/docker/docker/daemon/graphdriver/windows"
-	"github.com/docker/docker/image"
-	"github.com/docker/docker/pkg/archive"
-	"github.com/microsoft/hcsshim"
 )
 
 // This is deliberately empty on Windows as the default path will be set by
@@ -98,25 +93,27 @@ func populateCommand(c *Container, env []string) error {
 
 	processConfig.Env = env
 
-	var layerFolder string
 	var layerPaths []string
-
-	// The following is specific to the Windows driver. We do this to
-	// enable VFS to continue operating for development purposes.
-	if wd, ok := c.daemon.driver.(*windows.WindowsGraphDriver); ok {
-		var err error
-		var img *image.Image
-		var ids []string
-
-		if img, err = c.daemon.graph.Get(c.ImageID); err != nil {
-			return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err)
+	img, err := c.daemon.graph.Get(c.ImageID)
+	if err != nil {
+		return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err)
+	}
+	for i := img; i != nil && err == nil; i, err = c.daemon.graph.GetParent(i) {
+		lp, err := c.daemon.driver.Get(i.ID, "")
+		if err != nil {
+			return fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err)
 		}
-		if ids, err = c.daemon.graph.ParentLayerIds(img); err != nil {
-			return fmt.Errorf("Failed to get parentlayer ids %s", img.ID)
+		layerPaths = append(layerPaths, lp)
+		err = c.daemon.driver.Put(i.ID)
+		if err != nil {
+			return fmt.Errorf("Failed to put layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err)
 		}
-		layerPaths = wd.LayerIdsToPaths(ids)
-		layerFolder = filepath.Join(wd.Info().HomeDir, filepath.Base(c.ID))
 	}
+	m, err := c.daemon.driver.GetMetadata(c.ID)
+	if err != nil {
+		return fmt.Errorf("Failed to get layer metadata - %s", err)
+	}
+	layerFolder := m["dir"]
 
 	// TODO Windows: Factor out remainder of unused fields.
 	c.command = &execdriver.Command{
@@ -151,14 +148,6 @@ func (container *Container) AllocateNetwork() error {
 	return nil
 }
 
-func (container *Container) ExportRw() (archive.Archive, error) {
-	if container.IsRunning() {
-		return nil, fmt.Errorf("Cannot export a running container.")
-	}
-	// TODO Windows. Implementation (different to Linux)
-	return nil, nil
-}
-
 func (container *Container) UpdateNetwork() error {
 	return nil
 }
@@ -174,36 +163,6 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error {
 	return nil
 }
 
-func (container *Container) PrepareStorage() error {
-	if wd, ok := container.daemon.driver.(*windows.WindowsGraphDriver); ok {
-		// Get list of paths to parent layers.
-		var ids []string
-		if container.ImageID != "" {
-			img, err := container.daemon.graph.Get(container.ImageID)
-			if err != nil {
-				return err
-			}
-
-			ids, err = container.daemon.graph.ParentLayerIds(img)
-			if err != nil {
-				return err
-			}
-		}
-
-		if err := hcsshim.PrepareLayer(wd.Info(), container.ID, wd.LayerIdsToPaths(ids)); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (container *Container) CleanupStorage() error {
-	if wd, ok := container.daemon.driver.(*windows.WindowsGraphDriver); ok {
-		return hcsshim.UnprepareLayer(wd.Info(), container.ID)
-	}
-	return nil
-}
-
 // prepareMountPoints is a no-op on Windows
 func (container *Container) prepareMountPoints() error {
 	return nil

+ 47 - 0
daemon/daemon.go

@@ -24,6 +24,7 @@ import (
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/image"
+	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/graphdb"
@@ -683,6 +684,12 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
 		return nil, fmt.Errorf("Couldn't create Tag store repositories-%s: %s", d.driver.String(), err)
 	}
 
+	if restorer, ok := d.driver.(graphdriver.ImageRestorer); ok {
+		if _, err := restorer.RestoreCustomImages(repositories, g); err != nil {
+			return nil, fmt.Errorf("Couldn't restore custom images: %s", err)
+		}
+	}
+
 	d.netController, err = initNetworkController(config)
 	if err != nil {
 		return nil, fmt.Errorf("Error initializing network controller: %v", err)
@@ -840,6 +847,46 @@ func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface
 	return nil
 }
 
+func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
+	initID := fmt.Sprintf("%s-init", container.ID)
+	return daemon.driver.Changes(container.ID, initID)
+}
+
+func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
+	initID := fmt.Sprintf("%s-init", container.ID)
+	return daemon.driver.Diff(container.ID, initID)
+}
+
+func (daemon *Daemon) createRootfs(container *Container) error {
+	// Step 1: create the container directory.
+	// This doubles as a barrier to avoid race conditions.
+	if err := os.Mkdir(container.root, 0700); err != nil {
+		return err
+	}
+	initID := fmt.Sprintf("%s-init", container.ID)
+	if err := daemon.driver.Create(initID, container.ImageID); err != nil {
+		return err
+	}
+	initPath, err := daemon.driver.Get(initID, "")
+	if err != nil {
+		return err
+	}
+
+	if err := setupInitLayer(initPath); err != nil {
+		daemon.driver.Put(initID)
+		return err
+	}
+
+	// We want to unmount init layer before we take snapshot of it
+	// for the actual container.
+	daemon.driver.Put(initID)
+
+	if err := daemon.driver.Create(container.ID, initID); err != nil {
+		return err
+	}
+	return nil
+}
+
 // FIXME: this is a convenience function for integration tests
 // which need direct access to daemon.graph.
 // Once the tests switch to using engine and jobs, this method

+ 0 - 41
daemon/daemon_unix.go

@@ -14,7 +14,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/daemon/graphdriver"
-	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers/kernel"
@@ -39,16 +38,6 @@ const (
 	platformSupported = true
 )
 
-func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
-	initID := fmt.Sprintf("%s-init", container.ID)
-	return daemon.driver.Changes(container.ID, initID)
-}
-
-func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
-	initID := fmt.Sprintf("%s-init", container.ID)
-	return daemon.driver.Diff(container.ID, initID)
-}
-
 func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
 	var (
 		labelOpts []string
@@ -74,36 +63,6 @@ func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error
 	return err
 }
 
-func (daemon *Daemon) createRootfs(container *Container) error {
-	// Step 1: create the container directory.
-	// This doubles as a barrier to avoid race conditions.
-	if err := os.Mkdir(container.root, 0700); err != nil {
-		return err
-	}
-	initID := fmt.Sprintf("%s-init", container.ID)
-	if err := daemon.driver.Create(initID, container.ImageID); err != nil {
-		return err
-	}
-	initPath, err := daemon.driver.Get(initID, "")
-	if err != nil {
-		return err
-	}
-
-	if err := setupInitLayer(initPath); err != nil {
-		daemon.driver.Put(initID)
-		return err
-	}
-
-	// We want to unmount init layer before we take snapshot of it
-	// for the actual container.
-	daemon.driver.Put(initID)
-
-	if err := daemon.driver.Create(container.ID, initID); err != nil {
-		return err
-	}
-	return nil
-}
-
 func checkKernel() error {
 	// Check for unsupported kernel versions
 	// FIXME: it would be cleaner to not test for specific versions, but rather

+ 2 - 49
daemon/daemon_windows.go

@@ -5,14 +5,11 @@ import (
 	"os"
 	"syscall"
 
-	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/graphdriver"
-	"github.com/docker/docker/daemon/graphdriver/windows"
-	"github.com/docker/docker/pkg/archive"
+	_ "github.com/docker/docker/daemon/graphdriver/windows"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/libnetwork"
-	"github.com/microsoft/hcsshim"
 )
 
 const (
@@ -20,55 +17,11 @@ const (
 	platformSupported    = true
 )
 
-func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
-	return daemon.driver.Changes(container.ID, container.ImageID)
-}
-
-func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
-	return daemon.driver.Diff(container.ID, container.ImageID)
-}
-
 func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
 	return nil
 }
 
-func (daemon *Daemon) createRootfs(container *Container) error {
-	// Step 1: create the container directory.
-	// This doubles as a barrier to avoid race conditions.
-	if err := os.Mkdir(container.root, 0700); err != nil {
-		return err
-	}
-
-	if wd, ok := daemon.driver.(*windows.WindowsGraphDriver); ok {
-		if container.ImageID != "" {
-			// Get list of paths to parent layers.
-			logrus.Debugln("createRootfs: Container has parent image:", container.ImageID)
-			img, err := daemon.graph.Get(container.ImageID)
-			if err != nil {
-				return err
-			}
-
-			ids, err := daemon.graph.ParentLayerIds(img)
-			if err != nil {
-				return err
-			}
-			logrus.Debugf("Got image ids: %d", len(ids))
-
-			if err := hcsshim.CreateSandboxLayer(wd.Info(), container.ID, container.ImageID, wd.LayerIdsToPaths(ids)); err != nil {
-				return err
-			}
-		} else {
-			if err := daemon.driver.Create(container.ID, container.ImageID); err != nil {
-				return err
-			}
-		}
-	} else {
-		// Fall-back code path to allow the use of the VFS driver for development
-		if err := daemon.driver.Create(container.ID, container.ImageID); err != nil {
-			return err
-		}
-
-	}
+func setupInitLayer(initLayer string) error {
 	return nil
 }
 

+ 3 - 7
daemon/delete.go

@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"os"
 	"path"
-	"runtime"
 
 	"github.com/Sirupsen/logrus"
 )
@@ -117,12 +116,9 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
 		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
 	}
 
-	// There will not be an -init on Windows, so don't fail by not attempting to delete it
-	if runtime.GOOS != "windows" {
-		initID := fmt.Sprintf("%s-init", container.ID)
-		if err := daemon.driver.Remove(initID); err != nil {
-			return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
-		}
+	initID := fmt.Sprintf("%s-init", container.ID)
+	if err := daemon.driver.Remove(initID); err != nil {
+		return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
 	}
 
 	if err = os.RemoveAll(container.root); err != nil {

+ 7 - 1
daemon/execdriver/windows/run.go

@@ -9,6 +9,7 @@ import (
 	"errors"
 	"fmt"
 	"os"
+	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -100,8 +101,13 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	}
 
 	for i := 0; i < len(c.LayerPaths); i++ {
+		_, filename := filepath.Split(c.LayerPaths[i])
+		g, err := hcsshim.NameToGuid(filename)
+		if err != nil {
+			return execdriver.ExitStatus{ExitCode: -1}, err
+		}
 		cu.Layers = append(cu.Layers, layer{
-			ID:   hcsshim.NewGUID(c.LayerPaths[i]).ToString(),
+			ID:   g.ToString(),
 			Path: c.LayerPaths[i],
 		})
 	}

+ 0 - 14
daemon/graphdriver/driver_windows.go

@@ -1,19 +1,5 @@
 package graphdriver
 
-import (
-	"github.com/docker/docker/pkg/archive"
-	"github.com/microsoft/hcsshim"
-)
-
-type WindowsGraphDriver interface {
-	Driver
-	CopyDiff(id, sourceId string, parentLayerPaths []string) error
-	LayerIdsToPaths(ids []string) []string
-	Info() hcsshim.DriverInfo
-	Export(id string, parentLayerPaths []string) (archive.Archive, error)
-	Import(id string, layerData archive.Reader, parentLayerPaths []string) (int64, error)
-}
-
 var (
 	// Slice of drivers that should be used in order
 	priority = []string{

+ 30 - 0
daemon/graphdriver/imagerestorer.go

@@ -0,0 +1,30 @@
+package graphdriver
+
+import (
+	"github.com/docker/docker/image"
+	"github.com/docker/docker/pkg/archive"
+)
+
+// NOTE: These interfaces are used for implementing specific features of the Windows
+// graphdriver implementation.  The current versions are a short-term solution and
+// likely to change or possibly be eliminated, so avoid using them outside of the Windows
+// graphdriver code.
+
+// ImageRestorer interface allows the implementer to add a custom image to
+// the graph and tagstore.
+type ImageRestorer interface {
+	RestoreCustomImages(tagger Tagger, recorder Recorder) ([]string, error)
+}
+
+// Tagger is an interface that exposes the TagStore.Tag function without needing
+// to import graph.
+type Tagger interface {
+	Tag(repoName, tag, imageName string, force bool) error
+}
+
+// Recorder is an interface that exposes the Graph.Register and Graph.Exists
+// functions without needing to import graph.
+type Recorder interface {
+	Exists(id string) bool
+	Register(img *image.Image, layerData archive.Reader) error
+}

+ 335 - 95
daemon/graphdriver/windows/windows.go

@@ -3,17 +3,26 @@
 package windows
 
 import (
+	"crypto/sha512"
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
 	"sync"
 	"time"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/daemon/graphdriver"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/ioutils"
+	"github.com/docker/docker/pkg/random"
 	"github.com/microsoft/hcsshim"
 )
 
@@ -27,7 +36,7 @@ const (
 	filterDriver
 )
 
-type WindowsGraphDriver struct {
+type Driver struct {
 	info       hcsshim.DriverInfo
 	sync.Mutex // Protects concurrent modification to active
 	active     map[string]int
@@ -36,7 +45,7 @@ type WindowsGraphDriver struct {
 // New returns a new Windows storage filter driver.
 func InitFilter(home string, options []string) (graphdriver.Driver, error) {
 	logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
-	d := &WindowsGraphDriver{
+	d := &Driver{
 		info: hcsshim.DriverInfo{
 			HomeDir: home,
 			Flavour: filterDriver,
@@ -49,7 +58,7 @@ func InitFilter(home string, options []string) (graphdriver.Driver, error) {
 // New returns a new Windows differencing disk driver.
 func InitDiff(home string, options []string) (graphdriver.Driver, error) {
 	logrus.Debugf("WindowsGraphDriver InitDiff at %s", home)
-	d := &WindowsGraphDriver{
+	d := &Driver{
 		info: hcsshim.DriverInfo{
 			HomeDir: home,
 			Flavour: diffDriver,
@@ -59,11 +68,7 @@ func InitDiff(home string, options []string) (graphdriver.Driver, error) {
 	return d, nil
 }
 
-func (d *WindowsGraphDriver) Info() hcsshim.DriverInfo {
-	return d.info
-}
-
-func (d *WindowsGraphDriver) String() string {
+func (d *Driver) String() string {
 	switch d.info.Flavour {
 	case diffDriver:
 		return "windowsdiff"
@@ -74,7 +79,7 @@ func (d *WindowsGraphDriver) String() string {
 	}
 }
 
-func (d *WindowsGraphDriver) Status() [][2]string {
+func (d *Driver) Status() [][2]string {
 	return [][2]string{
 		{"Windows", ""},
 	}
@@ -82,45 +87,137 @@ func (d *WindowsGraphDriver) Status() [][2]string {
 
 // Exists returns true if the given id is registered with
 // this driver
-func (d *WindowsGraphDriver) Exists(id string) bool {
-	result, err := hcsshim.LayerExists(d.info, id)
+func (d *Driver) Exists(id string) bool {
+	rId, err := d.resolveId(id)
+	if err != nil {
+		return false
+	}
+	result, err := hcsshim.LayerExists(d.info, rId)
 	if err != nil {
 		return false
 	}
 	return result
 }
 
-func (d *WindowsGraphDriver) Create(id, parent string) error {
-	return hcsshim.CreateLayer(d.info, id, parent)
+func (d *Driver) Create(id, parent string) error {
+	rPId, err := d.resolveId(parent)
+	if err != nil {
+		return err
+	}
+
+	parentChain, err := d.getLayerChain(rPId)
+	if err != nil {
+		return err
+	}
+
+	var layerChain []string
+
+	parentIsInit := strings.HasSuffix(rPId, "-init")
+
+	if !parentIsInit && rPId != "" {
+		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
+		if err != nil {
+			return err
+		}
+		layerChain = []string{parentPath}
+	}
+
+	layerChain = append(layerChain, parentChain...)
+
+	if parentIsInit {
+		if len(layerChain) == 0 {
+			return fmt.Errorf("Cannot create a read/write layer without a parent layer.")
+		}
+		if err := hcsshim.CreateSandboxLayer(d.info, id, layerChain[0], layerChain); err != nil {
+			return err
+		}
+	} else {
+		if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
+			return err
+		}
+	}
+
+	if _, err := os.Lstat(d.dir(parent)); err == nil {
+		if err := d.setLayerChain(id, layerChain); err != nil {
+			if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
+				logrus.Warnf("Failed to DestroyLayer %s: %s", id, err)
+			}
+			return err
+		}
+	} else if os.IsNotExist(err) {
+		// If the parent doesn't exist, this must be a special creation for an image
+		// registered at an alternate location. Use the parent id as the alternate ID.
+		if err := d.setId(id, parent); err != nil {
+			if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
+				logrus.Warnf("Failed to DestroyLayer %s: %s", id, err)
+			}
+			return err
+		}
+	} else {
+		if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
+			logrus.Warnf("Failed to DestroyLayer %s: %s", id, err)
+		}
+		return err
+	}
+
+	return nil
 }
 
-func (d *WindowsGraphDriver) dir(id string) string {
+func (d *Driver) dir(id string) string {
 	return filepath.Join(d.info.HomeDir, filepath.Base(id))
 }
 
 // Remove unmounts and removes the dir information
-func (d *WindowsGraphDriver) Remove(id string) error {
-	return hcsshim.DestroyLayer(d.info, id)
+func (d *Driver) Remove(id string) error {
+	rId, err := d.resolveId(id)
+	if err != nil {
+		return err
+	}
+
+	return hcsshim.DestroyLayer(d.info, rId)
 }
 
 // Get returns the rootfs path for the id. This will mount the dir at it's given path
-func (d *WindowsGraphDriver) Get(id, mountLabel string) (string, error) {
+func (d *Driver) Get(id, mountLabel string) (string, error) {
+	logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
 	var dir string
 
 	d.Lock()
 	defer d.Unlock()
 
-	if d.active[id] == 0 {
-		if err := hcsshim.ActivateLayer(d.info, id); err != nil {
+	rId, err := d.resolveId(id)
+	if err != nil {
+		return "", err
+	}
+
+	// Getting the layer paths must be done outside of the lock.
+	layerChain, err := d.getLayerChain(rId)
+	if err != nil {
+		return "", err
+	}
+
+	if d.active[rId] == 0 {
+		if err := hcsshim.ActivateLayer(d.info, rId); err != nil {
+			return "", err
+		}
+		if err := hcsshim.PrepareLayer(d.info, rId, layerChain); err != nil {
+			if err2 := hcsshim.DeactivateLayer(d.info, rId); err2 != nil {
+				logrus.Warnf("Failed to Deactivate %s: %s", id, err)
+			}
 			return "", err
 		}
 	}
 
-	mountPath, err := hcsshim.GetLayerMountPath(d.info, id)
+	mountPath, err := hcsshim.GetLayerMountPath(d.info, rId)
 	if err != nil {
+		if err2 := hcsshim.DeactivateLayer(d.info, rId); err2 != nil {
+			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
+		}
 		return "", err
 	}
 
+	d.active[rId]++
+
 	// If the layer has a mount path, use that. Otherwise, use the
 	// folder path.
 	if mountPath != "" {
@@ -129,61 +226,131 @@ func (d *WindowsGraphDriver) Get(id, mountLabel string) (string, error) {
 		dir = d.dir(id)
 	}
 
-	d.active[id]++
-
 	return dir, nil
 }
 
-func (d *WindowsGraphDriver) Put(id string) error {
+func (d *Driver) Put(id string) error {
 	logrus.Debugf("WindowsGraphDriver Put() id %s", id)
 
+	rId, err := d.resolveId(id)
+	if err != nil {
+		return err
+	}
+
 	d.Lock()
 	defer d.Unlock()
 
-	if d.active[id] > 1 {
-		d.active[id]--
-	} else if d.active[id] == 1 {
-		if err := hcsshim.DeactivateLayer(d.info, id); err != nil {
+	if d.active[rId] > 1 {
+		d.active[rId]--
+	} else if d.active[rId] == 1 {
+		if err := hcsshim.UnprepareLayer(d.info, rId); err != nil {
 			return err
 		}
-		delete(d.active, id)
+		if err := hcsshim.DeactivateLayer(d.info, rId); err != nil {
+			return err
+		}
+		delete(d.active, rId)
 	}
 
 	return nil
 }
 
-func (d *WindowsGraphDriver) Cleanup() error {
+func (d *Driver) Cleanup() error {
 	return nil
 }
 
 // Diff produces an archive of the changes between the specified
 // layer and its parent layer which may be "".
-func (d *WindowsGraphDriver) Diff(id, parent string) (arch archive.Archive, err error) {
-	return nil, fmt.Errorf("The Windows graphdriver does not support Diff()")
+func (d *Driver) Diff(id, parent string) (arch archive.Archive, err error) {
+	rId, err := d.resolveId(id)
+	if err != nil {
+		return
+	}
+
+	// Getting the layer paths must be done outside of the lock.
+	layerChain, err := d.getLayerChain(rId)
+	if err != nil {
+		return
+	}
+
+	d.Lock()
+
+	// To support export, a layer must be activated but not prepared.
+	if d.info.Flavour == filterDriver {
+		if d.active[rId] == 0 {
+			if err = hcsshim.ActivateLayer(d.info, rId); err != nil {
+				d.Unlock()
+				return
+			}
+			defer func() {
+				if err := hcsshim.DeactivateLayer(d.info, rId); err != nil {
+					logrus.Warnf("Failed to Deactivate %s: %s", rId, err)
+				}
+			}()
+		} else {
+			if err = hcsshim.UnprepareLayer(d.info, rId); err != nil {
+				d.Unlock()
+				return
+			}
+			defer func() {
+				if err := hcsshim.PrepareLayer(d.info, rId, layerChain); err != nil {
+					logrus.Warnf("Failed to re-PrepareLayer %s: %s", rId, err)
+				}
+			}()
+		}
+	}
+
+	d.Unlock()
+
+	return d.exportLayer(rId, layerChain)
 }
 
 // Changes produces a list of changes between the specified layer
 // and its parent layer. If parent is "", then all changes will be ADD changes.
-func (d *WindowsGraphDriver) Changes(id, parent string) ([]archive.Change, error) {
+func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
 	return nil, fmt.Errorf("The Windows graphdriver does not support Changes()")
 }
 
 // ApplyDiff extracts the changeset from the given diff into the
 // layer with the specified id and parent, returning the size of the
 // new layer in bytes.
-func (d *WindowsGraphDriver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
-	start := time.Now().UTC()
-	logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
+func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
+	rPId, err := d.resolveId(parent)
+	if err != nil {
+		return
+	}
 
-	destination := d.dir(id)
 	if d.info.Flavour == diffDriver {
+		start := time.Now().UTC()
+		logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
+		destination := d.dir(id)
 		destination = filepath.Dir(destination)
+		if size, err = chrootarchive.ApplyUncompressedLayer(destination, diff); err != nil {
+			return
+		}
+		logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
+
+		return
 	}
 
-	if size, err = chrootarchive.ApplyLayer(destination, diff); err != nil {
+	parentChain, err := d.getLayerChain(rPId)
+	if err != nil {
+		return
+	}
+	parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
+	if err != nil {
+		return
+	}
+	layerChain := []string{parentPath}
+	layerChain = append(layerChain, parentChain...)
+
+	if size, err = d.importLayer(id, diff, layerChain); err != nil {
+		return
+	}
+
+	if err = d.setLayerChain(id, layerChain); err != nil {
 		return
 	}
-	logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
 
 	return
 }
@@ -191,8 +358,13 @@ func (d *WindowsGraphDriver) ApplyDiff(id, parent string, diff archive.Reader) (
 // DiffSize calculates the changes between the specified layer
 // and its parent and returns the size in bytes of the changes
 // relative to its base filesystem directory.
-func (d *WindowsGraphDriver) DiffSize(id, parent string) (size int64, err error) {
-	changes, err := d.Changes(id, parent)
+func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
+	rPId, err := d.resolveId(parent)
+	if err != nil {
+		return
+	}
+
+	changes, err := d.Changes(id, rPId)
 	if err != nil {
 		return
 	}
@@ -206,65 +378,88 @@ func (d *WindowsGraphDriver) DiffSize(id, parent string) (size int64, err error)
 	return archive.ChangesSize(layerFs, changes), nil
 }
 
-func (d *WindowsGraphDriver) CopyDiff(sourceId, id string, parentLayerPaths []string) error {
-	d.Lock()
-	defer d.Unlock()
+func (d *Driver) RestoreCustomImages(tagger graphdriver.Tagger, recorder graphdriver.Recorder) (imageIDs []string, err error) {
+	strData, err := hcsshim.GetSharedBaseImages()
+	if err != nil {
+		return nil, fmt.Errorf("Failed to restore base images: %s", err)
+	}
 
-	if d.info.Flavour == filterDriver && d.active[sourceId] == 0 {
-		if err := hcsshim.ActivateLayer(d.info, sourceId); err != nil {
-			return err
-		}
-		defer func() {
-			err := hcsshim.DeactivateLayer(d.info, sourceId)
-			if err != nil {
-				logrus.Warnf("Failed to Deactivate %s: %s", sourceId, err)
-			}
-		}()
+	type customImageInfo struct {
+		Name        string
+		Version     string
+		Path        string
+		Size        int64
+		CreatedTime time.Time
+	}
+	type customImageInfoList struct {
+		Images []customImageInfo
 	}
 
-	return hcsshim.CopyLayer(d.info, sourceId, id, parentLayerPaths)
-}
+	var infoData customImageInfoList
 
-func (d *WindowsGraphDriver) LayerIdsToPaths(ids []string) []string {
-	var paths []string
-	for _, id := range ids {
-		path, err := d.Get(id, "")
-		if err != nil {
-			logrus.Debug("LayerIdsToPaths: Error getting mount path for id", id, ":", err.Error())
-			return nil
-		}
-		if d.Put(id) != nil {
-			logrus.Debug("LayerIdsToPaths: Error putting mount path for id", id, ":", err.Error())
-			return nil
+	if err = json.Unmarshal([]byte(strData), &infoData); err != nil {
+		err = fmt.Errorf("JSON unmarshal returned error=%s", err)
+		logrus.Error(err)
+		return nil, err
+	}
+
+	for _, imageData := range infoData.Images {
+		_, folderName := filepath.Split(imageData.Path)
+
+		// Use crypto hash of the foldername to generate a docker style id.
+		h := sha512.Sum384([]byte(folderName))
+		id := fmt.Sprintf("%x", h[:32])
+
+		if !recorder.Exists(id) {
+			// Register the image.
+			img := &image.Image{
+				ID:            id,
+				Created:       imageData.CreatedTime,
+				DockerVersion: dockerversion.VERSION,
+				Architecture:  runtime.GOARCH,
+				OS:            runtime.GOOS,
+				Size:          imageData.Size,
+			}
+
+			if err := recorder.Register(img, nil); err != nil {
+				return nil, err
+			}
+
+			// Create tags for the new image.
+			if err := tagger.Tag(strings.ToLower(imageData.Name), imageData.Version, img.ID, true); err != nil {
+				return nil, err
+			}
+
+			// Create the alternate ID file.
+			if err := d.setId(img.ID, folderName); err != nil {
+				return nil, err
+			}
+
+			imageIDs = append(imageIDs, img.ID)
 		}
-		paths = append(paths, path)
 	}
-	return paths
+
+	return imageIDs, nil
 }
 
-func (d *WindowsGraphDriver) GetMetadata(id string) (map[string]string, error) {
-	return nil, nil
+func (d *Driver) GetMetadata(id string) (map[string]string, error) {
+	m := make(map[string]string)
+	m["dir"] = d.dir(id)
+	return m, nil
 }
 
-func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch archive.Archive, err error) {
-	layerFs, err := d.Get(id, "")
-	if err != nil {
-		return
-	}
-	defer func() {
-		if err != nil {
-			d.Put(id)
-		}
-	}()
+func (d *Driver) exportLayer(id string, parentLayerPaths []string) (arch archive.Archive, err error) {
+	layerFolder := d.dir(id)
 
-	tempFolder := layerFs + "-temp"
+	tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
 	if err = os.MkdirAll(tempFolder, 0755); err != nil {
 		logrus.Errorf("Could not create %s %s", tempFolder, err)
 		return
 	}
 	defer func() {
 		if err != nil {
-			if err2 := os.RemoveAll(tempFolder); err2 != nil {
+			_, folderName := filepath.Split(tempFolder)
+			if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
 				logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
 			}
 		}
@@ -281,7 +476,8 @@ func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch
 	return ioutils.NewReadCloserWrapper(archive, func() error {
 		err := archive.Close()
 		d.Put(id)
-		if err2 := os.RemoveAll(tempFolder); err2 != nil {
+		_, folderName := filepath.Split(tempFolder)
+		if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
 			logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
 		}
 		return err
@@ -289,24 +485,17 @@ func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch
 
 }
 
-func (d *WindowsGraphDriver) Import(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
-	layerFs, err := d.Get(id, "")
-	if err != nil {
-		return
-	}
-	defer func() {
-		if err != nil {
-			d.Put(id)
-		}
-	}()
+func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
+	layerFolder := d.dir(id)
 
-	tempFolder := layerFs + "-temp"
+	tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10)
 	if err = os.MkdirAll(tempFolder, 0755); err != nil {
 		logrus.Errorf("Could not create %s %s", tempFolder, err)
 		return
 	}
 	defer func() {
-		if err2 := os.RemoveAll(tempFolder); err2 != nil {
+		_, folderName := filepath.Split(tempFolder)
+		if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil {
 			logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
 		}
 	}()
@@ -324,3 +513,54 @@ func (d *WindowsGraphDriver) Import(id string, layerData archive.Reader, parentL
 
 	return
 }
+
+func (d *Driver) resolveId(id string) (string, error) {
+	content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerId"))
+	if os.IsNotExist(err) {
+		return id, nil
+	} else if err != nil {
+		return "", err
+	}
+	return string(content), nil
+}
+
+func (d *Driver) setId(id, altId string) error {
+	err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altId), 0600)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (d *Driver) getLayerChain(id string) ([]string, error) {
+	jPath := filepath.Join(d.dir(id), "layerchain.json")
+	content, err := ioutil.ReadFile(jPath)
+	if os.IsNotExist(err) {
+		return nil, nil
+	} else if err != nil {
+		return nil, fmt.Errorf("Unable to read layerchain file - %s", err)
+	}
+
+	var layerChain []string
+	err = json.Unmarshal(content, &layerChain)
+	if err != nil {
+		return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err)
+	}
+
+	return layerChain, nil
+}
+
+func (d *Driver) setLayerChain(id string, chain []string) error {
+	content, err := json.Marshal(&chain)
+	if err != nil {
+		return fmt.Errorf("Failed to marshall layerchain json - %s", err)
+	}
+
+	jPath := filepath.Join(d.dir(id), "layerchain.json")
+	err = ioutil.WriteFile(jPath, content, 0600)
+	if err != nil {
+		return fmt.Errorf("Unable to write layerchain file - %s", err)
+	}
+
+	return nil
+}

+ 78 - 26
graph/graph.go

@@ -77,11 +77,12 @@ func (r *retainedLayers) Exists(layerID string) bool {
 
 // A Graph is a store for versioned filesystem images and the relationship between them.
 type Graph struct {
-	root       string
-	idIndex    *truncindex.TruncIndex
-	driver     graphdriver.Driver
-	imageMutex imageMutex // protect images in driver.
-	retained   *retainedLayers
+	root             string
+	idIndex          *truncindex.TruncIndex
+	driver           graphdriver.Driver
+	imageMutex       imageMutex // protect images in driver.
+	retained         *retainedLayers
+	tarSplitDisabled bool
 }
 
 // file names for ./graph/<ID>/
@@ -117,6 +118,12 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
 		driver:   driver,
 		retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})},
 	}
+
+	// Windows does not currently support tarsplit functionality.
+	if runtime.GOOS == "windows" {
+		graph.tarSplitDisabled = true
+	}
+
 	if err := graph.restore(); err != nil {
 		return nil, err
 	}
@@ -141,12 +148,6 @@ func (graph *Graph) restore() error {
 		}
 	}
 
-	baseIds, err := graph.restoreBaseImages()
-	if err != nil {
-		return err
-	}
-	ids = append(ids, baseIds...)
-
 	graph.idIndex = truncindex.NewTruncIndex(ids)
 	logrus.Debugf("Restored %d elements", len(ids))
 	return nil
@@ -285,6 +286,13 @@ func (graph *Graph) Register(img *image.Image, layerData archive.Reader) (err er
 	return nil
 }
 
+func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.Reader) error {
+	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
+		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
+	}
+	return nil
+}
+
 // TempLayerArchive creates a temporary archive of the given image's filesystem layer.
 //   The archive is stored on disk and will be automatically deleted as soon as has been read.
 //   If output is not nil, a human-readable progress bar will be written to it.
@@ -442,6 +450,16 @@ func (graph *Graph) Heads() map[string]*image.Image {
 	return heads
 }
 
+// TarLayer returns a tar archive of the image's filesystem layer.
+func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
+	rdr, err := graph.assembleTarLayer(img)
+	if err != nil {
+		logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
+		return graph.driver.Diff(img.ID, img.Parent)
+	}
+	return rdr, nil
+}
+
 func (graph *Graph) imageRoot(id string) string {
 	return filepath.Join(graph.root, id)
 }
@@ -535,30 +553,64 @@ func jsonPath(root string) string {
 	return filepath.Join(root, jsonFileName)
 }
 
-func (graph *Graph) disassembleAndApplyTarLayer(img *image.Image, layerData archive.Reader, root string) error {
-	// this is saving the tar-split metadata
-	mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
-	if err != nil {
-		return err
+// storeImage stores file system layer data for the given image to the
+// graph's storage driver. Image metadata is stored in a file
+// at the specified root directory.
+func (graph *Graph) storeImage(img *image.Image, layerData archive.Reader, root string) (err error) {
+	// Store the layer. If layerData is not nil, unpack it into the new layer
+	if layerData != nil {
+		if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil {
+			return err
+		}
 	}
-	mfz := gzip.NewWriter(mf)
-	metaPacker := storage.NewJSONPacker(mfz)
-	defer mf.Close()
-	defer mfz.Close()
 
-	inflatedLayerData, err := archive.DecompressStream(layerData)
-	if err != nil {
+	if err := graph.saveSize(root, img.Size); err != nil {
 		return err
 	}
 
-	// we're passing nil here for the file putter, because the ApplyDiff will
-	// handle the extraction of the archive
-	rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil)
+	f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
 	if err != nil {
 		return err
 	}
 
-	if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, archive.Reader(rdr)); err != nil {
+	defer f.Close()
+
+	return json.NewEncoder(f).Encode(img)
+}
+
+func (graph *Graph) disassembleAndApplyTarLayer(img *image.Image, layerData archive.Reader, root string) (err error) {
+	var ar archive.Reader
+
+	if graph.tarSplitDisabled {
+		ar = layerData
+	} else {
+		// this is saving the tar-split metadata
+		mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
+		if err != nil {
+			return err
+		}
+
+		mfz := gzip.NewWriter(mf)
+		metaPacker := storage.NewJSONPacker(mfz)
+		defer mf.Close()
+		defer mfz.Close()
+
+		inflatedLayerData, err := archive.DecompressStream(layerData)
+		if err != nil {
+			return err
+		}
+
+		// we're passing nil here for the file putter, because the ApplyDiff will
+		// handle the extraction of the archive
+		rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil)
+		if err != nil {
+			return err
+		}
+
+		ar = archive.Reader(rdr)
+	}
+
+	if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, ar); err != nil {
 		return err
 	}
 

+ 0 - 120
graph/graph_unix.go

@@ -1,120 +0,0 @@
-// +build !windows
-
-package graph
-
-import (
-	"encoding/json"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-	"syscall"
-
-	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/image"
-	"github.com/docker/docker/pkg/archive"
-	"github.com/docker/docker/pkg/system"
-)
-
-// 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) error {
-	for pth, typ := range map[string]string{
-		"/dev/pts":         "dir",
-		"/dev/shm":         "dir",
-		"/proc":            "dir",
-		"/sys":             "dir",
-		"/.dockerinit":     "file",
-		"/.dockerenv":      "file",
-		"/etc/resolv.conf": "file",
-		"/etc/hosts":       "file",
-		"/etc/hostname":    "file",
-		"/dev/console":     "file",
-		"/etc/mtab":        "/proc/mounts",
-	} {
-		parts := strings.Split(pth, "/")
-		prev := "/"
-		for _, p := range parts[1:] {
-			prev = filepath.Join(prev, p)
-			syscall.Unlink(filepath.Join(initLayer, prev))
-		}
-
-		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
-			if os.IsNotExist(err) {
-				if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil {
-					return err
-				}
-				switch typ {
-				case "dir":
-					if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil {
-						return err
-					}
-				case "file":
-					f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
-					if err != nil {
-						return err
-					}
-					f.Close()
-				default:
-					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
-						return err
-					}
-				}
-			} else {
-				return err
-			}
-		}
-	}
-
-	// Layer is ready to use, if it wasn't before.
-	return nil
-}
-
-func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.Reader) error {
-	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
-		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
-	}
-	return nil
-}
-
-func (graph *Graph) restoreBaseImages() ([]string, error) {
-	return nil, nil
-}
-
-// storeImage stores file system layer data for the given image to the
-// graph's storage driver. Image metadata is stored in a file
-// at the specified root directory.
-func (graph *Graph) storeImage(img *image.Image, layerData archive.Reader, root string) (err error) {
-	// Store the layer. If layerData is not nil, unpack it into the new layer
-	if layerData != nil {
-		if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil {
-			return err
-		}
-	}
-
-	if err := graph.saveSize(root, img.Size); err != nil {
-		return err
-	}
-
-	f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
-	if err != nil {
-		return err
-	}
-
-	defer f.Close()
-
-	return json.NewEncoder(f).Encode(img)
-}
-
-// TarLayer returns a tar archive of the image's filesystem layer.
-func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
-	rdr, err := graph.assembleTarLayer(img)
-	if err != nil {
-		logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
-		return graph.driver.Diff(img.ID, img.Parent)
-	}
-	return rdr, nil
-}

+ 0 - 164
graph/graph_windows.go

@@ -1,164 +0,0 @@
-// +build windows
-
-package graph
-
-import (
-	"encoding/json"
-	"fmt"
-	"os"
-
-	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/daemon/graphdriver/windows"
-	"github.com/docker/docker/image"
-	"github.com/docker/docker/pkg/archive"
-)
-
-// SetupInitLayer populates a directory with mountpoints suitable
-// for bind-mounting dockerinit into the container. T
-func SetupInitLayer(initLayer string) error {
-	return nil
-}
-
-func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.Reader) error {
-	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
-		if img.Container != "" && layerData == nil {
-			logrus.Debugf("Copying from container %s.", img.Container)
-
-			var ids []string
-			if img.Parent != "" {
-				parentImg, err := graph.Get(img.Parent)
-				if err != nil {
-					return err
-				}
-
-				ids, err = graph.ParentLayerIds(parentImg)
-				if err != nil {
-					return err
-				}
-			}
-
-			if err := wd.CopyDiff(img.Container, img.ID, wd.LayerIdsToPaths(ids)); err != nil {
-				return fmt.Errorf("Driver %s failed to copy image rootfs %s: %s", graph.driver, img.Container, err)
-			}
-		} else if img.Parent == "" {
-			if err := graph.driver.Create(img.ID, img.Parent); err != nil {
-				return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
-			}
-		}
-	} else {
-		// This fallback allows the use of VFS during daemon development.
-		if err := graph.driver.Create(img.ID, img.Parent); err != nil {
-			return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
-		}
-	}
-	return nil
-}
-
-func (graph *Graph) restoreBaseImages() ([]string, error) {
-	// TODO Windows. This needs implementing (@swernli)
-	return nil, nil
-}
-
-// ParentLayerIds returns a list of all parent image IDs for the given image.
-func (graph *Graph) ParentLayerIds(img *image.Image) (ids []string, err error) {
-	for i := img; i != nil && err == nil; i, err = graph.GetParent(i) {
-		ids = append(ids, i.ID)
-	}
-
-	return
-}
-
-// storeImage stores file system layer data for the given image to the
-// graph's storage driver. Image metadata is stored in a file
-// at the specified root directory.
-func (graph *Graph) storeImage(img *image.Image, layerData archive.Reader, root string) (err error) {
-
-	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
-		// Store the layer. If layerData is not nil and this isn't a base image,
-		// unpack it into the new layer
-		if layerData != nil && img.Parent != "" {
-			var ids []string
-			if img.Parent != "" {
-				parentImg, err := graph.Get(img.Parent)
-				if err != nil {
-					return err
-				}
-
-				ids, err = graph.ParentLayerIds(parentImg)
-				if err != nil {
-					return err
-				}
-			}
-
-			if img.Size, err = wd.Import(img.ID, layerData, wd.LayerIdsToPaths(ids)); err != nil {
-				return err
-			}
-		}
-
-		if err := graph.saveSize(root, img.Size); err != nil {
-			return err
-		}
-
-		f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
-		if err != nil {
-			return err
-		}
-
-		defer f.Close()
-
-		return json.NewEncoder(f).Encode(img)
-	}
-	// We keep this functionality here so that we can still work with the
-	// VFS driver during development. This will not be used for actual running
-	// of Windows containers. Without this code, it would not be possible to
-	// docker pull using the VFS driver.
-
-	// Store the layer. If layerData is not nil, unpack it into the new layer
-	if layerData != nil {
-		if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil {
-			return err
-		}
-	}
-
-	if err := graph.saveSize(root, img.Size); err != nil {
-		return err
-	}
-
-	f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
-	if err != nil {
-		return err
-	}
-
-	defer f.Close()
-
-	return json.NewEncoder(f).Encode(img)
-}
-
-// TarLayer returns a tar archive of the image's filesystem layer.
-func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
-	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
-		var ids []string
-		if img.Parent != "" {
-			parentImg, err := graph.Get(img.Parent)
-			if err != nil {
-				return nil, err
-			}
-
-			ids, err = graph.ParentLayerIds(parentImg)
-			if err != nil {
-				return nil, err
-			}
-		}
-
-		return wd.Export(img.ID, wd.LayerIdsToPaths(ids))
-	}
-	// We keep this functionality here so that we can still work with the VFS
-	// driver during development. VFS is not supported (and just will not work)
-	// for Windows containers.
-	rdr, err := graph.assembleTarLayer(img)
-	if err != nil {
-		logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
-		return graph.driver.Diff(img.ID, img.Parent)
-	}
-	return rdr, nil
-}

+ 2 - 2
hack/make/.integration-daemon-start

@@ -55,8 +55,8 @@ else
 	export DOCKER_HOST="$DOCKER_TEST_HOST"
 fi
 
-# give it a second to come up so it's "ready"
-tries=10
+# give it a little time to come up so it's "ready"
+tries=30
 while ! docker version &> /dev/null; do
 	(( tries-- ))
 	if [ $tries -le 0 ]; then