Skip to content

Commit f5c7292

Browse files
giuseppeclaude
andcommitted
copy: detect zstd:chunked sentinel layer and skip full-digest mitigation during pull
When pulling an image, the storage destination's FilterLayers method detects the zstd:chunked sentinel layer, marks it as empty, and internally records that SkipMitigation() should be called to bypass unnecessary full-digest verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
1 parent cb9a242 commit f5c7292

3 files changed

Lines changed: 47 additions & 3 deletions

File tree

image/copy/single.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,18 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
458458
}
459459
manifestLayerInfos := man.LayerInfos()
460460

461+
// Let the destination filter manifest layer infos before copying.
462+
// Destinations that support it (e.g., c/storage) may detect storage-specific
463+
// markers and return indices of layers that should be treated as empty.
464+
type layerFilter interface {
465+
FilterLayers([]manifest.LayerInfo) []int
466+
}
467+
if f, ok := ic.c.dest.(layerFilter); ok {
468+
for _, idx := range f.FilterLayers(manifestLayerInfos) {
469+
manifestLayerInfos[idx].EmptyLayer = true
470+
}
471+
}
472+
461473
// copyGroup is used to determine if all layers are copied
462474
copyGroup := sync.WaitGroup{}
463475

@@ -466,7 +478,11 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor
466478
defer ic.c.concurrentBlobCopiesSemaphore.Release(1)
467479
defer copyGroup.Done()
468480
cld := copyLayerData{}
469-
if !ic.c.options.DownloadForeignLayers && ic.c.dest.AcceptsForeignLayerURLs() && len(srcLayer.URLs) != 0 {
481+
if manifestLayerInfos[index].EmptyLayer {
482+
// Empty layers (e.g., zstd:chunked sentinel) need no copying.
483+
cld.destInfo = srcLayer
484+
logrus.Debugf("Skipping empty layer %q", srcLayer.Digest)
485+
} else if !ic.c.options.DownloadForeignLayers && ic.c.dest.AcceptsForeignLayerURLs() && len(srcLayer.URLs) != 0 {
470486
// DiffIDs are, currently, needed only when converting from schema1.
471487
// In which case src.LayerInfos will not have URLs because schema1
472488
// does not support them.

image/storage/storage_dest.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ type storageImageDestination struct {
6262
manifest []byte // (Per-instance) manifest contents, or nil if not yet known.
6363
manifestMIMEType string // Valid if manifest != nil
6464
manifestDigest digest.Digest // Valid if manifest != nil
65-
untrustedDiffIDValues []digest.Digest // From config’s RootFS.DiffIDs (not even validated to be valid digest.Digest!); or nil if not read yet
65+
untrustedDiffIDValues []digest.Digest // From config’s RootFS.DiffIDs (not even validated to be valid digest.Digest!); or nil if not read yet
66+
zstdChunkedSentinelDetected bool // Set by FilterLayers if a zstd:chunked sentinel layer is present
6667
signatures []byte // Signature contents, temporary
6768
signatureses map[digest.Digest][]byte // Instance signature contents, temporary
6869
metadata storageImageMetadata // Metadata contents being built
@@ -221,6 +222,17 @@ func (s *storageImageDestination) NoteOriginalOCIConfig(ociConfig *imgspecv1.Ima
221222
return nil
222223
}
223224

225+
// FilterLayers inspects the manifest layer infos and returns indices of layers
226+
// that should be treated as empty. For c/storage, this detects the zstd:chunked
227+
// sentinel layer and records that the full-digest mitigation can be skipped.
228+
func (s *storageImageDestination) FilterLayers(layerInfos []manifest.LayerInfo) []int {
229+
if len(layerInfos) > 0 && layerInfos[0].Digest == toc.ZstdChunkedSentinelDigest {
230+
s.zstdChunkedSentinelDetected = true
231+
return []int{0}
232+
}
233+
return nil
234+
}
235+
224236
// PutBlobWithOptions writes contents of stream and returns data representing the result.
225237
// inputInfo.Digest can be optionally provided if known; if provided, and stream is read to the end without error, the digest MUST match the stream contents.
226238
// inputInfo.Size is the expected length of stream, if known.
@@ -424,6 +436,10 @@ func (s *storageImageDestination) PutBlobPartial(ctx context.Context, chunkAcces
424436
return private.UploadedBlob{}, err
425437
}
426438
defer differ.Close()
439+
if s.zstdChunkedSentinelDetected {
440+
logrus.Debugf("Sentinel detected for layer %s: skipping full-digest mitigation", srcInfo.Digest)
441+
chunked.SkipMitigation(differ)
442+
}
427443

428444
out, err := s.imageRef.transport.store.PrepareStagedLayer(nil, differ)
429445
if err != nil {
@@ -1451,6 +1467,11 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options
14511467
}
14521468
layerBlobs := man.LayerInfos()
14531469

1470+
// Mark the zstd:chunked sentinel layer as empty.
1471+
if len(layerBlobs) > 0 && layerBlobs[0].Digest == toc.ZstdChunkedSentinelDigest {
1472+
layerBlobs[0].EmptyLayer = true
1473+
}
1474+
14541475
// Extract, commit, or find the layers.
14551476
for i, blob := range layerBlobs {
14561477
if stopQueue, err := s.commitLayer(i, addedLayerInfo{

image/storage/storage_src.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,14 @@ func (s *storageImageSource) LayerInfosForCopy(ctx context.Context, instanceDige
352352
}
353353
slices.Reverse(physicalBlobInfos)
354354

355-
res, err := buildLayerInfosForCopy(man.LayerInfos(), physicalBlobInfos, gzipCompressedLayerType)
355+
manifestLayerInfos := man.LayerInfos()
356+
// Mark the zstd:chunked sentinel layer as empty — it was never stored
357+
// physically, so buildLayerInfosForCopy must skip it.
358+
if len(manifestLayerInfos) > 0 && manifestLayerInfos[0].Digest == toc.ZstdChunkedSentinelDigest {
359+
manifestLayerInfos[0].EmptyLayer = true
360+
}
361+
362+
res, err := buildLayerInfosForCopy(manifestLayerInfos, physicalBlobInfos, gzipCompressedLayerType)
356363
if err != nil {
357364
return nil, fmt.Errorf("creating LayerInfosForCopy of image %q: %w", s.image.ID, err)
358365
}

0 commit comments

Comments
 (0)