github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/graphdriver/windows/windows.go (about)

     1  //+build windows
     2  
     3  package windows // import "github.com/demonoid81/moby/daemon/graphdriver/windows"
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"syscall"
    19  	"time"
    20  	"unsafe"
    21  
    22  	winio "github.com/Microsoft/go-winio"
    23  	"github.com/Microsoft/go-winio/archive/tar"
    24  	"github.com/Microsoft/go-winio/backuptar"
    25  	"github.com/Microsoft/go-winio/vhd"
    26  	"github.com/Microsoft/hcsshim"
    27  	"github.com/Microsoft/hcsshim/osversion"
    28  	"github.com/demonoid81/moby/daemon/graphdriver"
    29  	"github.com/demonoid81/moby/pkg/archive"
    30  	"github.com/demonoid81/moby/pkg/containerfs"
    31  	"github.com/demonoid81/moby/pkg/idtools"
    32  	"github.com/demonoid81/moby/pkg/ioutils"
    33  	"github.com/demonoid81/moby/pkg/longpath"
    34  	"github.com/demonoid81/moby/pkg/reexec"
    35  	units "github.com/docker/go-units"
    36  	"github.com/pkg/errors"
    37  	"github.com/sirupsen/logrus"
    38  	"golang.org/x/sys/windows"
    39  )
    40  
    41  // filterDriver is an HCSShim driver type for the Windows Filter driver.
    42  const filterDriver = 1
    43  
    44  var (
    45  	// mutatedFiles is a list of files that are mutated by the import process
    46  	// and must be backed up and restored.
    47  	mutatedFiles = map[string]string{
    48  		"UtilityVM/Files/EFI/Microsoft/Boot/BCD":      "bcd.bak",
    49  		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG":  "bcd.log.bak",
    50  		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak",
    51  		"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak",
    52  	}
    53  	noreexec = false
    54  )
    55  
    56  // init registers the windows graph drivers to the register.
    57  func init() {
    58  	graphdriver.Register("windowsfilter", InitFilter)
    59  	// DOCKER_WINDOWSFILTER_NOREEXEC allows for inline processing which makes
    60  	// debugging issues in the re-exec codepath significantly easier.
    61  	if os.Getenv("DOCKER_WINDOWSFILTER_NOREEXEC") != "" {
    62  		logrus.Warnf("WindowsGraphDriver is set to not re-exec. This is intended for debugging purposes only.")
    63  		noreexec = true
    64  	} else {
    65  		reexec.Register("docker-windows-write-layer", writeLayerReexec)
    66  	}
    67  }
    68  
    69  type checker struct {
    70  }
    71  
    72  func (c *checker) IsMounted(path string) bool {
    73  	return false
    74  }
    75  
    76  // Driver represents a windows graph driver.
    77  type Driver struct {
    78  	// info stores the shim driver information
    79  	info hcsshim.DriverInfo
    80  	ctr  *graphdriver.RefCounter
    81  	// it is safe for windows to use a cache here because it does not support
    82  	// restoring containers when the daemon dies.
    83  	cacheMu sync.Mutex
    84  	cache   map[string]string
    85  }
    86  
    87  // InitFilter returns a new Windows storage filter driver.
    88  func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
    89  	logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
    90  
    91  	fsType, err := getFileSystemType(string(home[0]))
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	if strings.ToLower(fsType) == "refs" {
    96  		return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
    97  	}
    98  
    99  	if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
   100  		return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
   101  	}
   102  
   103  	d := &Driver{
   104  		info: hcsshim.DriverInfo{
   105  			HomeDir: home,
   106  			Flavour: filterDriver,
   107  		},
   108  		cache: make(map[string]string),
   109  		ctr:   graphdriver.NewRefCounter(&checker{}),
   110  	}
   111  	return d, nil
   112  }
   113  
   114  // win32FromHresult is a helper function to get the win32 error code from an HRESULT
   115  func win32FromHresult(hr uintptr) uintptr {
   116  	if hr&0x1fff0000 == 0x00070000 {
   117  		return hr & 0xffff
   118  	}
   119  	return hr
   120  }
   121  
   122  // getFileSystemType obtains the type of a file system through GetVolumeInformation
   123  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx
   124  func getFileSystemType(drive string) (fsType string, hr error) {
   125  	var (
   126  		modkernel32              = windows.NewLazySystemDLL("kernel32.dll")
   127  		procGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW")
   128  		buf                      = make([]uint16, 255)
   129  		size                     = windows.MAX_PATH + 1
   130  	)
   131  	if len(drive) != 1 {
   132  		hr = errors.New("getFileSystemType must be called with a drive letter")
   133  		return
   134  	}
   135  	drive += `:\`
   136  	n := uintptr(unsafe.Pointer(nil))
   137  	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)
   138  	if int32(r0) < 0 {
   139  		hr = syscall.Errno(win32FromHresult(r0))
   140  	}
   141  	fsType = windows.UTF16ToString(buf)
   142  	return
   143  }
   144  
   145  // String returns the string representation of a driver. This should match
   146  // the name the graph driver has been registered with.
   147  func (d *Driver) String() string {
   148  	return "windowsfilter"
   149  }
   150  
   151  // Status returns the status of the driver.
   152  func (d *Driver) Status() [][2]string {
   153  	return [][2]string{
   154  		{"Windows", ""},
   155  	}
   156  }
   157  
   158  // Exists returns true if the given id is registered with this driver.
   159  func (d *Driver) Exists(id string) bool {
   160  	rID, err := d.resolveID(id)
   161  	if err != nil {
   162  		return false
   163  	}
   164  	result, err := hcsshim.LayerExists(d.info, rID)
   165  	if err != nil {
   166  		return false
   167  	}
   168  	return result
   169  }
   170  
   171  // CreateReadWrite creates a layer that is writable for use as a container
   172  // file system.
   173  func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
   174  	if opts != nil {
   175  		return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
   176  	}
   177  	return d.create(id, parent, "", false, nil)
   178  }
   179  
   180  // Create creates a new read-only layer with the given id.
   181  func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
   182  	if opts != nil {
   183  		return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
   184  	}
   185  	return d.create(id, parent, "", true, nil)
   186  }
   187  
   188  func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
   189  	rPId, err := d.resolveID(parent)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	parentChain, err := d.getLayerChain(rPId)
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	var layerChain []string
   200  
   201  	if rPId != "" {
   202  		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
   203  		if err != nil {
   204  			return err
   205  		}
   206  		if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil {
   207  			// This is a legitimate parent layer (not the empty "-init" layer),
   208  			// so include it in the layer chain.
   209  			layerChain = []string{parentPath}
   210  		}
   211  	}
   212  
   213  	layerChain = append(layerChain, parentChain...)
   214  
   215  	if readOnly {
   216  		if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
   217  			return err
   218  		}
   219  	} else {
   220  		var parentPath string
   221  		if len(layerChain) != 0 {
   222  			parentPath = layerChain[0]
   223  		}
   224  
   225  		if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil {
   226  			return err
   227  		}
   228  
   229  		storageOptions, err := parseStorageOpt(storageOpt)
   230  		if err != nil {
   231  			return fmt.Errorf("Failed to parse storage options - %s", err)
   232  		}
   233  
   234  		if storageOptions.size != 0 {
   235  			if err := hcsshim.ExpandSandboxSize(d.info, id, storageOptions.size); err != nil {
   236  				return err
   237  			}
   238  		}
   239  	}
   240  
   241  	if _, err := os.Lstat(d.dir(parent)); err != nil {
   242  		if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
   243  			logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
   244  		}
   245  		return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err)
   246  	}
   247  
   248  	if err := d.setLayerChain(id, layerChain); err != nil {
   249  		if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
   250  			logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
   251  		}
   252  		return err
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  // dir returns the absolute path to the layer.
   259  func (d *Driver) dir(id string) string {
   260  	return filepath.Join(d.info.HomeDir, filepath.Base(id))
   261  }
   262  
   263  // Remove unmounts and removes the dir information.
   264  func (d *Driver) Remove(id string) error {
   265  	rID, err := d.resolveID(id)
   266  	if err != nil {
   267  		return err
   268  	}
   269  
   270  	// This retry loop is due to a bug in Windows (Internal bug #9432268)
   271  	// if GetContainers fails with ErrVmcomputeOperationInvalidState
   272  	// it is a transient error. Retry until it succeeds.
   273  	var computeSystems []hcsshim.ContainerProperties
   274  	retryCount := 0
   275  	for {
   276  		// Get and terminate any template VMs that are currently using the layer.
   277  		// Note: It is unfortunate that we end up in the graphdrivers Remove() call
   278  		// for both containers and images, but the logic for template VMs is only
   279  		// needed for images - specifically we are looking to see if a base layer
   280  		// is in use by a template VM as a result of having started a Hyper-V
   281  		// container at some point.
   282  		//
   283  		// We have a retry loop for ErrVmcomputeOperationInvalidState and
   284  		// ErrVmcomputeOperationAccessIsDenied as there is a race condition
   285  		// in RS1 and RS2 building during enumeration when a silo is going away
   286  		// for example under it, in HCS. AccessIsDenied added to fix 30278.
   287  		//
   288  		// TODO: For RS3, we can remove the retries. Also consider
   289  		// using platform APIs (if available) to get this more succinctly. Also
   290  		// consider enhancing the Remove() interface to have context of why
   291  		// the remove is being called - that could improve efficiency by not
   292  		// enumerating compute systems during a remove of a container as it's
   293  		// not required.
   294  		computeSystems, err = hcsshim.GetContainers(hcsshim.ComputeSystemQuery{})
   295  		if err != nil {
   296  			if osversion.Build() >= osversion.RS3 {
   297  				return err
   298  			}
   299  			if (err == hcsshim.ErrVmcomputeOperationInvalidState) || (err == hcsshim.ErrVmcomputeOperationAccessIsDenied) {
   300  				if retryCount >= 500 {
   301  					break
   302  				}
   303  				retryCount++
   304  				time.Sleep(10 * time.Millisecond)
   305  				continue
   306  			}
   307  			return err
   308  		}
   309  		break
   310  	}
   311  
   312  	for _, computeSystem := range computeSystems {
   313  		if strings.Contains(computeSystem.RuntimeImagePath, id) && computeSystem.IsRuntimeTemplate {
   314  			container, err := hcsshim.OpenContainer(computeSystem.ID)
   315  			if err != nil {
   316  				return err
   317  			}
   318  			defer container.Close()
   319  			err = container.Terminate()
   320  			if hcsshim.IsPending(err) {
   321  				err = container.Wait()
   322  			} else if hcsshim.IsAlreadyStopped(err) {
   323  				err = nil
   324  			}
   325  
   326  			if err != nil {
   327  				return err
   328  			}
   329  		}
   330  	}
   331  
   332  	layerPath := filepath.Join(d.info.HomeDir, rID)
   333  	tmpID := fmt.Sprintf("%s-removing", rID)
   334  	tmpLayerPath := filepath.Join(d.info.HomeDir, tmpID)
   335  	if err := os.Rename(layerPath, tmpLayerPath); err != nil && !os.IsNotExist(err) {
   336  		if !os.IsPermission(err) {
   337  			return err
   338  		}
   339  		// If permission denied, it's possible that the scratch is still mounted, an
   340  		// artifact after a hard daemon crash for example. Worth a shot to try detaching it
   341  		// before retrying the rename.
   342  		sandbox := filepath.Join(layerPath, "sandbox.vhdx")
   343  		if _, statErr := os.Stat(sandbox); statErr == nil {
   344  			if detachErr := vhd.DetachVhd(sandbox); detachErr != nil {
   345  				return errors.Wrapf(err, "failed to detach VHD: %s", detachErr)
   346  			}
   347  			if renameErr := os.Rename(layerPath, tmpLayerPath); renameErr != nil && !os.IsNotExist(renameErr) {
   348  				return errors.Wrapf(err, "second rename attempt following detach failed: %s", renameErr)
   349  			}
   350  		}
   351  	}
   352  	if err := hcsshim.DestroyLayer(d.info, tmpID); err != nil {
   353  		logrus.Errorf("Failed to DestroyLayer %s: %s", id, err)
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  // GetLayerPath gets the layer path on host
   360  func (d *Driver) GetLayerPath(id string) (string, error) {
   361  	return d.dir(id), nil
   362  }
   363  
   364  // Get returns the rootfs path for the id. This will mount the dir at its given path.
   365  func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
   366  	logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
   367  	var dir string
   368  
   369  	rID, err := d.resolveID(id)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	if count := d.ctr.Increment(rID); count > 1 {
   374  		return containerfs.NewLocalContainerFS(d.cache[rID]), nil
   375  	}
   376  
   377  	// Getting the layer paths must be done outside of the lock.
   378  	layerChain, err := d.getLayerChain(rID)
   379  	if err != nil {
   380  		d.ctr.Decrement(rID)
   381  		return nil, err
   382  	}
   383  
   384  	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
   385  		d.ctr.Decrement(rID)
   386  		return nil, err
   387  	}
   388  	if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
   389  		d.ctr.Decrement(rID)
   390  		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
   391  			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
   392  		}
   393  		return nil, err
   394  	}
   395  
   396  	mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
   397  	if err != nil {
   398  		d.ctr.Decrement(rID)
   399  		if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
   400  			logrus.Warnf("Failed to Unprepare %s: %s", id, err)
   401  		}
   402  		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
   403  			logrus.Warnf("Failed to Deactivate %s: %s", id, err)
   404  		}
   405  		return nil, err
   406  	}
   407  	d.cacheMu.Lock()
   408  	d.cache[rID] = mountPath
   409  	d.cacheMu.Unlock()
   410  
   411  	// If the layer has a mount path, use that. Otherwise, use the
   412  	// folder path.
   413  	if mountPath != "" {
   414  		dir = mountPath
   415  	} else {
   416  		dir = d.dir(id)
   417  	}
   418  
   419  	return containerfs.NewLocalContainerFS(dir), nil
   420  }
   421  
   422  // Put adds a new layer to the driver.
   423  func (d *Driver) Put(id string) error {
   424  	logrus.Debugf("WindowsGraphDriver Put() id %s", id)
   425  
   426  	rID, err := d.resolveID(id)
   427  	if err != nil {
   428  		return err
   429  	}
   430  	if count := d.ctr.Decrement(rID); count > 0 {
   431  		return nil
   432  	}
   433  	d.cacheMu.Lock()
   434  	_, exists := d.cache[rID]
   435  	delete(d.cache, rID)
   436  	d.cacheMu.Unlock()
   437  
   438  	// If the cache was not populated, then the layer was left unprepared and deactivated
   439  	if !exists {
   440  		return nil
   441  	}
   442  
   443  	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
   444  		return err
   445  	}
   446  	return hcsshim.DeactivateLayer(d.info, rID)
   447  }
   448  
   449  // Cleanup ensures the information the driver stores is properly removed.
   450  // We use this opportunity to cleanup any -removing folders which may be
   451  // still left if the daemon was killed while it was removing a layer.
   452  func (d *Driver) Cleanup() error {
   453  	items, err := ioutil.ReadDir(d.info.HomeDir)
   454  	if err != nil {
   455  		if os.IsNotExist(err) {
   456  			return nil
   457  		}
   458  		return err
   459  	}
   460  
   461  	// Note we don't return an error below - it's possible the files
   462  	// are locked. However, next time around after the daemon exits,
   463  	// we likely will be able to cleanup successfully. Instead we log
   464  	// warnings if there are errors.
   465  	for _, item := range items {
   466  		if item.IsDir() && strings.HasSuffix(item.Name(), "-removing") {
   467  			if err := hcsshim.DestroyLayer(d.info, item.Name()); err != nil {
   468  				logrus.Warnf("Failed to cleanup %s: %s", item.Name(), err)
   469  			} else {
   470  				logrus.Infof("Cleaned up %s", item.Name())
   471  			}
   472  		}
   473  	}
   474  
   475  	return nil
   476  }
   477  
   478  // Diff produces an archive of the changes between the specified
   479  // layer and its parent layer which may be "".
   480  // The layer should be mounted when calling this function
   481  func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
   482  	rID, err := d.resolveID(id)
   483  	if err != nil {
   484  		return
   485  	}
   486  
   487  	layerChain, err := d.getLayerChain(rID)
   488  	if err != nil {
   489  		return
   490  	}
   491  
   492  	// this is assuming that the layer is unmounted
   493  	if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
   494  		return nil, err
   495  	}
   496  	prepare := func() {
   497  		if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
   498  			logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
   499  		}
   500  	}
   501  
   502  	arch, err := d.exportLayer(rID, layerChain)
   503  	if err != nil {
   504  		prepare()
   505  		return
   506  	}
   507  	return ioutils.NewReadCloserWrapper(arch, func() error {
   508  		err := arch.Close()
   509  		prepare()
   510  		return err
   511  	}), nil
   512  }
   513  
   514  // Changes produces a list of changes between the specified layer
   515  // and its parent layer. If parent is "", then all changes will be ADD changes.
   516  // The layer should not be mounted when calling this function.
   517  func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
   518  	rID, err := d.resolveID(id)
   519  	if err != nil {
   520  		return nil, err
   521  	}
   522  	parentChain, err := d.getLayerChain(rID)
   523  	if err != nil {
   524  		return nil, err
   525  	}
   526  
   527  	if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
   528  		return nil, err
   529  	}
   530  	defer func() {
   531  		if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
   532  			logrus.Errorf("changes() failed to DeactivateLayer %s %s: %s", id, rID, err2)
   533  		}
   534  	}()
   535  
   536  	var changes []archive.Change
   537  	err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
   538  		r, err := hcsshim.NewLayerReader(d.info, id, parentChain)
   539  		if err != nil {
   540  			return err
   541  		}
   542  		defer r.Close()
   543  
   544  		for {
   545  			name, _, fileInfo, err := r.Next()
   546  			if err == io.EOF {
   547  				return nil
   548  			}
   549  			if err != nil {
   550  				return err
   551  			}
   552  			name = filepath.ToSlash(name)
   553  			if fileInfo == nil {
   554  				changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete})
   555  			} else {
   556  				// Currently there is no way to tell between an add and a modify.
   557  				changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify})
   558  			}
   559  		}
   560  	})
   561  	if err != nil {
   562  		return nil, err
   563  	}
   564  
   565  	return changes, nil
   566  }
   567  
   568  // ApplyDiff extracts the changeset from the given diff into the
   569  // layer with the specified id and parent, returning the size of the
   570  // new layer in bytes.
   571  // The layer should not be mounted when calling this function
   572  func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
   573  	var layerChain []string
   574  	if parent != "" {
   575  		rPId, err := d.resolveID(parent)
   576  		if err != nil {
   577  			return 0, err
   578  		}
   579  		parentChain, err := d.getLayerChain(rPId)
   580  		if err != nil {
   581  			return 0, err
   582  		}
   583  		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
   584  		if err != nil {
   585  			return 0, err
   586  		}
   587  		layerChain = append(layerChain, parentPath)
   588  		layerChain = append(layerChain, parentChain...)
   589  	}
   590  
   591  	size, err := d.importLayer(id, diff, layerChain)
   592  	if err != nil {
   593  		return 0, err
   594  	}
   595  
   596  	if err = d.setLayerChain(id, layerChain); err != nil {
   597  		return 0, err
   598  	}
   599  
   600  	return size, nil
   601  }
   602  
   603  // DiffSize calculates the changes between the specified layer
   604  // and its parent and returns the size in bytes of the changes
   605  // relative to its base filesystem directory.
   606  func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
   607  	rPId, err := d.resolveID(parent)
   608  	if err != nil {
   609  		return
   610  	}
   611  
   612  	changes, err := d.Changes(id, rPId)
   613  	if err != nil {
   614  		return
   615  	}
   616  
   617  	layerFs, err := d.Get(id, "")
   618  	if err != nil {
   619  		return
   620  	}
   621  	defer d.Put(id)
   622  
   623  	return archive.ChangesSize(layerFs.Path(), changes), nil
   624  }
   625  
   626  // GetMetadata returns custom driver information.
   627  func (d *Driver) GetMetadata(id string) (map[string]string, error) {
   628  	m := make(map[string]string)
   629  	m["dir"] = d.dir(id)
   630  	return m, nil
   631  }
   632  
   633  func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error {
   634  	t := tar.NewWriter(w)
   635  	for {
   636  		name, size, fileInfo, err := r.Next()
   637  		if err == io.EOF {
   638  			break
   639  		}
   640  		if err != nil {
   641  			return err
   642  		}
   643  		if fileInfo == nil {
   644  			// Write a whiteout file.
   645  			hdr := &tar.Header{
   646  				Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))),
   647  			}
   648  			err := t.WriteHeader(hdr)
   649  			if err != nil {
   650  				return err
   651  			}
   652  		} else {
   653  			err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
   654  			if err != nil {
   655  				return err
   656  			}
   657  		}
   658  	}
   659  	return t.Close()
   660  }
   661  
   662  // exportLayer generates an archive from a layer based on the given ID.
   663  func (d *Driver) exportLayer(id string, parentLayerPaths []string) (io.ReadCloser, error) {
   664  	archive, w := io.Pipe()
   665  	go func() {
   666  		err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
   667  			r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths)
   668  			if err != nil {
   669  				return err
   670  			}
   671  
   672  			err = writeTarFromLayer(r, w)
   673  			cerr := r.Close()
   674  			if err == nil {
   675  				err = cerr
   676  			}
   677  			return err
   678  		})
   679  		w.CloseWithError(err)
   680  	}()
   681  
   682  	return archive, nil
   683  }
   684  
   685  // writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and
   686  // writes it to a backup stream, and also saves any files that will be mutated
   687  // by the import layer process to a backup location.
   688  func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) {
   689  	var bcdBackup *os.File
   690  	var bcdBackupWriter *winio.BackupFileWriter
   691  	if backupPath, ok := mutatedFiles[hdr.Name]; ok {
   692  		bcdBackup, err = os.Create(filepath.Join(root, backupPath))
   693  		if err != nil {
   694  			return nil, err
   695  		}
   696  		defer func() {
   697  			cerr := bcdBackup.Close()
   698  			if err == nil {
   699  				err = cerr
   700  			}
   701  		}()
   702  
   703  		bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false)
   704  		defer func() {
   705  			cerr := bcdBackupWriter.Close()
   706  			if err == nil {
   707  				err = cerr
   708  			}
   709  		}()
   710  
   711  		buf.Reset(io.MultiWriter(w, bcdBackupWriter))
   712  	} else {
   713  		buf.Reset(w)
   714  	}
   715  
   716  	defer func() {
   717  		ferr := buf.Flush()
   718  		if err == nil {
   719  			err = ferr
   720  		}
   721  	}()
   722  
   723  	return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
   724  }
   725  
   726  func writeLayerFromTar(r io.Reader, w hcsshim.LayerWriter, root string) (int64, error) {
   727  	t := tar.NewReader(r)
   728  	hdr, err := t.Next()
   729  	totalSize := int64(0)
   730  	buf := bufio.NewWriter(nil)
   731  	for err == nil {
   732  		base := path.Base(hdr.Name)
   733  		if strings.HasPrefix(base, archive.WhiteoutPrefix) {
   734  			name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):])
   735  			err = w.Remove(filepath.FromSlash(name))
   736  			if err != nil {
   737  				return 0, err
   738  			}
   739  			hdr, err = t.Next()
   740  		} else if hdr.Typeflag == tar.TypeLink {
   741  			err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
   742  			if err != nil {
   743  				return 0, err
   744  			}
   745  			hdr, err = t.Next()
   746  		} else {
   747  			var (
   748  				name     string
   749  				size     int64
   750  				fileInfo *winio.FileBasicInfo
   751  			)
   752  			name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr)
   753  			if err != nil {
   754  				return 0, err
   755  			}
   756  			err = w.Add(filepath.FromSlash(name), fileInfo)
   757  			if err != nil {
   758  				return 0, err
   759  			}
   760  			hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root)
   761  			totalSize += size
   762  		}
   763  	}
   764  	if err != io.EOF {
   765  		return 0, err
   766  	}
   767  	return totalSize, nil
   768  }
   769  
   770  // importLayer adds a new layer to the tag and graph store based on the given data.
   771  func (d *Driver) importLayer(id string, layerData io.Reader, parentLayerPaths []string) (size int64, err error) {
   772  	if !noreexec {
   773  		cmd := reexec.Command(append([]string{"docker-windows-write-layer", d.info.HomeDir, id}, parentLayerPaths...)...)
   774  		output := bytes.NewBuffer(nil)
   775  		cmd.Stdin = layerData
   776  		cmd.Stdout = output
   777  		cmd.Stderr = output
   778  
   779  		if err = cmd.Start(); err != nil {
   780  			return
   781  		}
   782  
   783  		if err = cmd.Wait(); err != nil {
   784  			return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output)
   785  		}
   786  
   787  		return strconv.ParseInt(output.String(), 10, 64)
   788  	}
   789  	return writeLayer(layerData, d.info.HomeDir, id, parentLayerPaths...)
   790  }
   791  
   792  // writeLayerReexec is the re-exec entry point for writing a layer from a tar file
   793  func writeLayerReexec() {
   794  	size, err := writeLayer(os.Stdin, os.Args[1], os.Args[2], os.Args[3:]...)
   795  	if err != nil {
   796  		fmt.Fprint(os.Stderr, err)
   797  		os.Exit(1)
   798  	}
   799  	fmt.Fprint(os.Stdout, size)
   800  }
   801  
   802  // writeLayer writes a layer from a tar file.
   803  func writeLayer(layerData io.Reader, home string, id string, parentLayerPaths ...string) (size int64, retErr error) {
   804  	err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege})
   805  	if err != nil {
   806  		return 0, err
   807  	}
   808  	if noreexec {
   809  		defer func() {
   810  			if err := winio.DisableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil {
   811  				// This should never happen, but just in case when in debugging mode.
   812  				// See https://github.com/demonoid81/moby/pull/28002#discussion_r86259241 for rationale.
   813  				panic("Failed to disabled process privileges while in non re-exec mode")
   814  			}
   815  		}()
   816  	}
   817  
   818  	info := hcsshim.DriverInfo{
   819  		Flavour: filterDriver,
   820  		HomeDir: home,
   821  	}
   822  
   823  	w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths)
   824  	if err != nil {
   825  		return 0, err
   826  	}
   827  
   828  	defer func() {
   829  		if err := w.Close(); err != nil {
   830  			// This error should not be discarded as a failure here
   831  			// could result in an invalid layer on disk
   832  			if retErr == nil {
   833  				retErr = err
   834  			}
   835  		}
   836  	}()
   837  
   838  	return writeLayerFromTar(layerData, w, filepath.Join(home, id))
   839  }
   840  
   841  // resolveID computes the layerID information based on the given id.
   842  func (d *Driver) resolveID(id string) (string, error) {
   843  	content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID"))
   844  	if os.IsNotExist(err) {
   845  		return id, nil
   846  	} else if err != nil {
   847  		return "", err
   848  	}
   849  	return string(content), nil
   850  }
   851  
   852  // setID stores the layerId in disk.
   853  func (d *Driver) setID(id, altID string) error {
   854  	return ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600)
   855  }
   856  
   857  // getLayerChain returns the layer chain information.
   858  func (d *Driver) getLayerChain(id string) ([]string, error) {
   859  	jPath := filepath.Join(d.dir(id), "layerchain.json")
   860  	content, err := ioutil.ReadFile(jPath)
   861  	if os.IsNotExist(err) {
   862  		return nil, nil
   863  	} else if err != nil {
   864  		return nil, fmt.Errorf("Unable to read layerchain file - %s", err)
   865  	}
   866  
   867  	var layerChain []string
   868  	err = json.Unmarshal(content, &layerChain)
   869  	if err != nil {
   870  		return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err)
   871  	}
   872  
   873  	return layerChain, nil
   874  }
   875  
   876  // setLayerChain stores the layer chain information in disk.
   877  func (d *Driver) setLayerChain(id string, chain []string) error {
   878  	content, err := json.Marshal(&chain)
   879  	if err != nil {
   880  		return fmt.Errorf("Failed to marshall layerchain json - %s", err)
   881  	}
   882  
   883  	jPath := filepath.Join(d.dir(id), "layerchain.json")
   884  	err = ioutil.WriteFile(jPath, content, 0600)
   885  	if err != nil {
   886  		return fmt.Errorf("Unable to write layerchain file - %s", err)
   887  	}
   888  
   889  	return nil
   890  }
   891  
   892  type fileGetCloserWithBackupPrivileges struct {
   893  	path string
   894  }
   895  
   896  func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) {
   897  	if backupPath, ok := mutatedFiles[filename]; ok {
   898  		return os.Open(filepath.Join(fg.path, backupPath))
   899  	}
   900  
   901  	var f *os.File
   902  	// Open the file while holding the Windows backup privilege. This ensures that the
   903  	// file can be opened even if the caller does not actually have access to it according
   904  	// to the security descriptor. Also use sequential file access to avoid depleting the
   905  	// standby list - Microsoft VSO Bug Tracker #9900466
   906  	err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
   907  		path := longpath.AddPrefix(filepath.Join(fg.path, filename))
   908  		p, err := windows.UTF16FromString(path)
   909  		if err != nil {
   910  			return err
   911  		}
   912  		const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
   913  		h, err := windows.CreateFile(&p[0], windows.GENERIC_READ, windows.FILE_SHARE_READ, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS|fileFlagSequentialScan, 0)
   914  		if err != nil {
   915  			return &os.PathError{Op: "open", Path: path, Err: err}
   916  		}
   917  		f = os.NewFile(uintptr(h), path)
   918  		return nil
   919  	})
   920  	return f, err
   921  }
   922  
   923  func (fg *fileGetCloserWithBackupPrivileges) Close() error {
   924  	return nil
   925  }
   926  
   927  // DiffGetter returns a FileGetCloser that can read files from the directory that
   928  // contains files for the layer differences. Used for direct access for tar-split.
   929  func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
   930  	id, err := d.resolveID(id)
   931  	if err != nil {
   932  		return nil, err
   933  	}
   934  
   935  	return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil
   936  }
   937  
   938  type storageOptions struct {
   939  	size uint64
   940  }
   941  
   942  func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) {
   943  	options := storageOptions{}
   944  
   945  	// Read size to change the block device size per container.
   946  	for key, val := range storageOpt {
   947  		key := strings.ToLower(key)
   948  		switch key {
   949  		case "size":
   950  			size, err := units.RAMInBytes(val)
   951  			if err != nil {
   952  				return nil, err
   953  			}
   954  			options.size = uint64(size)
   955  		}
   956  	}
   957  	return &options, nil
   958  }