Merge pull request #36935 from cpuguy83/volume_tests_no_root

Fix issues with running volume tests as non-root.
diff --git a/daemon/graphdriver/lcow/lcow.go b/daemon/graphdriver/lcow/lcow.go
index f5a2ee2..9cc0620 100644
--- a/daemon/graphdriver/lcow/lcow.go
+++ b/daemon/graphdriver/lcow/lcow.go
@@ -56,11 +56,13 @@
 package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow"
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -964,3 +966,87 @@
 func unionMountName(disks []hcsshim.MappedVirtualDisk) string {
 	return fmt.Sprintf("%s-mount", disks[0].ContainerPath)
 }
+
+type nopCloser struct {
+	io.Reader
+}
+
+func (nopCloser) Close() error {
+	return nil
+}
+
+type fileGetCloserFromSVM struct {
+	id  string
+	svm *serviceVM
+	mvd *hcsshim.MappedVirtualDisk
+	d   *Driver
+}
+
+func (fgc *fileGetCloserFromSVM) Close() error {
+	if fgc.svm != nil {
+		if fgc.mvd != nil {
+			if err := fgc.svm.hotRemoveVHDs(*fgc.mvd); err != nil {
+				// We just log this as we're going to tear down the SVM imminently unless in global mode
+				logrus.Errorf("failed to remove mvd %s: %s", fgc.mvd.ContainerPath, err)
+			}
+		}
+	}
+	if fgc.d != nil && fgc.svm != nil && fgc.id != "" {
+		if err := fgc.d.terminateServiceVM(fgc.id, fmt.Sprintf("diffgetter %s", fgc.id), false); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (fgc *fileGetCloserFromSVM) Get(filename string) (io.ReadCloser, error) {
+	errOut := &bytes.Buffer{}
+	outOut := &bytes.Buffer{}
+	file := path.Join(fgc.mvd.ContainerPath, filename)
+	if err := fgc.svm.runProcess(fmt.Sprintf("cat %s", file), nil, outOut, errOut); err != nil {
+		logrus.Debugf("cat %s failed: %s", file, errOut.String())
+		return nil, err
+	}
+	return nopCloser{bytes.NewReader(outOut.Bytes())}, nil
+}
+
+// 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.
+func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
+	title := fmt.Sprintf("lcowdriver: diffgetter: %s", id)
+	logrus.Debugf(title)
+
+	ld, err := getLayerDetails(d.dir(id))
+	if err != nil {
+		logrus.Debugf("%s: failed to get vhdx information of %s: %s", title, d.dir(id), err)
+		return nil, err
+	}
+
+	// Start the SVM with a mapped virtual disk. Note that if the SVM is
+	// already running and we are in global mode, this will be hot-added.
+	mvd := hcsshim.MappedVirtualDisk{
+		HostPath:          ld.filename,
+		ContainerPath:     hostToGuest(ld.filename),
+		CreateInUtilityVM: true,
+		ReadOnly:          true,
+	}
+
+	logrus.Debugf("%s: starting service VM", title)
+	svm, err := d.startServiceVMIfNotRunning(id, []hcsshim.MappedVirtualDisk{mvd}, fmt.Sprintf("diffgetter %s", id))
+	if err != nil {
+		return nil, err
+	}
+
+	logrus.Debugf("%s: waiting for svm to finish booting", title)
+	err = svm.getStartError()
+	if err != nil {
+		d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false)
+		return nil, fmt.Errorf("%s: svm failed to boot: %s", title, err)
+	}
+
+	return &fileGetCloserFromSVM{
+		id:  id,
+		svm: svm,
+		mvd: &mvd,
+		d:   d}, nil
+}
diff --git a/image/tarexport/save.go b/image/tarexport/save.go
index 1f32fad..4e734b3 100644
--- a/image/tarexport/save.go
+++ b/image/tarexport/save.go
@@ -6,6 +6,7 @@
 	"io"
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"runtime"
 	"time"
@@ -158,6 +159,9 @@
 	if os == "" {
 		os = runtime.GOOS
 	}
+	if !system.IsOSSupported(os) {
+		return fmt.Errorf("os %q is not supported", os)
+	}
 	layer, err := l.lss[os].Get(topLayerID)
 	if err != nil {
 		return err
@@ -216,7 +220,11 @@
 		}
 
 		for _, l := range imageDescr.layers {
-			layers = append(layers, filepath.Join(l, legacyLayerFileName))
+			// IMPORTANT: We use path, not filepath here to ensure the layers
+			// in the manifest use Unix-style forward-slashes. Otherwise, a
+			// Linux image saved from LCOW won't be able to be imported on
+			// LCOL.
+			layers = append(layers, path.Join(l, legacyLayerFileName))
 		}
 
 		manifest = append(manifest, manifestItem{
@@ -313,6 +321,7 @@
 			v1Img.Parent = parent.Hex()
 		}
 
+		v1Img.OS = img.OS
 		src, err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created)
 		if err != nil {
 			return nil, err