Browse Source

Windows: Graph driver implementation

Signed-off-by: John Howard <jhoward@microsoft.com>
John Howard 10 years ago
parent
commit
52f4d09ffb

+ 10 - 3
builder/internals.go

@@ -24,7 +24,6 @@ import (
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/graph"
-	imagepkg "github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/httputils"
@@ -229,12 +228,20 @@ 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
 	}
@@ -455,7 +462,7 @@ func ContainsWildcards(name string) bool {
 	return false
 }
 
-func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
+func (b *Builder) pullImage(name string) (*graph.Image, error) {
 	remote, tag := parsers.ParseRepositoryTag(name)
 	if tag == "" {
 		tag = "latest"
@@ -493,7 +500,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
 	return image, nil
 }
 
-func (b *Builder) processImageFrom(img *imagepkg.Image) error {
+func (b *Builder) processImageFrom(img *graph.Image) error {
 	b.image = img.ID
 
 	if img.Config != nil {

+ 2 - 1
cliconfig/config.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/pkg/homedir"
+	"github.com/docker/docker/pkg/system"
 )
 
 const (
@@ -177,7 +178,7 @@ func (configFile *ConfigFile) Save() error {
 		return err
 	}
 
-	if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
+	if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
 		return err
 	}
 

+ 2 - 2
daemon/commit.go

@@ -1,7 +1,7 @@
 package daemon
 
 import (
-	"github.com/docker/docker/image"
+	"github.com/docker/docker/graph"
 	"github.com/docker/docker/runconfig"
 )
 
@@ -16,7 +16,7 @@ type ContainerCommitConfig struct {
 
 // Commit creates a new filesystem image from the current state of a container.
 // The image can optionally be tagged into a repository
-func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*image.Image, error) {
+func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*graph.Image, error) {
 	if c.Pause && !container.IsPaused() {
 		container.Pause()
 		defer container.Unpause()

+ 20 - 2
daemon/container.go

@@ -20,7 +20,7 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
 	"github.com/docker/docker/daemon/network"
-	"github.com/docker/docker/image"
+	"github.com/docker/docker/graph"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/fileutils"
@@ -259,6 +259,13 @@ func (container *Container) Start() (err error) {
 	if err := container.Mount(); err != nil {
 		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
+	}
+
 	if err := container.initializeNetworking(); err != nil {
 		return err
 	}
@@ -350,6 +357,10 @@ func (container *Container) cleanup() {
 
 	disableAllActiveLinks(container)
 
+	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)
 	}
@@ -573,7 +584,7 @@ func (container *Container) Changes() ([]archive.Change, error) {
 	return container.changes()
 }
 
-func (container *Container) GetImage() (*image.Image, error) {
+func (container *Container) GetImage() (*graph.Image, error) {
 	if container.daemon == nil {
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 	}
@@ -666,8 +677,15 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 	if err != nil {
 		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()
 		return err

+ 11 - 2
daemon/container_linux.go → daemon/container_unix.go

@@ -1,4 +1,4 @@
-// +build linux
+// +build !windows
 
 package daemon
 
@@ -23,6 +23,7 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/ulimit"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
@@ -970,7 +971,7 @@ func (container *Container) setupWorkingDirectory() error {
 				return err
 			}
 
-			if err := os.MkdirAll(pth, 0755); err != nil {
+			if err := system.MkdirAll(pth, 0755); err != nil {
 				return err
 			}
 		}
@@ -1109,3 +1110,11 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error {
 
 	return nil
 }
+
+func (container *Container) PrepareStorage() error {
+	return nil
+}
+
+func (container *Container) CleanupStorage() error {
+	return nil
+}

+ 56 - 0
daemon/container_windows.go

@@ -4,10 +4,14 @@ package daemon
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/docker/docker/daemon/execdriver"
+	"github.com/docker/docker/daemon/graphdriver/windows"
+	"github.com/docker/docker/graph"
 	"github.com/docker/docker/pkg/archive"
+	"github.com/microsoft/hcsshim"
 )
 
 // This is deliberately empty on Windows as the default path will be set by
@@ -102,6 +106,26 @@ 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 *graph.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)
+		}
+		if ids, err = c.daemon.graph.ParentLayerIds(img); err != nil {
+			return fmt.Errorf("Failed to get parentlayer ids %s", img.ID)
+		}
+		layerPaths = wd.LayerIdsToPaths(ids)
+		layerFolder = filepath.Join(wd.Info().HomeDir, filepath.Base(c.ID))
+	}
+
 	// TODO Windows: Factor out remainder of unused fields.
 	c.command = &execdriver.Command{
 		ID:             c.ID,
@@ -118,6 +142,8 @@ func populateCommand(c *Container, env []string) error {
 		ProcessLabel:   c.GetProcessLabel(),
 		MountLabel:     c.GetMountLabel(),
 		FirstStart:     !c.HasBeenStartedBefore,
+		LayerFolder:    layerFolder,
+		LayerPaths:     layerPaths,
 	}
 
 	return nil
@@ -165,3 +191,33 @@ func (container *Container) DisableLink(name string) {
 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
+}

+ 1 - 2
daemon/create.go

@@ -8,7 +8,6 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/graph"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
@@ -47,7 +46,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 	var (
 		container *Container
 		warnings  []string
-		img       *image.Image
+		img       *graph.Image
 		imgID     string
 		err       error
 	)

+ 3 - 4
daemon/daemon.go

@@ -23,7 +23,6 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/graph"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/graphdb"
@@ -338,7 +337,7 @@ func (daemon *Daemon) restore() error {
 	return nil
 }
 
-func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) error {
+func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *graph.Image) error {
 	if img != nil && img.Config != nil {
 		if err := runconfig.Merge(config, img.Config); err != nil {
 			return err
@@ -879,7 +878,7 @@ func (daemon *Daemon) ContainerGraph() *graphdb.Database {
 	return daemon.containerGraph
 }
 
-func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
+func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*graph.Image, error) {
 	// Retrieve all images
 	images, err := daemon.Graph().Map()
 	if err != nil {
@@ -896,7 +895,7 @@ func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*i
 	}
 
 	// Loop on the children of the given image and check the config
-	var match *image.Image
+	var match *graph.Image
 	for elem := range imageMap[imgID] {
 		img, ok := images[elem]
 		if !ok {

+ 33 - 2
daemon/daemon_windows.go

@@ -6,11 +6,14 @@ import (
 	"runtime"
 	"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/pkg/parsers"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/libnetwork"
+	"github.com/microsoft/hcsshim"
 )
 
 func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
@@ -31,8 +34,36 @@ func (daemon *Daemon) createRootfs(container *Container) error {
 	if err := os.Mkdir(container.root, 0700); err != nil {
 		return err
 	}
-	if err := daemon.driver.Create(container.ID, container.ImageID); 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
+		}
+
 	}
 	return nil
 }

+ 1 - 0
daemon/graphdriver/driver.go

@@ -101,6 +101,7 @@ func GetDriver(name, home string, options []string) (Driver, error) {
 	if initFunc, exists := drivers[name]; exists {
 		return initFunc(filepath.Join(home, name), options)
 	}
+	logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
 	return nil, ErrNotSupported
 }
 

+ 13 - 3
daemon/graphdriver/driver_windows.go

@@ -1,14 +1,24 @@
 package graphdriver
 
-type DiffDiskDriver interface {
+import (
+	"github.com/docker/docker/pkg/archive"
+	"github.com/microsoft/hcsshim"
+)
+
+type WindowsGraphDriver interface {
 	Driver
-	CopyDiff(id, sourceId string) error
+	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.ArchiveReader, parentLayerPaths []string) (int64, error)
 }
 
 var (
 	// Slice of drivers that should be used in order
 	priority = []string{
-		"windows",
+		"windowsfilter",
+		"windowsdiff",
 		"vfs",
 	}
 )

+ 326 - 0
daemon/graphdriver/windows/windows.go

@@ -0,0 +1,326 @@
+//+build windows
+
+package windows
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"sync"
+	"time"
+
+	"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/ioutils"
+	"github.com/microsoft/hcsshim"
+)
+
+func init() {
+	graphdriver.Register("windowsfilter", InitFilter)
+	graphdriver.Register("windowsdiff", InitDiff)
+}
+
+const (
+	diffDriver = iota
+	filterDriver
+)
+
+type WindowsGraphDriver struct {
+	info       hcsshim.DriverInfo
+	sync.Mutex // Protects concurrent modification to active
+	active     map[string]int
+}
+
+// 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{
+		info: hcsshim.DriverInfo{
+			HomeDir: home,
+			Flavour: filterDriver,
+		},
+		active: make(map[string]int),
+	}
+	return d, nil
+}
+
+// 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{
+		info: hcsshim.DriverInfo{
+			HomeDir: home,
+			Flavour: diffDriver,
+		},
+		active: make(map[string]int),
+	}
+	return d, nil
+}
+
+func (d *WindowsGraphDriver) Info() hcsshim.DriverInfo {
+	return d.info
+}
+
+func (d *WindowsGraphDriver) String() string {
+	switch d.info.Flavour {
+	case diffDriver:
+		return "windowsdiff"
+	case filterDriver:
+		return "windowsfilter"
+	default:
+		return "Unknown driver flavour"
+	}
+}
+
+func (d *WindowsGraphDriver) Status() [][2]string {
+	return [][2]string{
+		{"Windows", ""},
+	}
+}
+
+// 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)
+	if err != nil {
+		return false
+	}
+	return result
+}
+
+func (d *WindowsGraphDriver) Create(id, parent string) error {
+	return hcsshim.CreateLayer(d.info, id, parent)
+}
+
+func (d *WindowsGraphDriver) 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)
+}
+
+// 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) {
+	var dir string
+
+	d.Lock()
+	defer d.Unlock()
+
+	if d.active[id] == 0 {
+		if err := hcsshim.ActivateLayer(d.info, id); err != nil {
+			return "", err
+		}
+	}
+
+	mountPath, err := hcsshim.GetLayerMountPath(d.info, id)
+	if err != nil {
+		return "", err
+	}
+
+	// If the layer has a mount path, use that. Otherwise, use the
+	// folder path.
+	if mountPath != "" {
+		dir = mountPath
+	} else {
+		dir = d.dir(id)
+	}
+
+	d.active[id]++
+
+	return dir, nil
+}
+
+func (d *WindowsGraphDriver) Put(id string) error {
+	logrus.Debugf("WindowsGraphDriver Put() id %s", id)
+
+	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 {
+			return err
+		}
+		delete(d.active, id)
+	}
+
+	return nil
+}
+
+func (d *WindowsGraphDriver) 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()")
+}
+
+// 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) {
+	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.ArchiveReader) (size int64, err error) {
+	start := time.Now().UTC()
+	logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
+
+	destination := d.dir(id)
+	if d.info.Flavour == diffDriver {
+		destination = filepath.Dir(destination)
+	}
+
+	if size, err = chrootarchive.ApplyLayer(destination, diff); err != nil {
+		return
+	}
+	logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
+
+	return
+}
+
+// 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)
+	if err != nil {
+		return
+	}
+
+	layerFs, err := d.Get(id, "")
+	if err != nil {
+		return
+	}
+	defer d.Put(id)
+
+	return archive.ChangesSize(layerFs, changes), nil
+}
+
+func (d *WindowsGraphDriver) CopyDiff(sourceId, id string, parentLayerPaths []string) error {
+	d.Lock()
+	defer d.Unlock()
+
+	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)
+			}
+		}()
+	}
+
+	return hcsshim.CopyLayer(d.info, sourceId, id, parentLayerPaths)
+}
+
+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
+		}
+		paths = append(paths, path)
+	}
+	return paths
+}
+
+func (d *WindowsGraphDriver) GetMetadata(id string) (map[string]string, error) {
+	return nil, 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)
+		}
+	}()
+
+	tempFolder := layerFs + "-temp"
+	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 {
+				logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
+			}
+		}
+	}()
+
+	if err = hcsshim.ExportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
+		return
+	}
+
+	archive, err := archive.Tar(tempFolder, archive.Uncompressed)
+	if err != nil {
+		return
+	}
+	return ioutils.NewReadCloserWrapper(archive, func() error {
+		err := archive.Close()
+		d.Put(id)
+		if err2 := os.RemoveAll(tempFolder); err2 != nil {
+			logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
+		}
+		return err
+	}), nil
+
+}
+
+func (d *WindowsGraphDriver) Import(id string, layerData archive.ArchiveReader, parentLayerPaths []string) (size int64, err error) {
+	layerFs, err := d.Get(id, "")
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			d.Put(id)
+		}
+	}()
+
+	tempFolder := layerFs + "-temp"
+	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 {
+			logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
+		}
+	}()
+
+	start := time.Now().UTC()
+	logrus.Debugf("Start untar layer")
+	if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
+		return
+	}
+	logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
+
+	if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
+		return
+	}
+
+	return
+}

