@@ -23,15 +23,18 @@ import (
2323 "encoding/json"
2424 "fmt"
2525 "io"
26+ "io/ioutil"
2627 "os"
2728 "path/filepath"
2829 "strconv"
2930 "strings"
3031
32+ "github.com/Microsoft/go-winio"
3133 winfs "github.com/Microsoft/go-winio/pkg/fs"
3234 "github.com/Microsoft/go-winio/vhd"
3335 "github.com/Microsoft/hcsshim"
3436 "github.com/Microsoft/hcsshim/computestorage"
37+ "github.com/Microsoft/hcsshim/pkg/ociwclayer"
3538 "github.com/containerd/containerd/errdefs"
3639 "github.com/containerd/containerd/log"
3740 "github.com/containerd/containerd/mount"
@@ -185,14 +188,14 @@ func (s *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
185188 return s .mounts (snapshot ), nil
186189}
187190
188- func (s * snapshotter ) Commit (ctx context.Context , name , key string , opts ... snapshots.Opt ) error {
191+ func (s * snapshotter ) Commit (ctx context.Context , name , key string , opts ... snapshots.Opt ) ( retErr error ) {
189192 ctx , t , err := s .ms .TransactionContext (ctx , true )
190193 if err != nil {
191194 return err
192195 }
193196
194197 defer func () {
195- if err != nil {
198+ if retErr != nil {
196199 if rerr := t .Rollback (); rerr != nil {
197200 log .G (ctx ).WithError (rerr ).Warn ("failed to rollback transaction" )
198201 }
@@ -202,15 +205,30 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
202205 // grab the existing id
203206 id , _ , _ , err := storage .GetInfo (ctx , key )
204207 if err != nil {
205- return err
208+ return errors . Wrapf ( err , "failed to get storage info for %s" , key )
206209 }
207210
208- usage , err := fs . DiskUsage (ctx , s . getSnapshotDir ( id ) )
211+ snapshot , err := storage . GetSnapshot (ctx , key )
209212 if err != nil {
210213 return err
211214 }
212215
213- if _ , err = storage .CommitActive (ctx , key , name , snapshots .Usage (usage ), opts ... ); err != nil {
216+ path := s .getSnapshotDir (id )
217+
218+ // If (windowsDiff).Apply was used to populate this layer, then it's already in the 'committed' state.
219+ // See createSnapshot below for more details
220+ if ! strings .Contains (key , snapshots .UnpackKeyPrefix ) {
221+ if err := s .convertScratchToReadOnlyLayer (ctx , snapshot , path ); err != nil {
222+ return err
223+ }
224+ }
225+
226+ usage , err := fs .DiskUsage (ctx , path )
227+ if err != nil {
228+ return errors .Wrapf (err , "failed to collect disk usage of snapshot storage: %s" , path )
229+ }
230+
231+ if _ , err := storage .CommitActive (ctx , key , name , snapshots .Usage (usage ), opts ... ); err != nil {
214232 return errors .Wrap (err , "failed to commit snapshot" )
215233 }
216234 return t .Commit ()
@@ -443,6 +461,43 @@ func (s *snapshotter) createScratchLayer(ctx context.Context, snDir string, pare
443461 return nil
444462}
445463
464+ // convertScratchToReadOnlyLayer reimporst the layer over itself, to transfer the files from the sandbox.vhdx to the on-disk storage.
465+ func (s * snapshotter ) convertScratchToReadOnlyLayer (ctx context.Context , snapshot storage.Snapshot , path string ) (retErr error ) {
466+
467+ // TODO darrenstahlmsft: When this is done isolated, we should disable these.
468+ // it currently cannot be disabled, unless we add ref counting. Since this is
469+ // temporary, leaving it enabled is OK for now.
470+ // https://github.com/containerd/containerd/issues/1681
471+ if err := winio .EnableProcessPrivileges ([]string {winio .SeBackupPrivilege , winio .SeRestorePrivilege }); err != nil {
472+ return errors .Wrap (err , "failed to enable necessary privileges" )
473+ }
474+
475+ parentLayerPaths := s .parentIDsToParentPaths (snapshot .ParentIDs )
476+ reader , writer := io .Pipe ()
477+
478+ go func () {
479+ err := ociwclayer .ExportLayerToTar (ctx , writer , path , parentLayerPaths )
480+ writer .CloseWithError (err )
481+ }()
482+
483+ if _ , err := ociwclayer .ImportLayerFromTar (ctx , reader , path , parentLayerPaths ); err != nil {
484+ return errors .Wrap (err , "failed to reimport snapshot" )
485+ }
486+
487+ if _ , err := io .Copy (ioutil .Discard , reader ); err != nil {
488+ return errors .Wrap (err , "failed discarding extra data in import stream" )
489+ }
490+
491+ // NOTE: We do not delete the sandbox.vhdx here, as that will break later calls to
492+ // ociwclayer.ExportLayerToTar for this snapshot.
493+ // As a consequence, the data for this layer is held twice, once on-disk and once
494+ // in the sandbox.vhdx.
495+ // TODO: This is either a bug or misfeature in hcsshim, so will need to be resolved
496+ // there first.
497+
498+ return nil
499+ }
500+
446501// This handles creating the UVMs scratch layer.
447502func (s * snapshotter ) createUVMScratchLayer (ctx context.Context , snDir string , parentLayers []string ) error {
448503 parentLen := len (parentLayers )
0 commit comments