diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index fc0fe5c500..31f58ab640 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -4,6 +4,7 @@ package windows import ( "bufio" + "bytes" "crypto/sha512" "encoding/json" "fmt" @@ -12,6 +13,7 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" "syscall" "time" @@ -28,6 +30,7 @@ import ( "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/longpath" + "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" "github.com/vbatts/tar-split/tar/storage" ) @@ -36,6 +39,7 @@ import ( func init() { graphdriver.Register("windowsfilter", InitFilter) graphdriver.Register("windowsdiff", InitDiff) + reexec.Register("docker-windows-write-layer", writeLayer) } const ( @@ -308,18 +312,21 @@ func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) { if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { return nil, err } - defer func() { + prepare := func() { if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { logrus.Warnf("Failed to Deactivate %s: %s", rID, err) } - }() + } arch, err := d.exportLayer(rID, layerChain) if err != nil { + prepare() return } return ioutils.NewReadCloserWrapper(arch, func() error { - return arch.Close() + err := arch.Close() + prepare() + return err }), nil } @@ -346,29 +353,35 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { } }() - r, err := hcsshim.NewLayerReader(d.info, id, parentChain) + var changes []archive.Change + err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { + r, err := hcsshim.NewLayerReader(d.info, id, parentChain) + if err != nil { + return err + } + 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}) + } + } + }) if err != nil { return nil, err } - defer r.Close() - var changes []archive.Change - for { - name, _, fileInfo, err := r.Next() - if err == io.EOF { - break - } - if err != nil { - return nil, 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}) - } - } return changes, nil } @@ -554,19 +567,21 @@ func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error { // exportLayer generates an archive from a layer based on the given ID. func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) { - var r hcsshim.LayerReader - r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths) - if err != nil { - return nil, err - } - archive, w := io.Pipe() go func() { - err := writeTarFromLayer(r, w) - cerr := r.Close() - if err == nil { - err = cerr - } + 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 + }) w.CloseWithError(err) }() @@ -682,21 +697,63 @@ func addAceToSddlDacl(sddl, ace string) (string, bool) { // importLayer adds a new layer to the tag and graph store based on the given data. func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { - var w hcsshim.LayerWriter - w, err = hcsshim.NewLayerWriter(d.info, id, parentLayerPaths) - if err != nil { + 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 } - size, err = writeLayerFromTar(layerData, w) - if err != nil { - w.Close() - return + + if err = cmd.Wait(); err != nil { + return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output) } - err = w.Close() + + return strconv.ParseInt(output.String(), 10, 64) +} + +// 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) + if err != nil { + return err + } + + err = w.Close() + if err != nil { + return err + } + + fmt.Fprint(os.Stdout, size) + return nil + }() + if err != nil { - return + fmt.Fprint(os.Stderr, err) + os.Exit(1) } - return } // resolveID computes the layerID information based on the given id.