+ 1 - 2
daemon/image_delete.go

@@ -7,7 +7,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/graph"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/utils"
@@ -160,7 +159,7 @@ func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
 			return err
 		}
 
-		if err := daemon.graph.WalkHistory(parent, func(p image.Image) error {
+		if err := daemon.graph.WalkHistory(parent, func(p graph.Image) error {
 			if imgID == p.ID {
 				if container.IsRunning() {
 					if force {

+ 2 - 1
daemon/volumes.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/chrootarchive"
+	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume/local"
@@ -35,7 +36,7 @@ func (m *mountPoint) Setup() (string, error) {
 			if !os.IsNotExist(err) {
 				return "", err
 			}
-			if err := os.MkdirAll(m.Source, 0755); err != nil {
+			if err := system.MkdirAll(m.Source, 0755); err != nil {
 				return "", err
 			}
 		}

+ 2 - 1
docker/daemon.go

@@ -18,6 +18,7 @@ import (
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/signal"
+	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/timeutils"
 	"github.com/docker/docker/pkg/tlsconfig"
 	"github.com/docker/docker/registry"
@@ -51,7 +52,7 @@ func migrateKey() (err error) {
 			}
 		}()
 
-		if err := os.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil {
+		if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil {
 			return fmt.Errorf("Unable to create daemon configuration directory: %s", err)
 		}
 

+ 52 - 47
graph/graph.go

@@ -37,6 +37,22 @@ type Graph struct {
 	imageMutex imageMutex // protect images in driver.
 }
 
+type Image struct {
+	ID              string            `json:"id"`
+	Parent          string            `json:"parent,omitempty"`
+	Comment         string            `json:"comment,omitempty"`
+	Created         time.Time         `json:"created"`
+	Container       string            `json:"container,omitempty"`
+	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
+	DockerVersion   string            `json:"docker_version,omitempty"`
+	Author          string            `json:"author,omitempty"`
+	Config          *runconfig.Config `json:"config,omitempty"`
+	Architecture    string            `json:"architecture,omitempty"`
+	OS              string            `json:"os,omitempty"`
+	Size            int64
+	graph           Graph
+}
+
 var (
 	// ErrDigestNotSet is used when request the digest for a layer
 	// but the layer has no digest value or content to compute the
@@ -79,6 +95,13 @@ func (graph *Graph) restore() error {
 			ids = append(ids, id)
 		}
 	}
+
+	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
@@ -100,7 +123,7 @@ func (graph *Graph) Exists(id string) bool {
 }
 
 // Get returns the image with the given id, or an error if the image doesn't exist.
-func (graph *Graph) Get(name string) (*image.Image, error) {
+func (graph *Graph) Get(name string) (*Image, error) {
 	id, err := graph.idIndex.Get(name)
 	if err != nil {
 		return nil, fmt.Errorf("could not find image: %v", err)
@@ -128,8 +151,8 @@ func (graph *Graph) Get(name string) (*image.Image, error) {
 }
 
 // Create creates a new image and registers it in the graph.
-func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
-	img := &image.Image{
+func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*Image, error) {
+	img := &Image{
 		ID:            stringid.GenerateRandomID(),
 		Comment:       comment,
 		Created:       time.Now().UTC(),
@@ -153,7 +176,7 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
 }
 
 // Register imports a pre-existing image into the graph.
-func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
+func (graph *Graph) Register(img *Image, layerData archive.ArchiveReader) (err error) {
 	if err := image.ValidateID(img.ID); err != nil {
 		return err
 	}
@@ -197,9 +220,10 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
 	}
 
 	// Create root filesystem in the driver
-	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)
+	if err := createRootFilesystemInDriver(graph, img, layerData); err != nil {
+		return err
 	}
+
 	// Apply the diff/layer
 	if err := graph.storeImage(img, layerData, tmp); err != nil {
 		return err
@@ -303,9 +327,9 @@ func (graph *Graph) Delete(name string) error {
 }
 
 // Map returns a list of all images in the graph, addressable by ID.
-func (graph *Graph) Map() (map[string]*image.Image, error) {
-	images := make(map[string]*image.Image)
-	err := graph.walkAll(func(image *image.Image) {
+func (graph *Graph) Map() (map[string]*Image, error) {
+	images := make(map[string]*Image)
+	err := graph.walkAll(func(image *Image) {
 		images[image.ID] = image
 	})
 	if err != nil {
@@ -316,7 +340,7 @@ func (graph *Graph) Map() (map[string]*image.Image, error) {
 
 // walkAll iterates over each image in the graph, and passes it to a handler.
 // The walking order is undetermined.
-func (graph *Graph) walkAll(handler func(*image.Image)) error {
+func (graph *Graph) walkAll(handler func(*Image)) error {
 	files, err := ioutil.ReadDir(graph.root)
 	if err != nil {
 		return err
@@ -336,9 +360,9 @@ func (graph *Graph) walkAll(handler func(*image.Image)) error {
 // If an image of id ID has 3 children images, then the value for key ID
 // will be a list of 3 images.
 // If an image has no children, it will not have an entry in the table.
-func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
-	byParent := make(map[string][]*image.Image)
-	err := graph.walkAll(func(img *image.Image) {
+func (graph *Graph) ByParent() (map[string][]*Image, error) {
+	byParent := make(map[string][]*Image)
+	err := graph.walkAll(func(img *Image) {
 		parent, err := graph.Get(img.Parent)
 		if err != nil {
 			return
@@ -346,7 +370,7 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
 		if children, exists := byParent[parent.ID]; exists {
 			byParent[parent.ID] = append(children, img)
 		} else {
-			byParent[parent.ID] = []*image.Image{img}
+			byParent[parent.ID] = []*Image{img}
 		}
 	})
 	return byParent, err
@@ -354,13 +378,13 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
 
 // Heads returns all heads in the graph, keyed by id.
 // A head is an image which is not the parent of another image in the graph.
-func (graph *Graph) Heads() (map[string]*image.Image, error) {
-	heads := make(map[string]*image.Image)
+func (graph *Graph) Heads() (map[string]*Image, error) {
+	heads := make(map[string]*Image)
 	byParent, err := graph.ByParent()
 	if err != nil {
 		return nil, err
 	}
-	err = graph.walkAll(func(image *image.Image) {
+	err = graph.walkAll(func(image *Image) {
 		// If it's not in the byParent lookup table, then
 		// it's not a parent -> so it's a head!
 		if _, exists := byParent[image.ID]; !exists {
@@ -374,33 +398,8 @@ func (graph *Graph) imageRoot(id string) string {
 	return filepath.Join(graph.root, id)
 }
 
-// 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.ArchiveReader, root string) (err error) {
-	// Store the layer. If layerData is not nil, unpack it into the new layer
-	if layerData != nil {
-		if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
-			return err
-		}
-	}
-
-	if err := graph.saveSize(root, int(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)
-}
-
 // loadImage fetches the image with the given id from the graph.
-func (graph *Graph) loadImage(id string) (*image.Image, error) {
+func (graph *Graph) loadImage(id string) (*Image, error) {
 	root := graph.imageRoot(id)
 
 	// Open the JSON file to decode by streaming
@@ -410,7 +409,7 @@ func (graph *Graph) loadImage(id string) (*image.Image, error) {
 	}
 	defer jsonSource.Close()
 
-	img := &image.Image{}
+	img := &Image{}
 	dec := json.NewDecoder(jsonSource)
 
 	// Decode the JSON data
@@ -488,7 +487,13 @@ func jsonPath(root string) string {
 	return filepath.Join(root, "json")
 }
 
-// TarLayer returns a tar archive of the image's filesystem layer.
-func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
-	return graph.driver.Diff(img.ID, img.Parent)
+// Build an Image object from raw json data
+func NewImgJSON(src []byte) (*Image, error) {
+	ret := &Image{}
+
+	// FIXME: Is there a cleaner way to "purify" the input json?
+	if err := json.Unmarshal(src, ret); err != nil {
+		return nil, err
+	}
+	return ret, nil
 }

+ 6 - 6
graph/graph_test.go

@@ -68,7 +68,7 @@ func TestInterruptedRegister(t *testing.T) {
 	graph, _ := tempGraph(t)
 	defer nukeGraph(graph)
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
-	image := &image.Image{
+	image := &Image{
 		ID:      stringid.GenerateRandomID(),
 		Comment: "testing",
 		Created: time.Now(),
@@ -128,7 +128,7 @@ func TestRegister(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	image := &image.Image{
+	image := &Image{
 		ID:      stringid.GenerateRandomID(),
 		Comment: "testing",
 		Created: time.Now(),
@@ -232,19 +232,19 @@ func TestByParent(t *testing.T) {
 
 	graph, _ := tempGraph(t)
 	defer nukeGraph(graph)
-	parentImage := &image.Image{
+	parentImage := &Image{
 		ID:      stringid.GenerateRandomID(),
 		Comment: "parent",
 		Created: time.Now(),
 		Parent:  "",
 	}
-	childImage1 := &image.Image{
+	childImage1 := &Image{
 		ID:      stringid.GenerateRandomID(),
 		Comment: "child1",
 		Created: time.Now(),
 		Parent:  parentImage.ID,
 	}
-	childImage2 := &image.Image{
+	childImage2 := &Image{
 		ID:      stringid.GenerateRandomID(),
 		Comment: "child2",
 		Created: time.Now(),
@@ -264,7 +264,7 @@ func TestByParent(t *testing.T) {
 	}
 }
 
-func createTestImage(graph *Graph, t *testing.T) *image.Image {
+func createTestImage(graph *Graph, t *testing.T) *Image {
 	archive, err := fakeTar()
 	if err != nil {
 		t.Fatal(err)

+ 114 - 0
graph/graph_unix.go

@@ -0,0 +1,114 @@
+// +build !windows
+
+package graph
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"syscall"
+
+	"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, layerData archive.ArchiveReader) 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, layerData archive.ArchiveReader, root string) (err error) {
+	// Store the layer. If layerData is not nil, unpack it into the new layer
+	if layerData != nil {
+		if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
+			return err
+		}
+	}
+
+	if err := graph.saveSize(root, int(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) (arch archive.Archive, err error) {
+	return graph.driver.Diff(img.ID, img.Parent)
+}

+ 160 - 0
graph/graph_windows.go

@@ -0,0 +1,160 @@
+// +build windows
+
+package graph
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/daemon/graphdriver/windows"
+	"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, layerData archive.ArchiveReader) 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) (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, layerData archive.ArchiveReader, 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, int(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)
+	} else {
+		// 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 img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
+				return err
+			}
+		}
+
+		if err := graph.saveSize(root, int(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) (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))
+	} else {
+		// 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.
+		return graph.driver.Diff(img.ID, img.Parent)
+	}
+}

+ 6 - 7
graph/history.go

@@ -5,13 +5,12 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/utils"
 )
 
 // WalkHistory calls the handler function for each image in the
 // provided images lineage starting from immediate parent.
-func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) error) (err error) {
+func (graph *Graph) WalkHistory(img *Image, handler func(Image) error) (err error) {
 	currentImg := img
 	for currentImg != nil {
 		if handler != nil {
@@ -29,7 +28,7 @@ func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) erro
 
 // depth returns the number of parents for a
 // current image
-func (graph *Graph) depth(img *image.Image) (int, error) {
+func (graph *Graph) depth(img *Image) (int, error) {
 	var (
 		count  = 0
 		parent = img
@@ -54,7 +53,7 @@ const MaxImageDepth = 127
 // CheckDepth returns an error if the depth of an image, as returned
 // by ImageDepth, is too large to support creating a container from it
 // on this daemon.
-func (graph *Graph) CheckDepth(img *image.Image) error {
+func (graph *Graph) CheckDepth(img *Image) error {
 	// We add 2 layers to the depth because the container's rw and
 	// init layer add to the restriction
 	depth, err := graph.depth(img)
@@ -86,7 +85,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
 
 	history := []*types.ImageHistory{}
 
-	err = s.graph.WalkHistory(foundImage, func(img image.Image) error {
+	err = s.graph.WalkHistory(foundImage, func(img Image) error {
 		history = append(history, &types.ImageHistory{
 			ID:        img.ID,
 			Created:   img.Created.Unix(),
@@ -101,14 +100,14 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
 	return history, err
 }
 
-func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
+func (graph *Graph) GetParent(img *Image) (*Image, error) {
 	if img.Parent == "" {
 		return nil, nil
 	}
 	return graph.Get(img.Parent)
 }
 
-func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 {
+func (graph *Graph) GetParentsSize(img *Image, size int64) int64 {
 	parentImage, err := graph.GetParent(img)
 	if err != nil || parentImage == nil {
 		return size

+ 1 - 2
graph/list.go

@@ -8,7 +8,6 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/utils"
 )
@@ -32,7 +31,7 @@ func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
 
 func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
 	var (
-		allImages  map[string]*image.Image
+		allImages  map[string]*Image
 		err        error
 		filtTagged = true
 		filtLabel  = false

+ 1 - 1
graph/load.go

@@ -98,7 +98,7 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
 			logrus.Debugf("Error reading embedded tar", err)
 			return err
 		}
-		img, err := image.NewImgJSON(imageJson)
+		img, err := NewImgJSON(imageJson)
 		if err != nil {
 			logrus.Debugf("Error unmarshalling json", err)
 			return err

+ 2 - 3
graph/manifest_test.go

@@ -7,7 +7,6 @@ import (
 	"testing"
 
 	"github.com/docker/distribution/digest"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
@@ -124,7 +123,7 @@ func TestManifestTarsumCache(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	img := &image.Image{ID: testManifestImageID}
+	img := &Image{ID: testManifestImageID}
 	if err := store.graph.Register(img, archive); err != nil {
 		t.Fatal(err)
 	}
@@ -190,7 +189,7 @@ func TestManifestDigestCheck(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	img := &image.Image{ID: testManifestImageID}
+	img := &Image{ID: testManifestImageID}
 	if err := store.graph.Register(img, archive); err != nil {
 		t.Fatal(err)
 	}

+ 4 - 5
graph/pull.go

@@ -13,7 +13,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
@@ -387,7 +386,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
 				imgJSON []byte
 				imgSize int
 				err     error
-				img     *image.Image
+				img     *Image
 			)
 			retries := 5
 			for j := 1; j <= retries; j++ {
@@ -399,7 +398,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
 					time.Sleep(time.Duration(j) * 500 * time.Millisecond)
 					continue
 				}
-				img, err = image.NewImgJSON(imgJSON)
+				img, err = NewImgJSON(imgJSON)
 				layersDownloaded = true
 				if err != nil && j == retries {
 					out.Write(sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))
@@ -540,7 +539,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
 	// downloadInfo is used to pass information from download to extractor
 	type downloadInfo struct {
 		imgJSON    []byte
-		img        *image.Image
+		img        *Image
 		digest     digest.Digest
 		tmpFile    *os.File
 		length     int64
@@ -556,7 +555,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
 			imgJSON = []byte(manifest.History[i].V1Compatibility)
 		)
 
-		img, err := image.NewImgJSON(imgJSON)
+		img, err := NewImgJSON(imgJSON)
 		if err != nil {
 			return false, fmt.Errorf("failed to parse json: %s", err)
 		}

+ 2 - 3
graph/push.go

@@ -11,7 +11,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
@@ -349,7 +348,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
 		}
 
 		layersSeen := make(map[string]bool)
-		layers := []*image.Image{}
+		layers := []*Image{}
 		for ; layer != nil; layer, err = s.graph.GetParent(layer) {
 			if err != nil {
 				return err
@@ -445,7 +444,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
 }
 
 // PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk
-func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
+func (s *TagStore) pushV2Image(r *registry.Session, img *Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
 	out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))
 
 	image, err := s.graph.Get(img.ID)

+ 14 - 9
graph/service.go

@@ -3,6 +3,7 @@ package graph
 import (
 	"fmt"
 	"io"
+	"runtime"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
@@ -58,17 +59,21 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) {
 // ImageTarLayer return the tarLayer of the image
 func (s *TagStore) ImageTarLayer(name string, dest io.Writer) error {
 	if image, err := s.LookupImage(name); err == nil && image != nil {
-		fs, err := s.graph.TarLayer(image)
-		if err != nil {
-			return err
-		}
-		defer fs.Close()
+		// On Windows, the base layer cannot be exported
+		if runtime.GOOS != "windows" || image.Parent != "" {
+
+			fs, err := s.graph.TarLayer(image)
+			if err != nil {
+				return err
+			}
+			defer fs.Close()
 
-		written, err := io.Copy(dest, fs)
-		if err != nil {
-			return err
+			written, err := io.Copy(dest, fs)
+			if err != nil {
+				return err
+			}
+			logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
 		}
-		logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
 		return nil
 	}
 	return fmt.Errorf("No such image: %s", name)

+ 3 - 4
graph/tags.go

@@ -15,7 +15,6 @@ import (
 
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/graph/tags"
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/registry"
@@ -126,7 +125,7 @@ func (store *TagStore) reload() error {
 	return nil
 }
 
-func (store *TagStore) LookupImage(name string) (*image.Image, error) {
+func (store *TagStore) LookupImage(name string) (*Image, error) {
 	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 	// (so we can pass all errors here)
 	repoName, ref := parsers.ParseRepositoryTag(name)
@@ -135,7 +134,7 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
 	}
 	var (
 		err error
-		img *image.Image
+		img *Image
 	)
 
 	img, err = store.GetImage(repoName, ref)
@@ -330,7 +329,7 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
 	return nil, nil
 }
 
-func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
+func (store *TagStore) GetImage(repoName, refOrID string) (*Image, error) {
 	repo, err := store.Get(repoName)
 
 	if err != nil {

+ 2 - 3
graph/tags_unit_test.go

@@ -11,7 +11,6 @@ import (
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/graphdriver"
 	_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
-	"github.com/docker/docker/image"
 	"github.com/docker/docker/trust"
 	"github.com/docker/docker/utils"
 )
@@ -80,7 +79,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
 	if err != nil {
 		t.Fatal(err)
 	}
-	img := &image.Image{ID: testOfficialImageID}
+	img := &Image{ID: testOfficialImageID}
 	if err := graph.Register(img, officialArchive); err != nil {
 		t.Fatal(err)
 	}
@@ -91,7 +90,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
 	if err != nil {
 		t.Fatal(err)
 	}
-	img = &image.Image{ID: testPrivateImageID}
+	img = &Image{ID: testPrivateImageID}
 	if err := graph.Register(img, privateArchive); err != nil {
 		t.Fatal(err)
 	}

+ 0 - 30
image/image.go

@@ -1,42 +1,12 @@
 package image
 
 import (
-	"encoding/json"
 	"fmt"
 	"regexp"
-	"time"
-
-	"github.com/docker/docker/runconfig"
 )
 
 var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
 
-type Image struct {
-	ID              string            `json:"id"`
-	Parent          string            `json:"parent,omitempty"`
-	Comment         string            `json:"comment,omitempty"`
-	Created         time.Time         `json:"created"`
-	Container       string            `json:"container,omitempty"`
-	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
-	DockerVersion   string            `json:"docker_version,omitempty"`
-	Author          string            `json:"author,omitempty"`
-	Config          *runconfig.Config `json:"config,omitempty"`
-	Architecture    string            `json:"architecture,omitempty"`
-	OS              string            `json:"os,omitempty"`
-	Size            int64
-}
-
-// Build an Image object from raw json data
-func NewImgJSON(src []byte) (*Image, error) {
-	ret := &Image{}
-
-	// FIXME: Is there a cleaner way to "purify" the input json?
-	if err := json.Unmarshal(src, ret); err != nil {
-		return nil, err
-	}
-	return ret, nil
-}
-
 // Check wheather id is a valid image ID or not
 func ValidateID(id string) error {
 	if ok := validHex.MatchString(id); !ok {