@@ -71,20 +71,33 @@ import (
7171 "time"
7272
7373 "github.com/Microsoft/hcsshim"
74+ "github.com/Microsoft/hcsshim/ext4/tar2ext4"
7475 "github.com/Microsoft/opengcs/client"
7576 "github.com/docker/docker/daemon/graphdriver"
7677 "github.com/docker/docker/pkg/archive"
7778 "github.com/docker/docker/pkg/containerfs"
7879 "github.com/docker/docker/pkg/idtools"
7980 "github.com/docker/docker/pkg/ioutils"
81+ "github.com/docker/docker/pkg/reexec"
8082 "github.com/docker/docker/pkg/system"
8183 "github.com/sirupsen/logrus"
8284)
8385
86+ // noreexec controls reexec functionality. Off by default, on for debugging purposes.
87+ var noreexec = false
88+
8489// init registers this driver to the register. It gets initialised by the
8590// function passed in the second parameter, implemented in this file.
8691func init () {
8792 graphdriver .Register ("lcow" , InitDriver )
93+ // DOCKER_LCOW_NOREEXEC allows for inline processing which makes
94+ // debugging issues in the re-exec codepath significantly easier.
95+ if os .Getenv ("DOCKER_LCOW_NOREEXEC" ) != "" {
96+ logrus .Warnf ("LCOW Graphdriver is set to not re-exec. This is intended for debugging purposes only." )
97+ noreexec = true
98+ } else {
99+ reexec .Register ("docker-lcow-tar2ext4" , tar2ext4Reexec )
100+ }
88101}
89102
90103const (
@@ -846,32 +859,72 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
846859func (d * Driver ) ApplyDiff (id , parent string , diff io.Reader ) (int64 , error ) {
847860 logrus .Debugf ("lcowdriver: applydiff: id %s" , id )
848861
849- svm , err := d .startServiceVMIfNotRunning (id , nil , fmt .Sprintf ("applydiff %s" , id ))
862+ // Log failures here as it's undiagnosable sometimes, due to a possible panic.
863+ // See https://github.com/moby/moby/issues/37955 for more information.
864+
865+ dest := filepath .Join (d .dataRoot , id , layerFilename )
866+ if ! noreexec {
867+ cmd := reexec .Command ([]string {"docker-lcow-tar2ext4" , dest }... )
868+ stdout := bytes .NewBuffer (nil )
869+ stderr := bytes .NewBuffer (nil )
870+ cmd .Stdin = diff
871+ cmd .Stdout = stdout
872+ cmd .Stderr = stderr
873+
874+ if err := cmd .Start (); err != nil {
875+ logrus .Warnf ("lcowdriver: applydiff: id %s failed to start re-exec: %s" , id , err )
876+ return 0 , err
877+ }
878+
879+ if err := cmd .Wait (); err != nil {
880+ logrus .Warnf ("lcowdriver: applydiff: id %s failed %s" , id , err )
881+ return 0 , fmt .Errorf ("re-exec error: %v: stderr: %s" , err , stderr )
882+ }
883+ return strconv .ParseInt (stdout .String (), 10 , 64 )
884+ }
885+ // The inline case
886+ size , err := tar2ext4Actual (dest , diff )
850887 if err != nil {
851- return 0 , err
888+ logrus . Warnf ( "lcowdriver: applydiff: id %s failed %s" , id , err )
852889 }
853- defer d .terminateServiceVM (id , fmt .Sprintf ("applydiff %s" , id ), false )
890+ return size , err
891+ }
854892
855- logrus .Debugf ("lcowdriver: applydiff: waiting for svm to finish booting" )
856- err = svm .getStartError ()
893+ // tar2ext4Reexec is the re-exec entry point for writing a layer from a tar file
894+ func tar2ext4Reexec () {
895+ size , err := tar2ext4Actual (os .Args [1 ], os .Stdin )
857896 if err != nil {
858- return 0 , fmt .Errorf ("lcowdriver: applydiff: svm failed to boot: %s" , err )
897+ fmt .Fprint (os .Stderr , err )
898+ os .Exit (1 )
859899 }
900+ fmt .Fprint (os .Stdout , size )
901+ }
860902
861- // TODO @jhowardmsft - the retries are temporary to overcome platform reliability issues.
862- // Obviously this will be removed as platform bugs are fixed.
863- retries := 0
864- for {
865- retries ++
866- size , err := svm .config .TarToVhd (filepath .Join (d .dataRoot , id , layerFilename ), diff )
867- if err != nil {
868- if retries <= 10 {
869- continue
870- }
871- return 0 , err
872- }
873- return size , err
903+ // tar2ext4Actual is the implementation of tar2ext to write a layer from a tar file.
904+ // It can be called through re-exec (default), or inline for debugging.
905+ func tar2ext4Actual (dest string , diff io.Reader ) (int64 , error ) {
906+ // maxDiskSize is not relating to the sandbox size - this is the
907+ // maximum possible size a layer VHD generated can be from an EXT4
908+ // layout perspective.
909+ const maxDiskSize = 128 * 1024 * 1024 * 1024 // 128GB
910+ out , err := os .Create (dest )
911+ if err != nil {
912+ return 0 , err
913+ }
914+ defer out .Close ()
915+ if err := tar2ext4 .Convert (
916+ diff ,
917+ out ,
918+ tar2ext4 .AppendVhdFooter ,
919+ tar2ext4 .ConvertWhiteout ,
920+ tar2ext4 .MaximumDiskSize (maxDiskSize )); err != nil {
921+ return 0 , err
922+ }
923+ fi , err := os .Stat (dest )
924+ if err != nil {
925+ return 0 , err
874926 }
927+ return fi .Size (), nil
875928}
876929
877930// Changes produces a list of changes between the specified layer
0 commit comments