mirror of
https://github.com/containerd/containerd.git
synced 2026-08-09 09:33:06 +00:00
Merge pull request #13185 from erofs/erofs-layers
erofs-differ: support zstd-wrapped EROFS layers
This commit is contained in:
@@ -98,20 +98,8 @@ func NewErofsDiffer(store content.Store, opts ...DifferOpt) differ {
|
||||
return d
|
||||
}
|
||||
|
||||
// Please avoid using any +suffix to list the algorithms used inside EROFS
|
||||
// blobs, since:
|
||||
// - Each EROFS layer can use multiple compression algorithms;
|
||||
// - The suffixes should only indicate the corresponding preprocessor for
|
||||
// `images.DiffCompression`.
|
||||
//
|
||||
// Since `images.DiffCompression` doesn't support arbitrary media types,
|
||||
// disallow non-empty suffixes for now.
|
||||
func isErofsMediaType(mt string) bool {
|
||||
if !strings.HasSuffix(mt, ".erofs") && !strings.HasPrefix(mt, "application/vnd.erofs.layer") {
|
||||
return false
|
||||
}
|
||||
_, _, hasExt := strings.Cut(mt, "+")
|
||||
return !hasExt
|
||||
return strings.HasSuffix(mt, ".erofs") || strings.HasPrefix(mt, "application/vnd.erofs.layer")
|
||||
}
|
||||
|
||||
func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (d ocispec.Descriptor, err error) {
|
||||
@@ -127,11 +115,32 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
||||
}
|
||||
}()
|
||||
|
||||
native := false
|
||||
if isErofsMediaType(desc.MediaType) {
|
||||
native = true
|
||||
} else if _, err := images.DiffCompression(ctx, desc.MediaType); err != nil {
|
||||
return emptyDesc, fmt.Errorf("currently unsupported media type: %s", desc.MediaType)
|
||||
var (
|
||||
erofsLayerType string
|
||||
fastcopy bool
|
||||
)
|
||||
diffLayerType := desc.MediaType
|
||||
native := isErofsMediaType(diffLayerType)
|
||||
if native {
|
||||
base, ext, hasExt := strings.Cut(diffLayerType, "+")
|
||||
// Mimic the OCI layer for EROFS blobs for diff.NewProcessorChain(), so
|
||||
// there is no need to bother with too much unrelated logic for now.
|
||||
diffLayerType = ocispec.MediaTypeImageLayer
|
||||
if hasExt {
|
||||
// `+zstd` indicates that the original EROFS blob is additionally
|
||||
// compressed with standard zstd streams.
|
||||
// Only `+zstd` is considered since it is more performant than gzip
|
||||
// and has useful features like skippable frames.
|
||||
if ext != "zstd" {
|
||||
return emptyDesc, fmt.Errorf("unsupported erofs layer suffix: %s", ext)
|
||||
}
|
||||
diffLayerType = diffLayerType + "+zstd"
|
||||
} else {
|
||||
fastcopy = true
|
||||
}
|
||||
erofsLayerType = base
|
||||
} else if _, err := images.DiffCompression(ctx, diffLayerType); err != nil {
|
||||
return emptyDesc, fmt.Errorf("unsupported media type: %s", desc.MediaType)
|
||||
}
|
||||
|
||||
var config diff.ApplyConfig
|
||||
@@ -153,7 +162,8 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
||||
defer ra.Close()
|
||||
|
||||
layerBlobPath := path.Join(layer, "layer.erofs")
|
||||
if native {
|
||||
// Allow copy file range when there is an uncompressed native EROFS layer
|
||||
if fastcopy {
|
||||
f, err := os.Create(layerBlobPath)
|
||||
if err != nil {
|
||||
return emptyDesc, err
|
||||
@@ -163,10 +173,11 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
||||
if err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
log.G(ctx).WithField("path", layerBlobPath).Debug("Applied layer with uncompressed EROFS blob")
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
processor := diff.NewProcessorChain(desc.MediaType, content.NewReader(ra))
|
||||
processor := diff.NewProcessorChain(diffLayerType, content.NewReader(ra))
|
||||
for {
|
||||
if processor, err = diff.GetProcessor(ctx, processor, config.ProcessorPayloads); err != nil {
|
||||
return emptyDesc, fmt.Errorf("failed to get stream processor for %s: %w", desc.MediaType, err)
|
||||
@@ -185,7 +196,18 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
||||
// Choose between tar index or tar conversion mode
|
||||
// Generate deterministic UUID from layer digest
|
||||
u := uuid.NewSHA1(uuid.NameSpaceURL, []byte("erofs:blobs/"+desc.Digest))
|
||||
if s.enableTarIndex {
|
||||
if native {
|
||||
f, err := os.Create(layerBlobPath)
|
||||
if err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
_, err = io.Copy(f, rc)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
log.G(ctx).WithField("path", layerBlobPath).Debug("Applied layer with compressed EROFS blob")
|
||||
} else if s.enableTarIndex {
|
||||
// Use the tar index method: generate tar index and append tar
|
||||
err = erofsutils.GenerateTarIndexAndAppendTar(ctx, rc, layerBlobPath, u.String(), s.mkfsExtraOpts)
|
||||
if err != nil {
|
||||
@@ -213,6 +235,13 @@ func (s erofsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
||||
}
|
||||
}
|
||||
|
||||
if native {
|
||||
return ocispec.Descriptor{
|
||||
MediaType: erofsLayerType,
|
||||
Size: rc.c,
|
||||
Digest: digester.Digest(),
|
||||
}, nil
|
||||
}
|
||||
return ocispec.Descriptor{
|
||||
MediaType: ocispec.MediaTypeImageLayer,
|
||||
Size: rc.c,
|
||||
|
||||
Reference in New Issue
Block a user