diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index c0bd2e5cf1..6d08a36689 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -45,12 +45,20 @@ var ( "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", } + noreexec = false ) // init registers the windows graph drivers to the register. func init() { graphdriver.Register("windowsfilter", InitFilter) - reexec.Register("docker-windows-write-layer", writeLayer) + // 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) + } } type checker struct { @@ -658,63 +666,73 @@ func writeLayerFromTar(r io.Reader, w hcsshim.LayerWriter, root string) (int64, // importLayer adds a new layer to the tag and graph store based on the given data. func (d *Driver) importLayer(id string, layerData io.Reader, parentLayerPaths []string) (size int64, err error) { - 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 !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 + if err = cmd.Start(); err != nil { + return + } + + if err = cmd.Wait(); err != nil { + return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output) + } + + return strconv.ParseInt(output.String(), 10, 64) } - - if err = cmd.Wait(); err != nil { - return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output) - } - - return strconv.ParseInt(output.String(), 10, 64) + return writeLayer(layerData, d.info.HomeDir, id, parentLayerPaths...) } -// writeLayer is the re-exec entry point for writing a layer from a tar file -func writeLayer() { - home := os.Args[1] - id := os.Args[2] - parentLayerPaths := os.Args[3:] - - err := func() error { - err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}) - if err != nil { - return err - } - - info := hcsshim.DriverInfo{ - Flavour: filterDriver, - HomeDir: home, - } - - w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths) - if err != nil { - return err - } - - size, err := writeLayerFromTar(os.Stdin, w, filepath.Join(home, id)) - if err != nil { - return err - } - - err = w.Close() - if err != nil { - return err - } - - fmt.Fprint(os.Stdout, size) - return nil - }() - +// 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) +} + +// 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") + } + }() + } + + info := hcsshim.DriverInfo{ + Flavour: filterDriver, + HomeDir: home, + } + + w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths) + if err != nil { + return 0, err + } + + size, err := writeLayerFromTar(layerData, w, filepath.Join(home, id)) + if err != nil { + return 0, err + } + + err = w.Close() + if err != nil { + return 0, err + } + + return size, nil } // resolveID computes the layerID information based on the given id. diff --git a/vendor.conf b/vendor.conf index b45f95b330..49aa7c8f57 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,7 +1,7 @@ # the following lines are in sorted order, FYI github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 github.com/Microsoft/hcsshim v0.5.7 -github.com/Microsoft/go-winio v0.3.5 +github.com/Microsoft/go-winio v0.3.6 github.com/Sirupsen/logrus f76d643702a30fbffecdfe50831e11881c96ceb3 https://github.com/aaronlehmann/logrus github.com/davecgh/go-spew 6d212800a42e8ab5c146b8ace3490ee17e5225f9 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go index 3d59412c76..e363eeb275 100644 --- a/vendor/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -83,7 +83,7 @@ func RunWithPrivileges(names []string, fn func() error) error { return err } defer releaseThreadToken(token) - err = adjustPrivileges(token, privileges) + err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED) if err != nil { return err } @@ -110,6 +110,15 @@ func mapPrivileges(names []string) ([]uint64, error) { // EnableProcessPrivileges enables privileges globally for the process. func EnableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED) +} + +// DisableProcessPrivileges disables privileges globally for the process. +func DisableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, 0) +} + +func enableDisableProcessPrivilege(names []string, action uint32) error { privileges, err := mapPrivileges(names) if err != nil { return err @@ -123,15 +132,15 @@ func EnableProcessPrivileges(names []string) error { } defer token.Close() - return adjustPrivileges(token, privileges) + return adjustPrivileges(token, privileges, action) } -func adjustPrivileges(token windows.Token, privileges []uint64) error { +func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { var b bytes.Buffer binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) for _, p := range privileges { binary.Write(&b, binary.LittleEndian, p) - binary.Write(&b, binary.LittleEndian, uint32(SE_PRIVILEGE_ENABLED)) + binary.Write(&b, binary.LittleEndian, action) } prevState := make([]byte, b.Len()) reqSize := uint32(0)