Skip to content

Commit 8a4cbab

Browse files
committed
Reimport windows layers when comitting snapshots
A Scratch layer only contains a sandbox.vhdx, but to be used as a parent layer, it must also contain the files on-disk. Hence, we Export the layer from the sandbox.vhdx and Import it back into itself, so that both data formats are present. Signed-off-by: Paul "TBBle" Hampson <[email protected]>
1 parent 10ecf83 commit 8a4cbab

1 file changed

Lines changed: 60 additions & 5 deletions

File tree

snapshots/windows/windows.go

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
447502
func (s *snapshotter) createUVMScratchLayer(ctx context.Context, snDir string, parentLayers []string) error {
448503
parentLen := len(parentLayers)

0 commit comments

Comments
 (0)