github.com/containerd/Containerd@v1.4.13/diff/windows/windows.go (about)

     1  // +build windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package windows
    20  
    21  import (
    22  	"context"
    23  	"io"
    24  	"io/ioutil"
    25  	"time"
    26  
    27  	winio "github.com/Microsoft/go-winio"
    28  	"github.com/containerd/containerd/archive"
    29  	"github.com/containerd/containerd/content"
    30  	"github.com/containerd/containerd/diff"
    31  	"github.com/containerd/containerd/errdefs"
    32  	"github.com/containerd/containerd/log"
    33  	"github.com/containerd/containerd/metadata"
    34  	"github.com/containerd/containerd/mount"
    35  	"github.com/containerd/containerd/platforms"
    36  	"github.com/containerd/containerd/plugin"
    37  	digest "github.com/opencontainers/go-digest"
    38  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    39  	"github.com/pkg/errors"
    40  	"github.com/sirupsen/logrus"
    41  )
    42  
    43  func init() {
    44  	plugin.Register(&plugin.Registration{
    45  		Type: plugin.DiffPlugin,
    46  		ID:   "windows",
    47  		Requires: []plugin.Type{
    48  			plugin.MetadataPlugin,
    49  		},
    50  		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
    51  			md, err := ic.Get(plugin.MetadataPlugin)
    52  			if err != nil {
    53  				return nil, err
    54  			}
    55  
    56  			ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
    57  			return NewWindowsDiff(md.(*metadata.DB).ContentStore())
    58  		},
    59  	})
    60  }
    61  
    62  // CompareApplier handles both comparison and
    63  // application of layer diffs.
    64  type CompareApplier interface {
    65  	diff.Applier
    66  	diff.Comparer
    67  }
    68  
    69  // windowsDiff does filesystem comparison and application
    70  // for Windows specific layer diffs.
    71  type windowsDiff struct {
    72  	store content.Store
    73  }
    74  
    75  var emptyDesc = ocispec.Descriptor{}
    76  
    77  // NewWindowsDiff is the Windows container layer implementation
    78  // for comparing and applying filesystem layers
    79  func NewWindowsDiff(store content.Store) (CompareApplier, error) {
    80  	return windowsDiff{
    81  		store: store,
    82  	}, nil
    83  }
    84  
    85  // Apply applies the content associated with the provided digests onto the
    86  // provided mounts. Archive content will be extracted and decompressed if
    87  // necessary.
    88  func (s windowsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (d ocispec.Descriptor, err error) {
    89  	t1 := time.Now()
    90  	defer func() {
    91  		if err == nil {
    92  			log.G(ctx).WithFields(logrus.Fields{
    93  				"d":     time.Since(t1),
    94  				"dgst":  desc.Digest,
    95  				"size":  desc.Size,
    96  				"media": desc.MediaType,
    97  			}).Debugf("diff applied")
    98  		}
    99  	}()
   100  
   101  	var config diff.ApplyConfig
   102  	for _, o := range opts {
   103  		if err := o(ctx, desc, &config); err != nil {
   104  			return emptyDesc, errors.Wrap(err, "failed to apply config opt")
   105  		}
   106  	}
   107  
   108  	ra, err := s.store.ReaderAt(ctx, desc)
   109  	if err != nil {
   110  		return emptyDesc, errors.Wrap(err, "failed to get reader from content store")
   111  	}
   112  	defer ra.Close()
   113  
   114  	processor := diff.NewProcessorChain(desc.MediaType, content.NewReader(ra))
   115  	for {
   116  		if processor, err = diff.GetProcessor(ctx, processor, config.ProcessorPayloads); err != nil {
   117  			return emptyDesc, errors.Wrapf(err, "failed to get stream processor for %s", desc.MediaType)
   118  		}
   119  		if processor.MediaType() == ocispec.MediaTypeImageLayer {
   120  			break
   121  		}
   122  	}
   123  	defer processor.Close()
   124  
   125  	digester := digest.Canonical.Digester()
   126  	rc := &readCounter{
   127  		r: io.TeeReader(processor, digester.Hash()),
   128  	}
   129  
   130  	layer, parentLayerPaths, err := mountsToLayerAndParents(mounts)
   131  	if err != nil {
   132  		return emptyDesc, err
   133  	}
   134  
   135  	// TODO darrenstahlmsft: When this is done isolated, we should disable these.
   136  	// it currently cannot be disabled, unless we add ref counting. Since this is
   137  	// temporary, leaving it enabled is OK for now.
   138  	if err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil {
   139  		return emptyDesc, err
   140  	}
   141  
   142  	if _, err := archive.Apply(ctx, layer, rc, archive.WithParents(parentLayerPaths), archive.AsWindowsContainerLayer()); err != nil {
   143  		return emptyDesc, err
   144  	}
   145  
   146  	// Read any trailing data
   147  	if _, err := io.Copy(ioutil.Discard, rc); err != nil {
   148  		return emptyDesc, err
   149  	}
   150  
   151  	return ocispec.Descriptor{
   152  		MediaType: ocispec.MediaTypeImageLayer,
   153  		Size:      rc.c,
   154  		Digest:    digester.Digest(),
   155  	}, nil
   156  }
   157  
   158  // Compare creates a diff between the given mounts and uploads the result
   159  // to the content store.
   160  func (s windowsDiff) Compare(ctx context.Context, lower, upper []mount.Mount, opts ...diff.Opt) (d ocispec.Descriptor, err error) {
   161  	return emptyDesc, errors.Wrap(errdefs.ErrNotImplemented, "windowsDiff does not implement Compare method")
   162  }
   163  
   164  type readCounter struct {
   165  	r io.Reader
   166  	c int64
   167  }
   168  
   169  func (rc *readCounter) Read(p []byte) (n int, err error) {
   170  	n, err = rc.r.Read(p)
   171  	rc.c += int64(n)
   172  	return
   173  }
   174  
   175  func mountsToLayerAndParents(mounts []mount.Mount) (string, []string, error) {
   176  	if len(mounts) != 1 {
   177  		return "", nil, errors.Wrap(errdefs.ErrInvalidArgument, "number of mounts should always be 1 for Windows layers")
   178  	}
   179  	mnt := mounts[0]
   180  	if mnt.Type != "windows-layer" {
   181  		// This is a special case error. When this is received the diff service
   182  		// will attempt the next differ in the chain which for Windows is the
   183  		// lcow differ that we want.
   184  		return "", nil, errors.Wrapf(errdefs.ErrNotImplemented, "windowsDiff does not support layer type %s", mnt.Type)
   185  	}
   186  
   187  	parentLayerPaths, err := mnt.GetParentPaths()
   188  	if err != nil {
   189  		return "", nil, err
   190  	}
   191  
   192  	return mnt.Source, parentLayerPaths, nil
   193  }