| package compression |
| |
| import ( |
| "bytes" |
| "context" |
| "io" |
| |
| "github.com/containerd/containerd/content" |
| "github.com/containerd/containerd/images" |
| digest "github.com/opencontainers/go-digest" |
| ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| "github.com/pkg/errors" |
| "github.com/sirupsen/logrus" |
| ) |
| |
| // Type represents compression type for blob data. |
| type Type int |
| |
| const ( |
| // Uncompressed indicates no compression. |
| Uncompressed Type = iota |
| |
| // Gzip is used for blob data. |
| Gzip |
| |
| // UnknownCompression means not supported yet. |
| UnknownCompression Type = -1 |
| ) |
| |
| var Default = Gzip |
| |
| func (ct Type) String() string { |
| switch ct { |
| case Uncompressed: |
| return "uncompressed" |
| case Gzip: |
| return "gzip" |
| default: |
| return "unknown" |
| } |
| } |
| |
| // DetectLayerMediaType returns media type from existing blob data. |
| func DetectLayerMediaType(ctx context.Context, cs content.Store, id digest.Digest, oci bool) (string, error) { |
| ra, err := cs.ReaderAt(ctx, ocispec.Descriptor{Digest: id}) |
| if err != nil { |
| return "", err |
| } |
| defer ra.Close() |
| |
| ct, err := detectCompressionType(content.NewReader(ra)) |
| if err != nil { |
| return "", err |
| } |
| |
| switch ct { |
| case Uncompressed: |
| if oci { |
| return ocispec.MediaTypeImageLayer, nil |
| } |
| return images.MediaTypeDockerSchema2Layer, nil |
| case Gzip: |
| if oci { |
| return ocispec.MediaTypeImageLayerGzip, nil |
| } |
| return images.MediaTypeDockerSchema2LayerGzip, nil |
| default: |
| return "", errors.Errorf("failed to detect layer %v compression type", id) |
| } |
| } |
| |
| // detectCompressionType detects compression type from real blob data. |
| func detectCompressionType(cr io.Reader) (Type, error) { |
| var buf [10]byte |
| var n int |
| var err error |
| |
| if n, err = cr.Read(buf[:]); err != nil && err != io.EOF { |
| // Note: we'll ignore any io.EOF error because there are some |
| // odd cases where the layer.tar file will be empty (zero bytes) |
| // and we'll just treat it as a non-compressed stream and that |
| // means just create an empty layer. |
| // |
| // See issue docker/docker#18170 |
| return UnknownCompression, err |
| } |
| |
| for c, m := range map[Type][]byte{ |
| Gzip: {0x1F, 0x8B, 0x08}, |
| } { |
| if n < len(m) { |
| continue |
| } |
| if bytes.Equal(m, buf[:len(m)]) { |
| return c, nil |
| } |
| } |
| return Uncompressed, nil |
| } |
| |
| var toDockerLayerType = map[string]string{ |
| ocispec.MediaTypeImageLayer: images.MediaTypeDockerSchema2Layer, |
| images.MediaTypeDockerSchema2Layer: images.MediaTypeDockerSchema2Layer, |
| ocispec.MediaTypeImageLayerGzip: images.MediaTypeDockerSchema2LayerGzip, |
| images.MediaTypeDockerSchema2LayerGzip: images.MediaTypeDockerSchema2LayerGzip, |
| images.MediaTypeDockerSchema2LayerForeign: images.MediaTypeDockerSchema2Layer, |
| images.MediaTypeDockerSchema2LayerForeignGzip: images.MediaTypeDockerSchema2LayerGzip, |
| } |
| |
| var toOCILayerType = map[string]string{ |
| ocispec.MediaTypeImageLayer: ocispec.MediaTypeImageLayer, |
| images.MediaTypeDockerSchema2Layer: ocispec.MediaTypeImageLayer, |
| ocispec.MediaTypeImageLayerGzip: ocispec.MediaTypeImageLayerGzip, |
| images.MediaTypeDockerSchema2LayerGzip: ocispec.MediaTypeImageLayerGzip, |
| images.MediaTypeDockerSchema2LayerForeign: ocispec.MediaTypeImageLayer, |
| images.MediaTypeDockerSchema2LayerForeignGzip: ocispec.MediaTypeImageLayerGzip, |
| } |
| |
| func convertLayerMediaType(mediaType string, oci bool) string { |
| var converted string |
| if oci { |
| converted = toOCILayerType[mediaType] |
| } else { |
| converted = toDockerLayerType[mediaType] |
| } |
| if converted == "" { |
| logrus.Warnf("unhandled conversion for mediatype %q", mediaType) |
| return mediaType |
| } |
| return converted |
| } |
| |
| func ConvertAllLayerMediaTypes(oci bool, descs ...ocispec.Descriptor) []ocispec.Descriptor { |
| var converted []ocispec.Descriptor |
| for _, desc := range descs { |
| desc.MediaType = convertLayerMediaType(desc.MediaType, oci) |
| converted = append(converted, desc) |
| } |
| return converted |
| } |