daemon/graphdriver/windows/windows.go
52f4d09f
 //+build windows
 
 package windows
 
 import (
5649030e
 	"bufio"
b3bc5e0f
 	"bytes"
dfbb5520
 	"encoding/json"
1cb9e9b4
 	"errors"
52f4d09f
 	"fmt"
5649030e
 	"io"
dfbb5520
 	"io/ioutil"
52f4d09f
 	"os"
5649030e
 	"path"
52f4d09f
 	"path/filepath"
b3bc5e0f
 	"strconv"
dfbb5520
 	"strings"
4bac8bce
 	"sync"
5649030e
 	"syscall"
d4095a59
 	"time"
6f887887
 	"unsafe"
52f4d09f
 
5649030e
 	"github.com/Microsoft/go-winio"
 	"github.com/Microsoft/go-winio/archive/tar"
 	"github.com/Microsoft/go-winio/backuptar"
b9a395c8
 	"github.com/Microsoft/hcsshim"
52f4d09f
 	"github.com/docker/docker/daemon/graphdriver"
 	"github.com/docker/docker/pkg/archive"
7a7357da
 	"github.com/docker/docker/pkg/containerfs"
442b4562
 	"github.com/docker/docker/pkg/idtools"
52f4d09f
 	"github.com/docker/docker/pkg/ioutils"
cf7944bf
 	"github.com/docker/docker/pkg/longpath"
b3bc5e0f
 	"github.com/docker/docker/pkg/reexec"
83a2db20
 	"github.com/docker/docker/pkg/system"
22c83c56
 	units "github.com/docker/go-units"
1009e6a4
 	"github.com/sirupsen/logrus"
22c83c56
 	"golang.org/x/sys/windows"
52f4d09f
 )
 
dd0fc2be
 // filterDriver is an HCSShim driver type for the Windows Filter driver.
 const filterDriver = 1
 
7e5ee6d1
 var (
9c79b0ef
 	// mutatedFiles is a list of files that are mutated by the import process
 	// and must be backed up and restored.
 	mutatedFiles = map[string]string{
 		"UtilityVM/Files/EFI/Microsoft/Boot/BCD":      "bcd.bak",
 		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG":  "bcd.log.bak",
 		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak",
 		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak",
 	}
d9294719
 	noreexec = false
7e5ee6d1
 )
 
1fffc7a8
 // init registers the windows graph drivers to the register.
52f4d09f
 func init() {
 	graphdriver.Register("windowsfilter", InitFilter)
d9294719
 	// DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes
 	// debugging issues in the re-exec codepath significantly easier.
 	if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" {
 		logrus.Warnf("WindowsGraphDriver is set to not re-exec. This is intended for debugging purposes only.")
 		noreexec = true
 	} else {
 		reexec.Register("docker-windows-write-layer", writeLayerReexec)
 	}
52f4d09f
 }
 
4bac8bce
 type checker struct {
 }
 
 func (c *checker) IsMounted(path string) bool {
 	return false
 }
52f4d09f
 
1fffc7a8
 // Driver represents a windows graph driver.
dfbb5520
 type Driver struct {
1fffc7a8
 	// info stores the shim driver information
 	info hcsshim.DriverInfo
4bac8bce
 	ctr  *graphdriver.RefCounter
 	// it is safe for windows to use a cache here because it does not support
 	// restoring containers when the daemon dies.
 	cacheMu sync.Mutex
 	cache   map[string]string
52f4d09f
 }
 
1fffc7a8
 // InitFilter returns a new Windows storage filter driver.
442b4562
 func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
52f4d09f
 	logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
1cb9e9b4
 
 	fsType, err := getFileSystemType(string(home[0]))
 	if err != nil {
 		return nil, err
 	}
 	if strings.ToLower(fsType) == "refs" {
 		return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
 	}
 
3aa4a007
 	if err := idtools.MkdirAllAs(home, 0700, 0, 0); err != nil {
 		return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
 	}
 
