github.com/demonoid81/containerd@v1.3.4/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, errdefs.ErrNotImplemented 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, errdefs.ErrNotImplemented 185 } 186 187 parentLayerPaths, err := mnt.GetParentPaths() 188 if err != nil { 189 return "", nil, err 190 } 191 192 return mnt.Source, parentLayerPaths, nil 193 }