@@ -18,13 +18,16 @@ package apply
1818
1919import (
2020 "context"
21+ "crypto/rand"
22+ "encoding/base64"
2123 "fmt"
2224 "io"
2325 "time"
2426
2527 "github.com/containerd/containerd/v2/core/content"
2628 "github.com/containerd/containerd/v2/core/diff"
2729 "github.com/containerd/containerd/v2/core/mount"
30+ "github.com/containerd/errdefs"
2831 "github.com/containerd/log"
2932 digest "github.com/opencontainers/go-digest"
3033 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -33,13 +36,22 @@ import (
3336// NewFileSystemApplier returns an applier which simply mounts
3437// and applies diff onto the mounted filesystem.
3538func NewFileSystemApplier (cs content.Provider ) diff.Applier {
39+ return NewFileSystemApplierWithMountManager (cs , nil )
40+ }
41+
42+ // NewFileSystemApplierWithMountManager returns an applier which simply mounts and
43+ // applies diff onto the mounted filesystem.
44+ // An optional mount manager can be specified and it will take effect when applying.
45+ func NewFileSystemApplierWithMountManager (cs content.Provider , mm mount.Manager ) diff.Applier {
3646 return & fsApplier {
3747 store : cs ,
48+ mount : mm ,
3849 }
3950}
4051
4152type fsApplier struct {
4253 store content.Provider
54+ mount mount.Manager
4355}
4456
4557var emptyDesc = ocispec.Descriptor {}
@@ -98,6 +110,23 @@ func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts [
98110 r : io .TeeReader (processor , digester .Hash ()),
99111 }
100112
113+ // The number of `mounts` that need to be parsed by the mount manager
114+ // will be more than 1 in reality; this is needed to work around some
115+ // overlayfs/bind shortcuts in core/diff/apply/apply_linux.go
116+ if s .mount != nil && len (mounts ) > 1 {
117+ var b [3 ]byte
118+ // Ignore read failures, just decreases uniqueness
119+ rand .Read (b [:])
120+ id := fmt .Sprintf ("fs-diffapply-%d-%s" , t1 .Nanosecond (), base64 .URLEncoding .EncodeToString (b [:]))
121+ info , err := s .mount .Activate (ctx , id , mounts )
122+ if err == nil {
123+ defer s .mount .Deactivate (ctx , id )
124+ mounts = info .System
125+ } else if ! errdefs .IsNotImplemented (err ) {
126+ return emptyDesc , fmt .Errorf ("failed to activate mounts: %w" , err )
127+ }
128+ }
129+
101130 if err := apply (ctx , mounts , rc , config .SyncFs ); err != nil {
102131 return emptyDesc , err
103132 }
0 commit comments