@@ -22,8 +22,10 @@ import (
2222 "encoding/base64"
2323 "fmt"
2424 "io"
25+ "os"
2526 "path"
2627 "path/filepath"
28+ "strings"
2729 "time"
2830
2931 "github.com/containerd/containerd/v2/core/content"
@@ -62,6 +64,27 @@ func NewErofsDiffer(store content.Store, mkfsExtraOpts []string) differ {
6264 }
6365}
6466
67+ // A valid EROFS native layer media type should be either:
68+ // - `application/vnd.erofs.image.layer`, or
69+ // - have the suffix `.image.layer.erofs`
70+ //
71+ // Please avoid using any +suffix to list the algorithms used inside EROFS
72+ // blobs, since:
73+ // - Each EROFS layer can use multiple compression algorithms;
74+ // - The suffixes should only indicate the corresponding preprocessor for
75+ // `images.DiffCompression`.
76+ //
77+ // Since `images.DiffCompression` doesn't support arbitrary media types,
78+ // disallow non-empty suffixes for now.
79+ func isErofsMediaType (mt string ) bool {
80+ mediaType , ext , ok := strings .Cut (mt , "+" )
81+ if ! ok || ext != "" {
82+ return false
83+ }
84+ return mediaType == "application/vnd.erofs.image.layer" ||
85+ strings .HasSuffix (mediaType , ".image.layer.erofs" )
86+ }
87+
6588func writeDiff (ctx context.Context , w io.Writer , lower []mount.Mount , upperRoot string ) error {
6689 var opts []archive.ChangeWriterOpt
6790
@@ -223,7 +246,10 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
223246 }
224247 }()
225248
226- if _ , err := images .DiffCompression (ctx , desc .MediaType ); err != nil {
249+ native := false
250+ if isErofsMediaType (desc .MediaType ) {
251+ native = true
252+ } else if _ , err := images .DiffCompression (ctx , desc .MediaType ); err != nil {
227253 return emptyDesc , fmt .Errorf ("currently unsupported media type: %s" , desc .MediaType )
228254 }
229255
@@ -245,6 +271,20 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
245271 }
246272 defer ra .Close ()
247273
274+ layerBlobPath := path .Join (layer , "layer.erofs" )
275+ if native {
276+ // TODO: prefer to use hardlink as an optimization here
277+ f , err := os .Create (layerBlobPath )
278+ if err != nil {
279+ return emptyDesc , err
280+ }
281+ _ , err = io .Copy (f , content .NewReader (ra ))
282+ if err != nil {
283+ return emptyDesc , err
284+ }
285+ return desc , nil
286+ }
287+
248288 processor := diff .NewProcessorChain (desc .MediaType , content .NewReader (ra ))
249289 for {
250290 if processor , err = diff .GetProcessor (ctx , processor , config .ProcessorPayloads ); err != nil {
@@ -261,7 +301,6 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
261301 r : io .TeeReader (processor , digester .Hash ()),
262302 }
263303
264- layerBlobPath := path .Join (layer , "layer.erofs" )
265304 err = erofsutils .ConvertTarErofs (ctx , rc , layerBlobPath , s .mkfsExtraOpts )
266305 if err != nil {
267306 return emptyDesc , fmt .Errorf ("failed to convert erofs: %w" , err )
0 commit comments