dfbb5520
 	d := &Driver{
52f4d09f
 		info: hcsshim.DriverInfo{
 			HomeDir: home,
 			Flavour: filterDriver,
 		},
4bac8bce
 		cache: make(map[string]string),
 		ctr:   graphdriver.NewRefCounter(&checker{}),
52f4d09f
 	}
 	return d, nil
 }
 
1cb9e9b4
 // win32FromHresult is a helper function to get the win32 error code from an HRESULT
 func win32FromHresult(hr uintptr) uintptr {
 	if hr&0x1fff0000 == 0x00070000 {
 		return hr & 0xffff
 	}
 	return hr
 }
 
 // getFileSystemType obtains the type of a file system through GetVolumeInformation
 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx
 func getFileSystemType(drive string) (fsType string, hr error) {
 	var (
22c83c56
 		modkernel32              = windows.NewLazySystemDLL("kernel32.dll")
1cb9e9b4
 		procGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW")
 		buf                      = make([]uint16, 255)
069fdc8a
 		size                     = windows.MAX_PATH + 1
1cb9e9b4
 	)
 	if len(drive) != 1 {
 		hr = errors.New("getFileSystemType must be called with a drive letter")
 		return
 	}
 	drive += `:\`
 	n := uintptr(unsafe.Pointer(nil))
069fdc8a
 	r0, _, _ := syscall.Syscall9(procGetVolumeInformation.Addr(), 8, uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(drive))), n, n, n, n, n, uintptr(unsafe.Pointer(&buf[0])), uintptr(size), 0)
1cb9e9b4
 	if int32(r0) < 0 {
 		hr = syscall.Errno(win32FromHresult(r0))
 	}
069fdc8a
 	fsType = windows.UTF16ToString(buf)
1cb9e9b4
 	return
 }
 
f182748b
 // String returns the string representation of a driver. This should match
 // the name the graph driver has been registered with.
dfbb5520
 func (d *Driver) String() string {
f182748b
 	return "windowsfilter"
52f4d09f
 }
 
1fffc7a8
 // Status returns the status of the driver.
dfbb5520
 func (d *Driver) Status() [][2]string {
52f4d09f
 	return [][2]string{
 		{"Windows", ""},
 	}
 }
 
bdabc76a
 // panicIfUsedByLcow does exactly what it says.
4ec9766a
 // TODO @jhowardmsft - this is a temporary measure for the bring-up of
bdabc76a
 // Linux containers on Windows. It is a failsafe to ensure that the right
 // graphdriver is used.
 func panicIfUsedByLcow() {
 	if system.LCOWSupported() {
 		panic("inconsistency - windowsfilter graphdriver should not be used when in LCOW mode")
 	}
 }
 
1fffc7a8
 // Exists returns true if the given id is registered with this driver.
dfbb5520
 func (d *Driver) Exists(id string) bool {
bdabc76a
 	panicIfUsedByLcow()
1fffc7a8
 	rID, err := d.resolveID(id)
dfbb5520
 	if err != nil {
 		return false
 	}
1fffc7a8
 	result, err := hcsshim.LayerExists(d.info, rID)
52f4d09f
 	if err != nil {
 		return false
 	}
 	return result
 }
 
ef5bfad3
 // CreateReadWrite creates a layer that is writable for use as a container
 // file system.
b937aa8e
 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
bdabc76a
 	panicIfUsedByLcow()
b937aa8e
 	if opts != nil {
 		return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
 	}
9c559e6d
 	return d.create(id, parent, "", false, nil)
ef5bfad3
 }
 
 // Create creates a new read-only layer with the given id.
b937aa8e
 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
bdabc76a
 	panicIfUsedByLcow()
b937aa8e
 	if opts != nil {
 		return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
 	}
9c559e6d
 	return d.create(id, parent, "", true, nil)
ef5bfad3
 }
 
 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
1fffc7a8
 	rPId, err := d.resolveID(parent)
dfbb5520
 	if err != nil {
 		return err
 	}
 
 	parentChain, err := d.getLayerChain(rPId)
 	if err != nil {
 		return err
 	}
 
 	var layerChain []string
 
ef5bfad3
 	if rPId != "" {
dfbb5520
 		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
 		if err != nil {
 			return err
 		}
ef5bfad3
 		if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil {
 			// This is a legitimate parent layer (not the empty "-init" layer),
 			// so include it in the layer chain.
 			layerChain = []string{parentPath}
 		}
dfbb5520
 	}
 
 	layerChain = append(layerChain, parentChain...)
 
ef5bfad3
 	if readOnly {
 		if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
dfbb5520
 			return err
 		}
 	} else {
ef5bfad3
 		var parentPath string
 		if len(layerChain) != 0 {
 			parentPath = layerChain[0]
 		}
6f887887
 
ef5bfad3
 		if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil {
dfbb5520
 			return err
 		}
7e5ee6d1
 
 		storageOptions, err := parseStorageOpt(storageOpt)
 		if err != nil {
 			return fmt.Errorf("Failed to parse storage options - %s", err)
 		}
 
0da31c68
 		if storageOptions.size != 0 {
7e5ee6d1
 			if err := hcsshim.ExpandSandboxSize(d.info, id, storageOptions.size); err != nil {
 				return err
 			}
 		}
dfbb5520
 	}
 
a456f20b
 	if _, err := os.Lstat(d.dir(parent)); err != nil {
 		if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
 			logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
dfbb5520
 		}
a456f20b
 		return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err)
 	}
 
 	if err := d.setLayerChain(id, layerChain); err != nil {
dfbb5520
 		if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
a456f20b
 			logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
dfbb5520
 		}
 		return err
 	}
 
 	return nil
52f4d09f
 }
 
1fffc7a8
 // dir returns the absolute path to the layer.
dfbb5520
 func (d *Driver) dir(id string) string {
52f4d09f
 	return filepath.Join(d.info.HomeDir, filepath.Base(id))
 }
 
1fffc7a8
 // Remove unmounts and removes the dir information.
dfbb5520
 func (d *Driver) Remove(id string) error {
bdabc76a
 	panicIfUsedByLcow()
1fffc7a8
 	rID, err := d.resolveID(id)
dfbb5520
 	if err != nil {
 		return err
 	}
be68006d
 
d4095a59
 	// This retry loop is due to a bug in Windows (Internal bug #9432268)
 	// if GetContainers fails with ErrVmcomputeOperationInvalidState
 	// it is a transient error. Retry until it succeeds.
 	var computeSystems []hcsshim.ContainerProperties
 	retryCount := 0
83a2db20
 	osv := system.GetOSVersion()
d4095a59
 	for {
83a2db20
 		// Get and terminate any template VMs that are currently using the layer.
 		// Note: It is unfortunate that we end up in the graphdrivers Remove() call
 		// for both containers and images, but the logic for template VMs is only
 		// needed for images - specifically we are looking to see if a base layer
 		// is in use by a template VM as a result of having started a Hyper-V
 		// container at some point.
 		//
 		// We have a retry loop for ErrVmcomputeOperationInvalidState and
 		// ErrVmcomputeOperationAccessIsDenied as there is a race condition
 		// in RS1 and RS2 building during enumeration when a silo is going away
 		// for example under it, in HCS. AccessIsDenied added to fix 30278.
 		//
 		// TODO @jhowardmsft - For RS3, we can remove the retries. Also consider
 		// using platform APIs (if available) to get this more succinctly. Also
39bcaee4
 		// consider enhancing the Remove() interface to have context of why
83a2db20
 		// the remove is being called - that could improve efficiency by not
 		// enumerating compute systems during a remove of a container as it's
 		// not required.
d4095a59
 		computeSystems, err = hcsshim.GetContainers(hcsshim.ComputeSystemQuery{})
 		if err != nil {
83a2db20
 			if (osv.Build < 15139) &&
 				((err == hcsshim.ErrVmcomputeOperationInvalidState) || (err == hcsshim.ErrVmcomputeOperationAccessIsDenied)) {
 				if retryCount >= 500 {
d4095a59
 					break
 				}
 				retryCount++
83a2db20
 				time.Sleep(10 * time.Millisecond)
d4095a59
 				continue
 			}
 			return err
 		}
 		break
c58e8dea
 	}
 
 	for _, computeSystem := range computeSystems {
 		if strings.Contains(computeSystem.RuntimeImagePath, id) && computeSystem.IsRuntimeTemplate {
 			container, err := hcsshim.OpenContainer(computeSystem.ID)
 			if err != nil {
 				return err
 			}
 			defer container.Close()
 			err = container.Terminate()
 			if hcsshim.IsPending(err) {
 				err = container.Wait()
 			} else if hcsshim.IsAlreadyStopped(err) {
 				err = nil
 			}
 
 			if err != nil {
 				return err
 			}
 		}
 	}
 
be68006d
 	layerPath := filepath.Join(d.info.HomeDir, rID)
 	tmpID := fmt.Sprintf("%s-removing", rID)
 	tmpLayerPath := filepath.Join(d.info.HomeDir, tmpID)
 	if err := os.Rename(layerPath, tmpLayerPath); err != nil && !os.IsNotExist(err) {
 		return err
 	}
 	if err := hcsshim.DestroyLayer(d.info, tmpID); err != nil {
 		logrus.Errorf("Failed to DestroyLayer %s: %s", id, err)
 	}
 
 	return nil
52f4d09f
 }
 
4e959ef2
 // Get returns the rootfs path for the id. This will mount the dir at its given path.
7a7357da
 func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
bdabc76a
 	panicIfUsedByLcow()
dfbb5520
 	logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
52f4d09f
 	var dir string
 
1fffc7a8
 	rID, err := d.resolveID(id)
dfbb5520
 	if err != nil {
7a7357da
 		return nil, err
dfbb5520
 	}
4bac8bce
 	if count := d.ctr.Increment(rID); count > 1 {
7a7357da
 		return containerfs.NewLocalContainerFS(d.cache[rID]), nil
4bac8bce
 	}
dfbb5520
 
 	// Getting the layer paths must be done outside of the lock.
1fffc7a8
 	layerChain, err := d.getLayerChain(rID)
dfbb5520
 	if err != nil {
4bac8bce
 		d.ctr.Decrement(rID)
7a7357da
 		return nil, err
dfbb5520
 	}
 
65d79e3e
 	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
4bac8bce
 		d.ctr.Decrement(rID)
7a7357da
 		return nil, err
65d79e3e
 	}
 	if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
4bac8bce
 		d.ctr.Decrement(rID)
65d79e3e
 		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
 			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
52f4d09f
 		}
7a7357da
 		return nil, err
52f4d09f
 	}
 
1fffc7a8
 	mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
52f4d09f
 	if err != nil {
4bac8bce
 		d.ctr.Decrement(rID)
7fab9b8a
 		if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
 			logrus.Warnf("Failed to Unprepare %s: %s", id, err)
 		}
1fffc7a8
 		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
dfbb5520
 			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
 		}
7a7357da
 		return nil, err
52f4d09f
 	}
4bac8bce
 	d.cacheMu.Lock()
 	d.cache[rID] = mountPath
 	d.cacheMu.Unlock()
52f4d09f
 
 	// If the layer has a mount path, use that. Otherwise, use the
 	// folder path.
 	if mountPath != "" {
 		dir = mountPath
 	} else {
 		dir = d.dir(id)
 	}
 
7a7357da
 	return containerfs.NewLocalContainerFS(dir), nil
52f4d09f
 }
 
1fffc7a8
 // Put adds a new layer to the driver.
dfbb5520
 func (d *Driver) Put(id string) error {
bdabc76a
 	panicIfUsedByLcow()
52f4d09f
 	logrus.Debugf("WindowsGraphDriver Put() id %s", id)
 
1fffc7a8
 	rID, err := d.resolveID(id)
dfbb5520
 	if err != nil {
 		return err
 	}
4bac8bce
 	if count := d.ctr.Decrement(rID); count > 0 {
 		return nil
 	}
 	d.cacheMu.Lock()
7fab9b8a
 	_, exists := d.cache[rID]
4bac8bce
 	delete(d.cache, rID)
 	d.cacheMu.Unlock()
dfbb5520
 
7fab9b8a
 	// If the cache was not populated, then the layer was left unprepared and deactivated
 	if !exists {
 		return nil
 	}
 
65d79e3e
 	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
 		return err
52f4d09f
 	}
65d79e3e
 	return hcsshim.DeactivateLayer(d.info, rID)
52f4d09f
 }
 
1fffc7a8
 // Cleanup ensures the information the driver stores is properly removed.
9910b9a7
 // We use this opportunity to cleanup any -removing folders which may be
 // still left if the daemon was killed while it was removing a layer.
dfbb5520
 func (d *Driver) Cleanup() error {
9910b9a7
 	items, err := ioutil.ReadDir(d.info.HomeDir)
 	if err != nil {
5e4e357f
 		if os.IsNotExist(err) {
 			return nil
 		}
9910b9a7
 		return err
 	}
 
 	// Note we don't return an error below - it's possible the files
 	// are locked. However, next time around after the daemon exits,
 	// we likely will be able to to cleanup successfully. Instead we log
 	// warnings if there are errors.
 	for _, item := range items {
 		if item.IsDir() && strings.HasSuffix(item.Name(), "-removing") {
 			if err := hcsshim.DestroyLayer(d.info, item.Name()); err != nil {
 				logrus.Warnf("Failed to cleanup %s: %s", item.Name(), err)
 			} else {
 				logrus.Infof("Cleaned up %s", item.Name())
 			}
 		}
 	}
 
52f4d09f
 	return nil
 }
 
 // Diff produces an archive of the changes between the specified
 // layer and its parent layer which may be "".
65d79e3e
 // The layer should be mounted when calling this function
aa2cc187
 func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
bdabc76a
 	panicIfUsedByLcow()
1fffc7a8
 	rID, err := d.resolveID(id)
dfbb5520
 	if err != nil {
 		return
 	}
 
1fffc7a8
 	layerChain, err := d.getLayerChain(rID)
dfbb5520
 	if err != nil {
 		return
 	}
 
65d79e3e
 	// this is assuming that the layer is unmounted
 	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
 		return nil, err
e91de9fb
 	}
b3bc5e0f
 	prepare := func() {
65d79e3e
 		if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
 			logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
 		}
b3bc5e0f
 	}
dfbb5520
 
5649030e
 	arch, err := d.exportLayer(rID, layerChain)
 	if err != nil {
b3bc5e0f
 		prepare()
5649030e
 		return
 	}
 	return ioutils.NewReadCloserWrapper(arch, func() error {
b3bc5e0f
 		err := arch.Close()
 		prepare()
 		return err
5649030e
 	}), nil
52f4d09f
 }
 
 // Changes produces a list of changes between the specified layer
 // and its parent layer. If parent is "", then all changes will be ADD changes.
4fac6036
 // The layer should not be mounted when calling this function.
dfbb5520
 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
bdabc76a
 	panicIfUsedByLcow()
5649030e
 	rID, err := d.resolveID(id)
 	if err != nil {
 		return nil, err
 	}
 	parentChain, err := d.getLayerChain(rID)
 	if err != nil {
 		return nil, err
 	}
 
4fac6036
 	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
65d79e3e
 		return nil, err
e91de9fb
 	}
65d79e3e
 	defer func() {
4fac6036
 		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
 			logrus.Errorf("changes() failed to DeactivateLayer %s %s: %s", id, rID, err2)
65d79e3e
 		}
 	}()
5649030e
 
 	var changes []archive.Change
b3bc5e0f
 	err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
 		r, err := hcsshim.NewLayerReader(d.info, id, parentChain)
5649030e
 		if err != nil {
b3bc5e0f
 			return err
5649030e
 		}
b3bc5e0f
 		defer r.Close()
 
 		for {
 			name, _, fileInfo, err := r.Next()
 			if err == io.EOF {
 				return nil
 			}
 			if err != nil {
 				return err
 			}
 			name = filepath.ToSlash(name)
 			if fileInfo == nil {
 				changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete})
 			} else {
 				// Currently there is no way to tell between an add and a modify.
 				changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify})
 			}
5649030e
 		}
b3bc5e0f
 	})
 	if err != nil {
 		return nil, err
5649030e
 	}
b3bc5e0f
 
5649030e
 	return changes, nil
52f4d09f
 }
 
 // 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.
65d79e3e
 // The layer should not be mounted when calling this function
aa2cc187
 func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
bdabc76a
 	panicIfUsedByLcow()
cf7944bf
 	var layerChain []string
 	if parent != "" {
 		rPId, err := d.resolveID(parent)
 		if err != nil {
 			return 0, err
 		}
 		parentChain, err := d.getLayerChain(rPId)
 		if err != nil {
 			return 0, err
 		}
 		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
 		if err != nil {
 			return 0, err
 		}
 		layerChain = append(layerChain, parentPath)
 		layerChain = append(layerChain, parentChain...)
dfbb5520
 	}
 
cf7944bf
 	size, err := d.importLayer(id, diff, layerChain)
 	if err != nil {
 		return 0, err
dfbb5520
 	}
 
 	if err = d.setLayerChain(id, layerChain); err != nil {
cf7944bf
 		return 0, err
52f4d09f
 	}
 
cf7944bf
 	return size, nil
52f4d09f
 }
 
 // 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.
dfbb5520
 func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
bdabc76a
 	panicIfUsedByLcow()
1fffc7a8
 	rPId, err := d.resolveID(parent)
dfbb5520
 	if err != nil {
 		return
 	}
 
 	changes, err := d.Changes(id, rPId)
52f4d09f
 	if err != nil {
 		return
 	}
 
 	layerFs, err := d.Get(id, "")
 	if err != nil {
 		return
 	}
 	defer d.Put(id)
 
7a7357da
 	return archive.ChangesSize(layerFs.Path(), changes), nil
52f4d09f
 }
 
1fffc7a8
 // GetMetadata returns custom driver information.
dfbb5520
 func (d *Driver) GetMetadata(id string) (map[string]string, error) {
bdabc76a
 	panicIfUsedByLcow()
dfbb5520
 	m := make(map[string]string)
 	m["dir"] = d.dir(id)
 	return m, nil
52f4d09f
 }
 
5649030e
 func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error {
 	t := tar.NewWriter(w)
 	for {
 		name, size, fileInfo, err := r.Next()
 		if err == io.EOF {
 			break
 		}
52f4d09f
 		if err != nil {
5649030e
 			return err
 		}
 		if fileInfo == nil {
 			// Write a whiteout file.
 			hdr := &tar.Header{
 				Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))),
 			}
 			err := t.WriteHeader(hdr)
 			if err != nil {
 				return err
 			}
 		} else {
 			err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
 			if err != nil {
 				return err
52f4d09f
 			}
 		}
5649030e
 	}
 	return t.Close()
 }
52f4d09f
 
5649030e
 // exportLayer generates an archive from a layer based on the given ID.
aa2cc187
 func (d *Driver) exportLayer(id string, parentLayerPaths []string) (io.ReadCloser, error) {
5649030e
 	archive, w := io.Pipe()
 	go func() {
b3bc5e0f
 		err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
 			r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths)
 			if err != nil {
 				return err
 			}
 
 			err = writeTarFromLayer(r, w)
 			cerr := r.Close()
 			if err == nil {
 				err = cerr
 			}
 			return err
 		})
5649030e
 		w.CloseWithError(err)
 	}()
52f4d09f
 
5649030e
 	return archive, nil
 }
 
9c79b0ef
 // writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and
 // writes it to a backup stream, and also saves any files that will be mutated
 // by the import layer process to a backup location.
 func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) {
 	var bcdBackup *os.File
 	var bcdBackupWriter *winio.BackupFileWriter
 	if backupPath, ok := mutatedFiles[hdr.Name]; ok {
 		bcdBackup, err = os.Create(filepath.Join(root, backupPath))
 		if err != nil {
 			return nil, err
 		}
 		defer func() {
 			cerr := bcdBackup.Close()
 			if err == nil {
 				err = cerr
 			}
 		}()
 
 		bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false)
 		defer func() {
 			cerr := bcdBackupWriter.Close()
 			if err == nil {
 				err = cerr
 			}
 		}()
 
 		buf.Reset(io.MultiWriter(w, bcdBackupWriter))
 	} else {
 		buf.Reset(w)
 	}
 
 	defer func() {
 		ferr := buf.Flush()
 		if err == nil {
 			err = ferr
 		}
 	}()
 
 	return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
 }
 
aa2cc187
 func writeLayerFromTar(r io.Reader, w hcsshim.LayerWriter, root string) (int64, error) {
5649030e
 	t := tar.NewReader(r)
 	hdr, err := t.Next()
 	totalSize := int64(0)
 	buf := bufio.NewWriter(nil)
 	for err == nil {
 		base := path.Base(hdr.Name)
 		if strings.HasPrefix(base, archive.WhiteoutPrefix) {
 			name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):])
 			err = w.Remove(filepath.FromSlash(name))
 			if err != nil {
 				return 0, err
 			}
 			hdr, err = t.Next()
cf7944bf
 		} else if hdr.Typeflag == tar.TypeLink {
 			err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
 			if err != nil {
 				return 0, err
 			}
 			hdr, err = t.Next()
5649030e
 		} else {
 			var (
 				name     string
 				size     int64
 				fileInfo *winio.FileBasicInfo
 			)
 			name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr)
 			if err != nil {
 				return 0, err
 			}
 			err = w.Add(filepath.FromSlash(name), fileInfo)
 			if err != nil {
 				return 0, err
 			}
9c79b0ef
 			hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root)
5649030e
 			totalSize += size
 		}
 	}
 	if err != io.EOF {
 		return 0, err
 	}
 	return totalSize, nil
52f4d09f
 }
 
1fffc7a8
 // importLayer adds a new layer to the tag and graph store based on the given data.
aa2cc187
 func (d *Driver) importLayer(id string, layerData io.Reader, parentLayerPaths []string) (size int64, err error) {
d9294719
 	if !noreexec {
 		cmd := reexec.Command(append([]string{"docker-windows-write-layer", d.info.HomeDir, id}, parentLayerPaths...)...)
 		output := bytes.NewBuffer(nil)
 		cmd.Stdin = layerData
 		cmd.Stdout = output
 		cmd.Stderr = output
 
 		if err = cmd.Start(); err != nil {
 			return
 		}
b3bc5e0f
 
d9294719
 		if err = cmd.Wait(); err != nil {
 			return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output)
 		}
b3bc5e0f
 
d9294719
 		return strconv.ParseInt(output.String(), 10, 64)
5649030e
 	}
d9294719
 	return writeLayer(layerData, d.info.HomeDir, id, parentLayerPaths...)
b3bc5e0f
 }
 
d9294719
 // writeLayerReexec is the re-exec entry point for writing a layer from a tar file
 func writeLayerReexec() {
 	size, err := writeLayer(os.Stdin, os.Args[1], os.Args[2], os.Args[3:]...)
 	if err != nil {
 		fmt.Fprint(os.Stderr, err)
 		os.Exit(1)
 	}
 	fmt.Fprint(os.Stdout, size)
 }
b3bc5e0f
 
d9294719
 // writeLayer writes a layer from a tar file.
 func writeLayer(layerData io.Reader, home string, id string, parentLayerPaths ...string) (int64, error) {
 	err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege})
 	if err != nil {
 		return 0, err
 	}
 	if noreexec {
 		defer func() {
 			if err := winio.DisableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil {
 				// This should never happen, but just in case when in debugging mode.
 				// See https://github.com/docker/docker/pull/28002#discussion_r86259241 for rationale.
 				panic("Failed to disabled process privileges while in non re-exec mode")
 			}
 		}()
 	}
b3bc5e0f
 
d9294719
 	info := hcsshim.DriverInfo{
 		Flavour: filterDriver,
 		HomeDir: home,
 	}
b3bc5e0f
 
d9294719
 	w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths)
 	if err != nil {
 		return 0, err
 	}
b3bc5e0f
 
d9294719
 	size, err := writeLayerFromTar(layerData, w, filepath.Join(home, id))
 	if err != nil {
 		return 0, err
 	}
b3bc5e0f
 
d9294719
 	err = w.Close()
5649030e
 	if err != nil {
d9294719
 		return 0, err
52f4d09f
 	}
d9294719
 
 	return size, nil
52f4d09f
 }
dfbb5520
 
1fffc7a8
 // resolveID computes the layerID information based on the given id.
 func (d *Driver) resolveID(id string) (string, error) {
 	content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID"))
dfbb5520
 	if os.IsNotExist(err) {
 		return id, nil
 	} else if err != nil {
 		return "", err
 	}
 	return string(content), nil
 }
 
1fffc7a8
 // setID stores the layerId in disk.
 func (d *Driver) setID(id, altID string) error {
3a425180
 	return ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600)
dfbb5520
 }
 
1fffc7a8
 // getLayerChain returns the layer chain information.
dfbb5520
 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
 }
 
1fffc7a8
 // setLayerChain stores the layer chain information in disk.
dfbb5520
 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
 }
4352da78
 
5649030e
 type fileGetCloserWithBackupPrivileges struct {
 	path string
 }
 
 func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) {
9c79b0ef
 	if backupPath, ok := mutatedFiles[filename]; ok {
 		return os.Open(filepath.Join(fg.path, backupPath))
 	}
 
5649030e
 	var f *os.File
 	// Open the file while holding the Windows backup privilege. This ensures that the
 	// file can be opened even if the caller does not actually have access to it according
c98e77c7
 	// to the security descriptor. Also use sequential file access to avoid depleting the
 	// standby list - Microsoft VSO Bug Tracker #9900466
5649030e
 	err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
cf7944bf
 		path := longpath.AddPrefix(filepath.Join(fg.path, filename))
069fdc8a
 		p, err := windows.UTF16FromString(path)
5649030e
 		if err != nil {
 			return err
 		}
c98e77c7
 		const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
069fdc8a
 		h, err := windows.CreateFile(&p[0], windows.GENERIC_READ, windows.FILE_SHARE_READ, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS|fileFlagSequentialScan, 0)
5649030e
 		if err != nil {
 			return &os.PathError{Op: "open", Path: path, Err: err}
 		}
 		f = os.NewFile(uintptr(h), path)
 		return nil
 	})
 	return f, err
 }
 
 func (fg *fileGetCloserWithBackupPrivileges) Close() error {
 	return nil
 }
 
58bec40d
 // DiffGetter returns a FileGetCloser that can read files from the directory that
 // contains files for the layer differences. Used for direct access for tar-split.
5649030e
 func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
bdabc76a
 	panicIfUsedByLcow()
5649030e
 	id, err := d.resolveID(id)
4352da78
 	if err != nil {
5649030e
 		return nil, err
4352da78
 	}
 
5649030e
 	return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil
4352da78
 }
7e5ee6d1
 
 type storageOptions struct {
 	size uint64
 }
 
 func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) {
 	options := storageOptions{}
 
 	// Read size to change the block device size per container.
 	for key, val := range storageOpt {
 		key := strings.ToLower(key)
 		switch key {
 		case "size":
 			size, err := units.RAMInBytes(val)
 			if err != nil {
 				return nil, err
 			}
 			options.size = uint64(size)
 		default:
 			return nil, fmt.Errorf("Unknown storage option: %s", key)
 		}
 	}
 	return &options, nil
 }