diff --git a/core/diff/apply/apply.go b/core/diff/apply/apply.go index 9acecea35cc40..67dccf922225d 100644 --- a/core/diff/apply/apply.go +++ b/core/diff/apply/apply.go @@ -18,6 +18,8 @@ package apply import ( "context" + "crypto/rand" + "encoding/base64" "fmt" "io" "time" @@ -25,6 +27,7 @@ import ( "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/diff" "github.com/containerd/containerd/v2/core/mount" + "github.com/containerd/errdefs" "github.com/containerd/log" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -33,13 +36,22 @@ import ( // NewFileSystemApplier returns an applier which simply mounts // and applies diff onto the mounted filesystem. func NewFileSystemApplier(cs content.Provider) diff.Applier { + return NewFileSystemApplierWithMountManager(cs, nil) +} + +// NewFileSystemApplierWithMountManager returns an applier which simply mounts and +// applies diff onto the mounted filesystem. +// An optional mount manager can be specified and it will take effect when applying. +func NewFileSystemApplierWithMountManager(cs content.Provider, mm mount.Manager) diff.Applier { return &fsApplier{ store: cs, + mount: mm, } } type fsApplier struct { store content.Provider + mount mount.Manager } var emptyDesc = ocispec.Descriptor{} @@ -98,6 +110,23 @@ func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts [ r: io.TeeReader(processor, digester.Hash()), } + // The number of `mounts` that need to be parsed by the mount manager + // will be more than 1 in reality; this is needed to work around some + // overlayfs/bind shortcuts in core/diff/apply/apply_linux.go + if s.mount != nil && len(mounts) > 1 { + var b [3]byte + // Ignore read failures, just decreases uniqueness + rand.Read(b[:]) + id := fmt.Sprintf("fs-diffapply-%d-%s", t1.Nanosecond(), base64.URLEncoding.EncodeToString(b[:])) + info, err := s.mount.Activate(ctx, id, mounts) + if err == nil { + defer s.mount.Deactivate(ctx, id) + mounts = info.System + } else if !errdefs.IsNotImplemented(err) { + return emptyDesc, fmt.Errorf("failed to activate mounts: %w", err) + } + } + if err := apply(ctx, mounts, rc, config.SyncFs); err != nil { return emptyDesc, err } diff --git a/plugins/diff/walking/plugin/plugin.go b/plugins/diff/walking/plugin/plugin.go index df949422c6ddb..8cf666b891fed 100644 --- a/plugins/diff/walking/plugin/plugin.go +++ b/plugins/diff/walking/plugin/plugin.go @@ -17,9 +17,12 @@ package plugin import ( + "errors" + "github.com/containerd/containerd/v2/core/diff" "github.com/containerd/containerd/v2/core/diff/apply" "github.com/containerd/containerd/v2/core/metadata" + "github.com/containerd/containerd/v2/core/mount" "github.com/containerd/containerd/v2/plugins" "github.com/containerd/containerd/v2/plugins/diff/walking" "github.com/containerd/platforms" @@ -33,6 +36,7 @@ func init() { ID: "walking", Requires: []plugin.Type{ plugins.MetadataPlugin, + plugins.MountManagerPlugin, }, InitFn: func(ic *plugin.InitContext) (any, error) { md, err := ic.GetSingle(plugins.MetadataPlugin) @@ -40,12 +44,19 @@ func init() { return nil, err } + var mm mount.Manager + if mountsI, err := ic.GetSingle(plugins.MountManagerPlugin); err == nil { + mm = mountsI.(mount.Manager) + } else if !errors.Is(err, plugin.ErrPluginNotFound) { + return nil, err + } + ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec()) cs := md.(*metadata.DB).ContentStore() return diffPlugin{ Comparer: walking.NewWalkingDiff(cs), - Applier: apply.NewFileSystemApplier(cs), + Applier: apply.NewFileSystemApplierWithMountManager(cs, mm), }, nil }, })